Sorry

This feed does not validate.

In addition, interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://techblog.thescore.com/feed.xml

  1. <?xml version="1.0"?>
  2. <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  3.  <channel>
  4.    <title>theScore Tech Blog</title>
  5.    <link>https://techblog.thescore.com</link>
  6.    <atom:link href="https://techblog.thescore.com/feed.xml" rel="self" type="application/rss+xml" />
  7.    <description>Things We Learn While Building the Ultimate, Personalized Mobile Sports Experience</description>
  8.    <language>en-us</language>
  9.    <pubDate>Tue, 09 May 2023 17:31:30 +0000</pubDate>
  10.    <lastBuildDate>Tue, 09 May 2023 17:31:30 +0000</lastBuildDate>
  11.  
  12.    
  13.    
  14.    <item>
  15.      <title>Repo.transact/2 (The Case Against Ecto.Multi)</title>
  16.      <link>https://techblog.thescore.com/2023/05/01/repo-transact/</link>
  17.      <pubDate>Mon, 01 May 2023 00:00:00 +0000</pubDate>
  18.      <author></author>
  19.      <guid>https://techblog.thescore.com/2023/05/01/repo-transact</guid>
  20.      <description>&lt;p&gt;After reading &lt;a href=&quot;https://medium.com/very-big-things/towards-maintainable-elixir-the-core-and-the-interface-c267f0da43&quot;&gt;Towards Maintainable
  21. Elixir&lt;/a&gt;
  22. by Saša Jurić and hearing about his famous &lt;code&gt;Repo.transact&lt;/code&gt; in some of his
  23. talks, I decided it was time to explore this for myself.&lt;/p&gt;
  24.  
  25. &lt;p&gt;This post takes into account that you (the reader) are aware and know why and
  26. when to use &lt;a href=&quot;https://hexdocs.pm/ecto/Ecto.Multi.html&quot;&gt;Ecto.Multi&lt;/a&gt;. But for
  27. those unfamiliar, the TL;DR is you would use an &lt;code&gt;Ecto.Multi&lt;/code&gt; when you want to
  28. perform multiple transaction that you want to be committed to the database in
  29. one shot. Meaning, if one of the transactions fails, you would want to revert
  30. all other transactions in the run.&lt;/p&gt;
  31.  
  32. &lt;h2&gt;The Problem with Ecto.Multi&lt;/h2&gt;
  33.  
  34. &lt;p&gt;Lets get something straight, &lt;strong&gt;there is nothing wrong with using Ecto.Multi&lt;/strong&gt;
  35. in your codebase. If it works for you, then it works. However after working
  36. with it in multiple codebases I have started to see a common theme: it is very
  37. noisy and can be sometimes hard to follow and DRY up. You can get around it by
  38. using a lot of private functions to support the Ecto.Multi, but then your
  39. module just has a tons of wrapper functions.&lt;/p&gt;
  40.  
  41. &lt;h2&gt;What is Repo.transact/2?&lt;/h2&gt;
  42.  
  43. &lt;blockquote&gt;
  44. &lt;p&gt;The function Repo.transact is our small wrapper around
  45. &lt;a href=&quot;https://hexdocs.pm/ecto/Ecto.Repo.html#c:transaction/2&quot;&gt;Repo.transaction/2&lt;/a&gt;.
  46. This function commits the transaction if the lambda returns &lt;code&gt;{:ok, result}&lt;/code&gt;,
  47. rolling it back if the lambda returns &lt;code&gt;{:error, reason}&lt;/code&gt;. In both cases, the
  48. function returns the result of the lambda. We chose this approach over
  49. Ecto.Multi, because we’ve experimentally established that multi adds a lot of
  50. noise with no real benefits for our needs.&lt;/p&gt;
  51.  
  52. &lt;p&gt;-- &lt;cite&gt;Saša Jurić&lt;/cite&gt;&lt;/p&gt;
  53. &lt;/blockquote&gt;
  54.  
  55. &lt;h2&gt;Function definition&lt;/h2&gt;
  56.  
  57. &lt;p&gt;Saša never gives out the implementation of the function but I came up with
  58. this, and it works great; Exactly as you would expect.&lt;/p&gt;
  59. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyApp.Repo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  60. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  61. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;otp_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:my_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  62. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Adapters.Postgres&lt;/span&gt;
  63. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;@doc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  64. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;A small wrapper around `Repo.transaction/2&amp;#39;.&lt;/span&gt;
  65. &lt;span class=&quot;sh&quot;&gt;  Commits the transaction if the lambda returns `{:ok, result}`, rolling it&lt;/span&gt;
  66. &lt;span class=&quot;sh&quot;&gt;  back if the lambda returns `{:error, reason}`. In both cases, the function&lt;/span&gt;
  67. &lt;span class=&quot;sh&quot;&gt;  returns the result of the lambda.&lt;/span&gt;
  68. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  69. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;@spec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;
  70. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  71. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  72. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  73. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  74. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
  75. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:transaction_commited&lt;/span&gt;
  76. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  77. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:transaction_rollback_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  78. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  79. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  80. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;
  81. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  82. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  83. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  84. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  85. &lt;h2&gt;Ecto.Multi vs Repo.transact&lt;/h2&gt;
  86.  
  87. &lt;p&gt;Lets say we want to take the typical user registration flow as an example. If
  88. we want to insert a user, log the action to an audit table and also enqueue a job
  89. to send a confirmation email.&lt;/p&gt;
  90.  
  91. &lt;p&gt;We would have something like this using an Ecto.Multi:&lt;/p&gt;
  92.  
  93. &lt;h4&gt;Ecto.Multi implementation&lt;/h4&gt;
  94. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  95. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Multi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  96. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Multi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Accounts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_user_changeset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  97. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Multi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  98. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Logs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_registered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  99. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  100. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Multi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:email_job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  101. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Mailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enqueue_email_confirmation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  102. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  103. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  104. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  105. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  106. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  107. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_failed_operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;failed_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_changes_so_far&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  108. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;failed_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  109. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  110. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  111. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  112. &lt;p&gt;As we can see, it is not the worst, but once we see the &lt;code&gt;Repo.transact/2&lt;/code&gt; way,
  113. it will be clear which is better.&lt;/p&gt;
  114.  
  115. &lt;h4&gt;Repo.transact implementation&lt;/h4&gt;
  116. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  117. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  118. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Accounts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  119. &lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Logs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_registered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  120. &lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Mailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enqueue_email_confirmation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  121. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  122. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  123. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  124. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  125. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  126. &lt;p&gt;As you can see, it is much shorter and easier to read. Another big benefit is
  127. that we do not need to go down to the changeset level for inserting, we could
  128. use our functions that perform &lt;code&gt;Repo.insert&lt;/code&gt;s in them
  129. (&lt;code&gt;Accounts.new_user_changeset/1&lt;/code&gt; vs &lt;code&gt;Accounts.create_user/1&lt;/code&gt;). This lets us
  130. compose many functions together from outside the context modules without having
  131. the need to expose your changeset functions.&lt;/p&gt;
  132.  
  133. &lt;p&gt;The end result is the same, but it is a lot easier to read what is going on IMHO.&lt;/p&gt;
  134. </description>
  135.    </item>
  136.    
  137.    
  138.    
  139.    <item>
  140.      <title>Testing a Java-based Kubernetes Admission Webhook Locally</title>
  141.      <link>https://techblog.thescore.com/2022/12/23/testing-kubernetes-webhook-locally/</link>
  142.      <pubDate>Fri, 23 Dec 2022 00:00:00 +0000</pubDate>
  143.      <author></author>
  144.      <guid>https://techblog.thescore.com/2022/12/23/testing-kubernetes-webhook-locally</guid>
  145.      <description>&lt;p&gt;A Kubernetes Admission webhook can be an effective way to enforce and apply custom system policies. Admission webhooks
  146. are callbacks which allow admission requests (creating or updating a kubernetes resource) to first be forwarded to
  147. a custom external service. In the external service, administrators can enforce constraints (Validating Admission Webhook) or apply changes (Mutating
  148. Admission Webhook) to their Kubernetes resources.&lt;/p&gt;
  149.  
  150. &lt;p&gt;The Platform Core team at theScore exposes some of our internal tooling as Kubernetes custom resources. We use Validating
  151. Admission Webhooks to enforce that any resource and schema changes in a service&amp;#39;s custom resource files are valid. This
  152. allows users to catch configuration issues quickly and avoid their instances transitioning into a bad state.&lt;/p&gt;
  153.  
  154. &lt;p&gt;However, one issue with Kubernetes Webhooks is that is can be tedious to test them in a local dev environment as they
  155. are called directly from the Kubernetes API Server and to properly test, we would like to mimic this behaviour. This
  156. outlines how the Platform Team&amp;#39;s setup to test a Validating Webhook.&lt;/p&gt;
  157.  
  158. &lt;h2&gt;Prerequisites&lt;/h2&gt;
  159.  
  160. &lt;p&gt;Before being able to test a webhook, the service that is invoked from the Kubernetes API server needs to be implemented. For more information on
  161. implementing webhooks, see the kubernetes documentation &lt;a href=&quot;https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
  162.  
  163. &lt;p&gt;For the sake of this blog, it is assumed you have a Admission Webhook Service implementation that can be called via the command
  164. line and accepts keystore and trust store paths as arguments. This is helpful as kubernetes requires webhooks to have
  165. a valid TLS certificate to be successfully called.&lt;/p&gt;
  166.  
  167. &lt;p&gt;On the Platform Core Team at theScore, our webhook can be invoked using these commands:&lt;/p&gt;
  168. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Build the webhook with maven&lt;/span&gt;
  169. mvn&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;package&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-DskipTests
  170.  
  171.  
  172. datadex&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;webhook&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--port&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;port&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;num&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--keystore-path&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;path&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;to&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;keystore&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--keystore-pass&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;keystore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;password&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--truststore-path&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;truststore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;path&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--truststore-pass&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;truststore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;password&amp;gt;
  173. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  174. &lt;h2&gt;Testing Process&lt;/h2&gt;
  175.  
  176. &lt;h3&gt;Webhook Resource Definition&lt;/h3&gt;
  177.  
  178. &lt;p&gt;As part of the development of your Admission Webhook, you should have created a CRD file for the custom service to be
  179. called by the API Server. For local testing, there are some particular configurations which are needed that will be pointed
  180. out here. &lt;/p&gt;
  181.  
  182. &lt;p&gt;Below is an example webhook definition that we use on the Platform Team at theScore. As seen by the &lt;code&gt;webhooks.rules&lt;/code&gt;
  183. section, this webhook gets invoked whenever a Datadex Instance (a Platform Team custom resource) get created or updated.
  184. For local testing, you should pay attention to the &lt;code&gt;webhooks.clientConfig&lt;/code&gt; section:
  185. * &lt;code&gt;url&lt;/code&gt; is set to &lt;code&gt;https://host.docker.internal:443/validate&lt;/code&gt;
  186.   *  This defines the location of the locally running webhook. We will be using docker-desktop to create a local kubernetes cluster and the &lt;code&gt;https://host.docker.internal&lt;/code&gt; allows docker to access services running on the local machine outside the docker container. In this example, calls to this url will hit the server running on localhost at port 443.
  187. * &lt;code&gt;caBundle&lt;/code&gt; defines the CAs (Certificate Authorities) used to verify the TLS connection of the webhook. See the &amp;quot;Generating Local TLS Certificate Files&amp;quot; section below for more details&lt;/p&gt;
  188. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;admissionregistration.k8s.io/v1&lt;/span&gt;
  189. &lt;span class=&quot;nt&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;ValidatingWebhookConfiguration&lt;/span&gt;
  190. &lt;span class=&quot;nt&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  191. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;datadex-operator.thescore.com&amp;quot;&lt;/span&gt;
  192. &lt;span class=&quot;nt&quot;&gt;webhooks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  193. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;datadex-operator.thescore.com&lt;/span&gt;
  194. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  195. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;apiGroups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;datadex.thescore.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;]&lt;/span&gt;
  196. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;apiVersions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;v1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;]&lt;/span&gt;
  197. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;CREATE&amp;quot;&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;UPDATE&amp;quot;&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;]&lt;/span&gt;
  198. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;datadexinstances&amp;quot;&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;]&lt;/span&gt;
  199. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Namespaced&amp;quot;&lt;/span&gt;
  200. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;clientConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  201. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;https://host.docker.internal:443/validate&amp;quot;&lt;/span&gt;
  202. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;caBundle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;&amp;lt;This field will be populated later in this blog&amp;gt;&lt;/span&gt;
  203. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;admissionReviewVersions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;v1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;]&lt;/span&gt;
  204. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;sideEffects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;None&lt;/span&gt;
  205. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;timeoutSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;30&lt;/span&gt;
  206. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  207. &lt;h3&gt;Generating Local TLS Certificate Files&lt;/h3&gt;
  208.  
  209. &lt;p&gt;As mentioned above, webhook services must have a valid TLS certificate. The cert can be obtained in several ways but
  210. the easiest for local testing is to generate a self-signed certificate. They can be requested at any time by any developer,
  211. and never expire making testing easy. However, this is only for local testing. For other live instances and especially
  212. production, be sure to obtain a certificate from an approved Certificate Authority.&lt;/p&gt;
  213.  
  214. &lt;p&gt;To generate a self-signed cert, openssl can be used. The following bash script can be used to generate all the necessary
  215. data. It is originally from Slack&amp;#39;s open source &lt;a href=&quot;https://github.com/slackhq/simple-kubernetes-webhook/blob/main/dev/gen-certs.sh&quot;&gt;code&lt;/a&gt; and modified to this specific use case.&lt;/p&gt;
  216. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/bin/bash&lt;/span&gt;
  217.  
  218. openssl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;genrsa&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-out&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2048&lt;/span&gt;
  219.  
  220. openssl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;req&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-new&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-x509&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-sha256&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-days&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3650&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  221. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-subj&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/C=CA/CN=datadex-webhook&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  222. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-out&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.crt
  223.  
  224. openssl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;req&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-newkey&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rsa:2048&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-nodes&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-sha256&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-keyout&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  225. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-subj&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/C=CA/CN=datadex-webhook&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  226. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-out&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.csr
  227.  
  228. openssl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;x509&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-req&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  229. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-extfile&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;subjectAltName=DNS:host.docker.internal&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  230. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-sha256&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  231. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-days&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3650&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  232. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-in&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.csr&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  233. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-CA&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-CAkey&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-CAcreateserial&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  234. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;-out&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.crt
  235.  
  236. cat&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.bundle.crt
  237.  
  238. &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;
  239. &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;gt;&amp;gt; Generating kube secrets...&amp;quot;&lt;/span&gt;
  240. kubectl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;create&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;secret&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;tls&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;validating-webhook-tls&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  241. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;--cert&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;server.bundle.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  242. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;--key&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;server.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  243. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;--dry-run&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;client&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-o&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;yaml&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  244. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&amp;gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;webhook-secret.yaml
  245.  
  246. &lt;span class=&quot;nv&quot;&gt;formatted_crt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;cat&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;base64&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;fold&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
  247. &lt;span class=&quot;c1&quot;&gt;# yq is a command line yaml processor. See https://github.com/mikefarah/yq for info and install instructions&lt;/span&gt;
  248. yq&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-y&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-i&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-e&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;.webhooks[0].clientConfig.caBundle = \&amp;quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$formatted_crt&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;webhook-configuration.yaml
  249.  
  250. &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;
  251. &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;gt;&amp;gt; Generating Keystore and truststore files&amp;quot;&lt;/span&gt;
  252. openssl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pkcs12&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-export&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-inkey&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-in&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-out&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;key.pkcs12&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-password&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pass:123456
  253. keytool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-importkeystore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-noprompt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-srckeystore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;key.pkcs12&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-srcstoretype&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pkcs12&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-destkeystore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;key.jks&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-storepass&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-srcstorepass&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;
  254.  
  255. csplit&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-f&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;crt-&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-n&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.bundle.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/-----BEGIN CERTIFICATE-----/&amp;#39;&lt;/span&gt;
  256. &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;file&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;crt-*&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  257. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;keytool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-delete&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-noprompt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-keystore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;trust.jks&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-trustcacerts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-storepass&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-alias&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;service-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;
  258. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;keytool&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-import&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-noprompt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-keystore&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;trust.jks&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-file&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-storepass&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-alias&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;service-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  259. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;rm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  260. &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
  261.  
  262. rm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ca.srl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.csr&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.key&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;server.bundle.crt&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;key.pkcs12
  263. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  264. &lt;p&gt;This script does a couple of things
  265. 1. Outputs two files; &lt;code&gt;key.jks&lt;/code&gt; and &lt;code&gt;trust.jks&lt;/code&gt;. These are stores for the self-signed server key and TLS certificate which will later be provided to the webhook service as arguments
  266. 2. Creates a YAML file (&lt;code&gt;webhook-secret.yaml&lt;/code&gt;) which defines a Kubernetes TLS secret resource
  267. 3. Populates the &lt;code&gt;caBundle&lt;/code&gt; field of the local Validating Webhook Kubernetes CRD (&lt;code&gt;webhook-configuration.yaml&lt;/code&gt;). This defines the local CA which allows the self-signed cert to be verified.&lt;/p&gt;
  268.  
  269. &lt;h3&gt;Local Kubernetes Environment Setup&lt;/h3&gt;
  270.  
  271. &lt;p&gt;Now we must set up and configure our local kubernetes cluster to enable it to call our Custom Webhook.&lt;/p&gt;
  272.  
  273. &lt;h4&gt;Docker-Desktop Setup&lt;/h4&gt;
  274.  
  275. &lt;p&gt;For this example we will use docker-desktop. It provides out of the box kubernetes so to get a local kubernetes cluster
  276. running, all we need to do is download docker-desktop and enable Kubernetes. See docker&amp;#39;s &lt;a href=&quot;https://docs.docker.com/desktop/kubernetes/&quot;&gt;documentation&lt;/a&gt; for more
  277. information on this.&lt;/p&gt;
  278.  
  279. &lt;h4&gt;Kubernetes Setup&lt;/h4&gt;
  280.  
  281. &lt;p&gt;Once docker is all setup, next we need to install &lt;code&gt;kubectl&lt;/code&gt; if not done already:
  282. * MacOS: &lt;code&gt;brew install kubectl&lt;/code&gt;
  283. * See &lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/&quot;&gt;documentation&lt;/a&gt; for other installation method&lt;/p&gt;
  284.  
  285. &lt;h4&gt;Webhook Configuration&lt;/h4&gt;
  286.  
  287. &lt;p&gt;The final step is to configure the kubernetes cluster to actually call our webhook whenever an update/create resource
  288. request is made.
  289. 1. Switch to the docker-desktop k8s context
  290.   * &lt;code&gt;kubectl config use-context docker-desktop&lt;/code&gt;
  291. 2. [Optional] You can create a custom namespace for testing if desired or just use the default namespace.
  292. 3. Configure the cluster to call the local webhook:
  293.   * Setup TLS secret: &lt;code&gt;kubectl apply -f webhook-secret.yaml&lt;/code&gt;
  294.   * Apply the webhook configuration: &lt;code&gt;kubectl apply -f webhook-configuration.yaml&lt;/code&gt;&lt;/p&gt;
  295.  
  296. &lt;h3&gt;Invoking Webhook Endpoints&lt;/h3&gt;
  297.  
  298. &lt;p&gt;At this point all the setup and configuration is complete and the webhook is ready to be tested:
  299. 1. Start a local instance of the webhook (substitute the command with the command to start up your webhook)
  300.   * &lt;code&gt;datadex webhook --port=443 --keystore-path=key.jks --keystore-pass=123456 --truststore-path=trust.jks --truststore-pass=123456&lt;/code&gt;
  301. 2. In the docker-desktop cluster environment you previously setup, create or update the resource that the webhook is listening to:
  302.   * &lt;code&gt;kubectl apply -f &amp;lt;yaml file&amp;gt;&lt;/code&gt;&lt;/p&gt;
  303.  
  304. &lt;p&gt;That&amp;#39;s it! At this point, the Kubernetes API Server in docker-desktop will have called your local webhook instance and
  305. the behaviour of your webhook in an actual live kubernetes cluster can be tested. You can actively develop and debug
  306. with instant feedback.&lt;/p&gt;
  307.  
  308. &lt;h3&gt;Alternate Testing Options&lt;/h3&gt;
  309.  
  310. &lt;p&gt;If you do not want to test your webhook being called from the actual Kubernetes API Server, an alternative is to use
  311. cURL as a quick and easy way to manually call the endpoints of your Webhook:&lt;/p&gt;
  312.  
  313. &lt;ol&gt;
  314. &lt;li&gt;Generate the TLS certificates. See the &amp;quot;Generating Local TLS Certificate Files&amp;quot; section above&lt;/li&gt;
  315. &lt;li&gt;Run the webhook service from the command line:&lt;/li&gt;
  316. &lt;/ol&gt;
  317. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;mvn&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;package&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-DskipTests
  318. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;datadex&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;webhook&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--port&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--keystore-path&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;key.jks&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--keystore-pass&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--truststore-path&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;trust.jks&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--truststore-pass&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;
  319. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  320. &lt;ol&gt;
  321. &lt;li&gt;Use cURL to hit webhook endpoint:&lt;/li&gt;
  322. &lt;/ol&gt;
  323. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Pinging a GET endpoint with no Payload (eg: health endpoint)&lt;/span&gt;
  324. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  325. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;curl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-k&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;https://localhost:8443/health
  326.  
  327. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Hitting an endpoint expecting data:&lt;/span&gt;
  328. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#  &lt;/span&gt;
  329. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# The --data option is the payload the the k8s API server will provide when it calls the webhook&lt;/span&gt;
  330. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#webhook-request-and-response for details&lt;/span&gt;
  331. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  332. &lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;curl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--data&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&amp;lt;Request JSON File Path&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-k&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;https://localhost:443/&amp;lt;endpoint&amp;gt;
  333. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
  334.    </item>
  335.    
  336.    
  337.    
  338.    <item>
  339.      <title>PostgreSQL Queries on JSONB Fields with Ecto</title>
  340.      <link>https://techblog.thescore.com/2022/06/23/embedded-schema-queries-with-ecto/</link>
  341.      <pubDate>Thu, 23 Jun 2022 00:00:00 +0000</pubDate>
  342.      <author></author>
  343.      <guid>https://techblog.thescore.com/2022/06/23/embedded-schema-queries-with-ecto</guid>
  344.      <description>&lt;p&gt;The Promotions team at theScore is responsible for enabling dynamic marketing campaigns within &lt;a href=&quot;https://thescore.bet/&quot;&gt;theScore Bet&lt;/a&gt; app.  Each promotional campaign allows us to specify an audience of users who will be available to participate in the campaign, by comparing on past actions that a given user has taken against a set of dynamic rules.  For example, when creating a new promotion, we can specify that its audience can only include patrons who have previously deposited a specific amount of money, or placed a specific quantity of bets.&lt;/p&gt;
  345.  
  346. &lt;p&gt;Disclaimer: The specific scenario, and format of the data shown in the examples throughout, is contrived entirely for the purposes of this article.&lt;/p&gt;
  347.  
  348. &lt;h2&gt;An overview of the PostgreSQL schema&lt;/h2&gt;
  349.  
  350. &lt;p&gt;The Promotions service depends on the ability to read past events placed by a patron.  These events are received asynchronously from other internal services, via Kafka, and then stored internally in the Promotions database as a generic &amp;quot;user action&amp;quot;.  These actions are used for a variety of purposes within our Promotions service, but in this case, we are exploring looking at the past actions of a given user to see if they fulfill the audience criteria of a given promotional campaign.  The table which they get stored within has been created with a migration that resembled the code sample below.&lt;/p&gt;
  351. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.Repo.Migrations.CreateUserActionsTable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  352. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Migration&lt;/span&gt;
  353.  
  354. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;change&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  355. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  356. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  357. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  358. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:occurred_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:utc_datetime_usec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  359. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  360. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:binary_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  361. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:jsonb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  362.  
  363. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  364. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  365. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  366. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  367. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  368. &lt;p&gt;The corresponding Ecto schema for the &amp;quot;user_actions&amp;quot; table looks like the following.&lt;/p&gt;
  369. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  370. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Schema&lt;/span&gt;
  371.  
  372. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction.CasinoWagerAttributes&lt;/span&gt;
  373. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction.DepositAttributes&lt;/span&gt;
  374. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction.RegistrationAttributes&lt;/span&gt;
  375. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction.SportsbookBetAttributes&lt;/span&gt;
  376.  
  377. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;user_actions&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  378. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
  379. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:registration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deposit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:sportsbook_bet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:casino_wager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  380. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:occurred_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:utc_datetime_usec&lt;/span&gt;
  381. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:usa_new_jersey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:usa_colorado&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:usa_indiana&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:usa_iowa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:can_ontario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  382. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.UUID&lt;/span&gt;
  383.  
  384. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeds_one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:casino_wager_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CasinoWagerAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attributes&lt;/span&gt;
  385. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeds_one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deposit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DepositAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attributes&lt;/span&gt;
  386. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeds_one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:registration_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RegistrationAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attributes&lt;/span&gt;
  387. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeds_one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:sportsbook_bet_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SportsbookBetAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attributes&lt;/span&gt;
  388.  
  389. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  390. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  391. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  392. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  393. &lt;p&gt;Each of the various embedded schemas to populate the &lt;code&gt;attributes&lt;/code&gt; field is represented in a separate Ecto schema of its own.  For the purposes of this article, only a truncated sample of the &lt;code&gt;SportsbookBetAttributes&lt;/code&gt; embedded schema will be shown below as well.&lt;/p&gt;
  394. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction.SportsbookBetAttributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  395. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Schema&lt;/span&gt;
  396.  
  397. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;@primary_key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  398. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedded_schema&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  399. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:amount_cents_wagered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:integer&lt;/span&gt;
  400. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:amount_cents_payout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:integer&lt;/span&gt;
  401. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:bet_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:straight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:parlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  402. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:odds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:integer&lt;/span&gt;
  403.  
  404. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeds_many&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:legs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SportsbookBetLegAttributes&lt;/span&gt;
  405. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  406. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  407.  
  408. &lt;span class=&quot;kd&quot;&gt;defmodule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction.SportsbookBetLegAttributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  409. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Ecto.Schema&lt;/span&gt;
  410.  
  411. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;@primary_key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  412. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embedded_schema&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  413. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:event_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
  414. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:event_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
  415. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:market_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
  416. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:market_selection_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
  417. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:market_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
  418. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:in_play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:boolean&lt;/span&gt;
  419. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:odds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:integer&lt;/span&gt;
  420. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  421. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  422. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  423. &lt;h2&gt;Example Criteria to Turn into Database Queries&lt;/h2&gt;
  424.  
  425. &lt;p&gt;The audience criteria of a promotional campaign can be based on any type of action that we record (ie. deposit, sportsbook bet, etc) and the various attributes of the action.  The following are examples of such an audience rule.&lt;/p&gt;
  426.  
  427. &lt;ul&gt;
  428. &lt;li&gt;  The patron &lt;strong&gt;registered&lt;/strong&gt; their account in January 2022&lt;/li&gt;
  429. &lt;li&gt;  The patron has placed at least 3 &lt;strong&gt;deposits&lt;/strong&gt; from within New Jersey&lt;/li&gt;
  430. &lt;li&gt;  The patron placed at least one &lt;strong&gt;sportsbook bet&lt;/strong&gt; on the last years Super Bowl&lt;/li&gt;
  431. &lt;/ul&gt;
  432.  
  433. &lt;p&gt;For the first two examples, the data is stored within more fields that are of a more primitive data type within the &lt;code&gt;user_actions&lt;/code&gt; table of the database, and are standard to query.  For the last example, it requires querying the JSONB &lt;code&gt;attributes&lt;/code&gt; field to determine if any of the &amp;quot;legs&amp;quot; on &lt;strong&gt;any&lt;/strong&gt; of the users previous sportsbook bets contain the &lt;code&gt;event_id&lt;/code&gt; associated with last years Super Bowl.&lt;/p&gt;
  434.  
  435. &lt;p&gt;Similarly, the audience criteria also needs to support scenarios where &amp;quot;The patron has placed a &lt;em&gt;sportsbook bet&lt;/em&gt; that was not on last years Super Bowl&amp;quot; as well.  This would require looking for a sportsbook bet that does &lt;strong&gt;not&lt;/strong&gt; have an &lt;code&gt;event_id&lt;/code&gt; corresponding to the Super Bowl for the previous year in &lt;strong&gt;any&lt;/strong&gt; of its legs.&lt;/p&gt;
  436.  
  437. &lt;p&gt;The queries we need to write need to work for both &amp;quot;at least one leg of a sportsbook contains&amp;quot; and &amp;quot;none of the legs of a sportsbook bet can contain&amp;quot; scenarios.  To do so, we need to investigate how to make precise queries on a set of data within a JSONB field in PostgreSQL, and subsequently how we can turn those queries into composable Ecto queries within our Elixir application as well.&lt;/p&gt;
  438.  
  439. &lt;p&gt;Notably, we want to be able to support operators of &amp;quot;in&amp;quot; and &amp;quot;not in&amp;quot; when querying for data, as opposed to operators such as &amp;quot;equals&amp;quot;, &amp;quot;not equals&amp;quot;, &amp;quot;greater than or equals&amp;quot;, etc.&lt;/p&gt;
  440.  
  441. &lt;h2&gt;Query of a JSONB Field&lt;/h2&gt;
  442.  
  443. &lt;p&gt;The first results our team came across while looking into this was using PostgreSQL native &lt;a href=&quot;%5Bhttps://www.postgresql.org/docs/current/functions-json.html%5D(https://www.postgresql.org/docs/current/functions-json.html)&quot;&gt;functions and operators&lt;/a&gt;.  We quickly realized that these would not fit all of our use-cases. It would be challenging to turn it into a reusable Ecto query that would work for a variety of fields and would be an inelegant solution for deeply nested data.&lt;/p&gt;
  444.  
  445. &lt;p&gt;The next option we came across was to use the function &lt;code&gt;jsonb_to_recordset&lt;/code&gt; to create a queryable schema of records from an array within a JSONB field (ie. the &lt;code&gt;embeds_many :legs&lt;/code&gt; section of our bet attributes).&lt;/p&gt;
  446.  
  447. &lt;p&gt;In the example below, we’ll try to find if the user has at least one bet with the following attributes.&lt;/p&gt;
  448. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;fcca980b-8edc-4848-91cc-4e00ff47019c&amp;quot;&lt;/span&gt;
  449. &lt;span class=&quot;nt&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;:can_ontario&lt;/span&gt;
  450. &lt;span class=&quot;nt&quot;&gt;amount_cents_wagered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;at least 1500 (ie. $15)&lt;/span&gt;
  451. &lt;span class=&quot;nt&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;(not in any of the following)&lt;/span&gt;
  452. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;- &amp;quot;e8a95b17-972c-4421-9090-8bf66c0804a9&amp;quot;&lt;/span&gt;
  453. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;- &amp;quot;5fbef652-e772-4bae-84f5-31255fe2fadd&amp;quot;&lt;/span&gt;
  454. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;- &amp;quot;dd92d86c-fe6d-43a7-8765-a3e1026145db&amp;quot;&lt;/span&gt;
  455. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  456. &lt;h2&gt;Using &lt;code&gt;jsonb_to_recordset&lt;/code&gt; with SQL&lt;/h2&gt;
  457.  
  458. &lt;p&gt;The first step to transforming this into a usable query within our Elixir app was to try to get it working with a query made directly against the database to validate that it is the right tool for the job.&lt;/p&gt;
  459.  
  460. &lt;p&gt;After some initial research, we composed the following SQL query:&lt;/p&gt;
  461. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
  462. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_actions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  463. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonb_to_recordset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;legs&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;event_id&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  464. &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;
  465. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;f0bb75d3-2cce-4328-9177-02349fa99de6&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  466. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;ontario&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  467. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;amount_cents_wagered&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  468. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  469. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;e8a95b17-972c-4421-9090-8bf66c0804a9&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  470. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;5fbef652-e772-4bae-84f5-31255fe2fadd&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  471. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;dd92d86c-fe6d-43a7-8765-a3e1026145db&amp;#39;&lt;/span&gt;
  472. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  473. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  474. &lt;p&gt;While this allows us to use the operators we want (ie. &lt;code&gt;&amp;gt;=&lt;/code&gt; and &lt;code&gt;NOT IN&lt;/code&gt;) on data within the &amp;quot;attributes&amp;quot; JSONB field, and gets us the exact records we want, it would be a challenge to try to make it fit into an Ecto query since Ecto queries only allow for one entity to be in the &lt;code&gt;from&lt;/code&gt; clause of a given query.  For example, we are permitted to begin a query with &lt;code&gt;from UserAction&lt;/code&gt;, but are unable to create a query that selects from two data sources such as &lt;code&gt;from UserAction, fragment(&amp;quot;jsonb_to_recordset(...)&amp;quot;)&lt;/code&gt;.  If there is a way around that, we were unable to find a solution.&lt;/p&gt;
  475.  
  476. &lt;p&gt;The next option we looked to was modifying the first query by utilizing a lateral-join to try to make a &amp;quot;user action&amp;quot; such that it has a one-to-many relationship with its own &amp;quot;legs&amp;quot; nested within the JSONB &amp;quot;attributes&amp;quot; field.  By doing so, we eliminate the need to use multiple clauses in the &lt;code&gt;FROM&lt;/code&gt; section of the query, which helps gets us closer to a valid Ecto query translation.  The query then looks like this:&lt;/p&gt;
  477. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
  478. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  479. &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;
  480. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_actions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;
  481. &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LATERAL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  482. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  483. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonb_to_recordset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;legs&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  484. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;event_id&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  485. &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
  486. &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;
  487. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;f0bb75d3-2cce-4328-9177-02349fa99de6&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  488. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;can_ontario&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  489. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;amount_cents_wagered&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  490. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  491. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;e8a95b17-972c-4421-9090-8bf66c0804a9&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  492. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;5fbef652-e772-4bae-84f5-31255fe2fadd&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  493. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;dd92d86c-fe6d-43a7-8765-a3e1026145db&amp;#39;&lt;/span&gt;
  494. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  495. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  496. &lt;p&gt;The problem we quickly saw with this query is that will look to see if &lt;em&gt;any&lt;/em&gt; of the &amp;quot;legs&amp;quot; of a specific bet adhere to the condition.  Instead, we want to make sure that the &lt;strong&gt;all&lt;/strong&gt; of the legs within the bet are not on those listed events (ie. cannot have any of the specified &amp;quot;event id&amp;quot; values).  As such, we found that putting the inner join in a subquery helped to achieve the goal, wrapping it in a &amp;quot;NOT EXISTS&amp;quot; condition.&lt;/p&gt;
  497. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
  498. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  499. &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;
  500. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_actions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;
  501. &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;
  502. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;f0bb75d3-2cce-4328-9177-02349fa99de6&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  503. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;ontario&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  504. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;amount_cents_wagered&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  505. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  506. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
  507. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  508. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;
  509. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_actions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lateral_action&lt;/span&gt;
  510. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LATERAL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  511. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  512. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonb_to_recordset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lateral_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;legs&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  513. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;event_id&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  514. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
  515. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;
  516. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lateral_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;
  517. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  518. &lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;e8a95b17-972c-4421-9090-8bf66c0804a9&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  519. &lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;5fbef652-e772-4bae-84f5-31255fe2fadd&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  520. &lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;dd92d86c-fe6d-43a7-8765-a3e1026145db&amp;#39;&lt;/span&gt;
  521. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  522. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  523. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  524. &lt;p&gt;This query seems to meet all of the goals that we were looking to achieve, so the next step was to take it and turn it into a composable query within Ecto.&lt;/p&gt;
  525.  
  526. &lt;h2&gt;Translating SQL query to an Ecto Query&lt;/h2&gt;
  527.  
  528. &lt;p&gt;Many parts of our query are pretty quick and easy to make into an &lt;code&gt;Ecto.Query&lt;/code&gt; structure.  We can begin with everything before the subquery.  For this example, we can assume all of the values were passed in and the operators for each part of the expression are known:&lt;/p&gt;
  529. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount_cents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  530. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  531. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  532. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  533. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  534. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;(?-&amp;gt;?)::integer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;amount_cents_wagered&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount_cents&lt;/span&gt;
  535. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  536. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  537. &lt;p&gt;We also need to build a query around the &lt;code&gt;jsonb_to_recordset&lt;/code&gt; data that we created in the lateral join seen earlier.&lt;/p&gt;
  538.  
  539. &lt;p&gt;This was a bit challenging, as &lt;code&gt;jsonb_to_recordset&lt;/code&gt; is a native function of PostgreSQL and there are no functions built into Ecto to work with it directly.  It also requires us to define an &lt;code&gt;AS ... ON ...&lt;/code&gt; set of clauses to use with it.  Since Ecto will create its own &lt;code&gt;AS&lt;/code&gt; alias with each &amp;quot;from&amp;quot; or &amp;quot;join&amp;quot;, that presents challenges as well.&lt;/p&gt;
  540.  
  541. &lt;p&gt;After some research on this issue on the elixir forums, we were pointed in the right direction to wrap the subquery in parentheses, so that we could use our own &lt;code&gt;AS&lt;/code&gt; within it, and it would not conflict with the one Ecto adds on.&lt;/p&gt;
  542. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_legs_query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  543. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  544. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:leg_user_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  545. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;inner_lateral_join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  546. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  547. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;(SELECT * FROM jsonb_to_recordset(?-&amp;gt;&amp;#39;legs&amp;#39;) AS record(event_id varchar)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  548. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:sportsbook_bet_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  549. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  550. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  551. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:sportsbook_bet_legs&lt;/span&gt;
  552. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  553. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  554. &lt;p&gt;To utilize both queries together, we need to include the &lt;code&gt;sportsbook_bet_legs_query&lt;/code&gt; function as a subquery within the first, and wrap it in a &lt;code&gt;where not exists&lt;/code&gt; clause of the outer query.&lt;/p&gt;
  555.  
  556. &lt;p&gt;Since the subquery uses a lateral join, we need to remember to join it back to the outer query using Ecto’s &lt;code&gt;parent_as/1&lt;/code&gt; function.  Another example of this can be found in Ecto’s documentation for &lt;code&gt;exists/1&lt;/code&gt; as well (&lt;a href=&quot;https://hexdocs.pm/ecto/Ecto.Query.API.html#exists/1&quot;&gt;https://hexdocs.pm/ecto/Ecto.Query.API.html#exists/1&lt;/a&gt;)&lt;/p&gt;
  557. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount_cents_wagered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  558. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Promotions.UserAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  559. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  560. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  561. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  562. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;(?-&amp;gt;?)::integer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;amount_cents_wagered&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount_cents_wagered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  563. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  564. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  565. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  566. &lt;span class=&quot;w&quot;&gt;               &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;leg_user_action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action_subquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  567. &lt;span class=&quot;w&quot;&gt;               &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;sportsbook_bet_legs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_legs&lt;/span&gt;
  568. &lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_ids_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  569. &lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent_as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action_subquery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  570. &lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_legs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_ids&lt;/span&gt;
  571. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  572. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  573. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  574. &lt;p&gt;With the function now complete, it can be called by simply taking the earlier example and passing in the arguments to the reusable function:&lt;/p&gt;
  575. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;fcca980b-8edc-4848-91cc-4e00ff47019c&amp;quot;&lt;/span&gt;
  576. &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:can_ontario&lt;/span&gt;
  577. &lt;span class=&quot;n&quot;&gt;amount_cents_wagered&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1_500&lt;/span&gt;
  578. &lt;span class=&quot;n&quot;&gt;event_ids&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  579. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;e8a95b17-972c-4421-9090-8bf66c0804a9&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  580. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;5fbef652-e772-4bae-84f5-31255fe2fadd&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  581. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;dd92d86c-fe6d-43a7-8765-a3e1026145db&amp;quot;&lt;/span&gt;
  582. &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  583.  
  584. &lt;span class=&quot;n&quot;&gt;ecto_query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sportsbook_bet_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount_cents_wagered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  585. &lt;span class=&quot;n&quot;&gt;matching_bets&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ecto_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  586.  
  587. &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matching_bets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  588. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# user qualified for promotion&lt;/span&gt;
  589. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  590. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  591. &lt;p&gt;As a reminder, one of the goals of our function was to find a &amp;quot;user action&amp;quot; record that did not contain any of the values of the list of &lt;code&gt;event_id&lt;/code&gt;’s from the attributes of any of its array of &amp;quot;leg&amp;quot; data points.  One benefit of structuring the query in the way that has been shown here is that if we want to flip it to find if one (or more) legs &lt;strong&gt;does&lt;/strong&gt; include a particular &lt;code&gt;event_id&lt;/code&gt;, we can simply remove the &lt;code&gt;not&lt;/code&gt; from the &lt;code&gt;not exists&lt;/code&gt; condition without any other modifications.&lt;/p&gt;
  592.  
  593. &lt;p&gt;The queries that we need in our application are more robust, including changing up the operators that we use instead of statically choosing &amp;quot;equals&amp;quot;, &amp;quot;greater than or equals&amp;quot; and &amp;quot;not in&amp;quot; as outlined in our example, but from this template we were able to build out a library of robust and composable queries to scope results based on attributes both in more primitive data types within our database schema, as well as JSONB fields too.&lt;/p&gt;
  594.  
  595. &lt;p&gt;I&amp;#39;ve set up a small-scale Elixir application which can be used to quickly scaffold a database with records, as seen in the format throughout this blog post, so you can test the queries seen within this article and tweak them as needed to experiment. &lt;a href=&quot;https://github.com/michaelfich/ecto_jsonb_example&quot;&gt;Github Repository: Ecto JSONB Example&lt;/a&gt;.&lt;/p&gt;
  596. </description>
  597.    </item>
  598.    
  599.    
  600.    
  601.    <item>
  602.      <title>Migrating PostgreSQL to Aurora with near Zero-Downtime</title>
  603.      <link>https://techblog.thescore.com/2018/06/29/migrating-postgresql-to-aurora-with-near-zero-downtime/</link>
  604.      <pubDate>Fri, 29 Jun 2018 00:00:00 +0000</pubDate>
  605.      <author></author>
  606.      <guid>https://techblog.thescore.com/2018/06/29/migrating-postgresql-to-aurora-with-near-zero-downtime</guid>
  607.      <description>&lt;p&gt;&lt;img src=&quot;/assets/elephant-migration.jpg&quot; alt=&quot;&quot;&gt;
  608. &lt;a href=&quot;https://flickr.com/photos/jeaneeem/22658750486&quot; title=&quot;15_CBP_JMP-39&quot;&gt;15&lt;em&gt;CBP&lt;/em&gt;JMP-39&lt;/a&gt; by &lt;a href=&quot;https://flickr.com/people/jeaneeem&quot;&gt;jeaneeem&lt;/a&gt; is licensed under &lt;a href=&quot;https://creativecommons.org/licenses/by-nc-nd/2.0/&quot;&gt;CC BY-NC-ND&lt;/a&gt;&lt;/p&gt;
  609.  
  610. &lt;h1&gt;Migrating PostgreSQL to Aurora with near Zero-Downtime&lt;/h1&gt;
  611.  
  612. &lt;p&gt;The goal here is to migrate an Amazon Web Services (AWS) Relational Database Service (RDS) PostgreSQL Database to Aurora with as little downtime as possible. The following documents the initial strategy that was considered using read replica promotion, followed by the end result using AWS&amp;#39;s Database Migration Service (DMS).&lt;/p&gt;
  613.  
  614. &lt;h1&gt;Why Aurora&lt;/h1&gt;
  615.  
  616. &lt;p&gt;From the &lt;a href=&quot;https://aws.amazon.com/rds/aurora/details/&quot;&gt;Aurora Details Page&lt;/a&gt;:&lt;/p&gt;
  617.  
  618. &lt;blockquote&gt;
  619. &lt;p&gt;Amazon Aurora is designed to offer greater than 99.99% availability, increasing MySQL and PostgreSQL performance and availability by tightly integrating the database engine with an SSD-backed virtualized storage layer purpose-built for database workloads. Amazon Aurora&amp;#39;s storage is fault-tolerant and self-healing and disk failures are repaired in the background without loss of database availability. Amazon Aurora is designed to automatically detect database crashes and restart without the need for crash recovery or to rebuild the database cache. If the entire instance fails, Amazon Aurora will automatically fail over to one of up to 15 read replicas.&lt;/p&gt;
  620. &lt;/blockquote&gt;
  621.  
  622. &lt;p&gt;Essentially, better performance, reliability, and the ability to scale.&lt;/p&gt;
  623.  
  624. &lt;p&gt;Our configuration changes become easier. Aurora acts as a cluster, so we have two endpoints to deal with, and the replicas underneath will adjust dynamically. We use &lt;a href=&quot;https://github.com/taskrabbit/makara&quot;&gt;Makara&lt;/a&gt; to spread our database queries between our master and slaves. With RDS PostgreSQL we would have to enumerate all our slave databases so we could take advantage of the replicas. If we added a new replica, we would have to adjust our configuration to take advantage of it. With Aurora, we have two endpoints to deal with now (primary/writer and a read-only URL). These endpoints will dynamically rotate between the available instances that fall under that cluster URL. This is great as it handles a failover to a different primary database, or changing the number of replicas. Effectively, we end up with less configuration, but we still need Makara to direct our write and read queries to the two cluster URLs.&lt;/p&gt;
  625.  
  626. &lt;h1&gt;Migration Approaches&lt;/h1&gt;
  627.  
  628. &lt;p&gt;As mentioned earlier, there are two approaches to doing this migration. First, we&amp;#39;ll cover the &lt;em&gt;recommended&lt;/em&gt; approach that AWS suggested as we&amp;#39;re on the RDS platform. As we&amp;#39;ll cover, it is easy and quick but it does incur downtime during the migration. The second approach uses DMS and was brought up to us after opening a support ticket with AWS on how to do our migration with zero downtime.&lt;/p&gt;
  629.  
  630. &lt;h2&gt;Read Replica Promotion&lt;/h2&gt;
  631.  
  632. &lt;p&gt;This is the &lt;em&gt;recommended&lt;/em&gt; approach for migrating from PostgreSQL to Aurora. It is easy and has a little risk (during the migration), although it has unavoidable downtime.&lt;/p&gt;
  633.  
  634. &lt;ul&gt;
  635. &lt;li&gt;Create an Aurora read replica off of your master database.&lt;/li&gt;
  636. &lt;li&gt;Pause the master PostgreSQL database.
  637.  
  638. &lt;ul&gt;
  639. &lt;li&gt;This is done so that the no additional write changes are occurring (as it won&amp;#39;t be replicated to the Aurora read replica during the promotion process).&lt;/li&gt;
  640. &lt;/ul&gt;&lt;/li&gt;
  641. &lt;li&gt;Promote the Aurora read replica.
  642.  
  643. &lt;ul&gt;
  644. &lt;li&gt;This takes about 10-20 minutes (at least in our experience).&lt;/li&gt;
  645. &lt;/ul&gt;&lt;/li&gt;
  646. &lt;li&gt;Deploy app configuration changes to use new Aurora cluster for write/reads.&lt;/li&gt;
  647. &lt;li&gt;Immediately start building additional read replicas to match existing number.
  648.  
  649. &lt;ul&gt;
  650. &lt;li&gt;Each one takes about 5-10 minutes to create (at least in our experience).&lt;/li&gt;
  651. &lt;/ul&gt;&lt;/li&gt;
  652. &lt;li&gt;When happy with the new setup, remove/delete the old databases.&lt;/li&gt;
  653. &lt;/ul&gt;
  654.  
  655. &lt;p&gt;It is &lt;em&gt;recommended&lt;/em&gt; to do a dry run of the promotion (and just throw it away afterwards) to gauge how long that process takes.&lt;/p&gt;
  656.  
  657. &lt;p&gt;The issue here is that you have:&lt;/p&gt;
  658.  
  659. &lt;ul&gt;
  660. &lt;li&gt;Downtime due to the promotion process. You need to prevent writes from happening on the master. Replication between the PostgreSQL master and the Aurora read replica breaks when the promotion process starts.&lt;/li&gt;
  661. &lt;li&gt;Immediately after you have your new Aurora master, you need to start building read replicas up.
  662.  
  663. &lt;ul&gt;
  664. &lt;li&gt;In our case, we would want to match our existing number of replicas. So this would take additional time. Otherwise, you possibly run the risk of overwhelming the Database without those read replicas up.&lt;/li&gt;
  665. &lt;/ul&gt;&lt;/li&gt;
  666. &lt;/ul&gt;
  667.  
  668. &lt;p&gt;One good thing is that even with the downtime the old read replicas will continue to serve up &lt;code&gt;GET&lt;/code&gt; requests. In addition, any CDN/&lt;a href=&quot;https://varnish-cache.org/&quot;&gt;Varnish&lt;/a&gt; caching will also help serve stale data during the downtime.&lt;/p&gt;
  669.  
  670. &lt;h2&gt;Full Load and Ongoing Replication with DMS&lt;/h2&gt;
  671.  
  672. &lt;p&gt;This approach is much more involved and finicky, but it does offer a &lt;em&gt;near&lt;/em&gt; zero-downtime migration strategy. The general process here is:&lt;/p&gt;
  673.  
  674. &lt;ul&gt;
  675. &lt;li&gt;Create an Aurora read replica (this is so we can keep the DB schema)&lt;/li&gt;
  676. &lt;li&gt;Promote Aurora read replica so it is on its own&lt;/li&gt;
  677. &lt;li&gt;Tweak settings on PostgreSQL DB&lt;/li&gt;
  678. &lt;li&gt;Setup DMS Endpoints for the original PostgreSQL and Aurora databases&lt;/li&gt;
  679. &lt;li&gt;Setup DMS Replication Instance to assist in the migration&lt;/li&gt;
  680. &lt;li&gt;Setup DMS Task to use the endpoints and the replication instance&lt;/li&gt;
  681. &lt;li&gt;Truncate Aurora data (we want to wipe the data, but we keep the schema)&lt;/li&gt;
  682. &lt;li&gt;Initiate the migration
  683.  
  684. &lt;ul&gt;
  685. &lt;li&gt;The first phase is a &lt;em&gt;full load&lt;/em&gt; of the tables from the source to the target&lt;/li&gt;
  686. &lt;li&gt;The second phase is an &lt;em&gt;on-going replication&lt;/em&gt; of changes from the source to the target&lt;/li&gt;
  687. &lt;/ul&gt;&lt;/li&gt;
  688. &lt;li&gt;Both DBs are in-sync now&lt;/li&gt;
  689. &lt;li&gt;Create read replicas off of the Aurora master&lt;/li&gt;
  690. &lt;li&gt;Change app configuration to point to Aurora cluster&lt;/li&gt;
  691. &lt;li&gt;Delete old DBs&lt;/li&gt;
  692. &lt;/ul&gt;
  693.  
  694. &lt;p&gt;This is a multi-part process, so the following sections will walk through each part.&lt;/p&gt;
  695.  
  696. &lt;h3&gt;Setting up Aurora Read Replica&lt;/h3&gt;
  697.  
  698. &lt;ul&gt;
  699. &lt;li&gt;Create an Aurora read replica of the PostgreSQL database
  700.  
  701. &lt;ul&gt;
  702. &lt;li&gt;Name it something like &lt;code&gt;&amp;lt;service&amp;gt;—&amp;lt;environment&amp;gt;-aurora&lt;/code&gt;&lt;/li&gt;
  703. &lt;/ul&gt;&lt;/li&gt;
  704. &lt;li&gt;By creating an Aurora instance, we also get an Aurora cluster (i.e., &lt;code&gt;&amp;lt;service&amp;gt;—&amp;lt;environment&amp;gt;-aurora-cluster&lt;/code&gt;)
  705.  
  706. &lt;ul&gt;
  707. &lt;li&gt;Ensure the subnet group and the security groups are set to the same values as defined for the PostgreSQL DB.&lt;/li&gt;
  708. &lt;/ul&gt;&lt;/li&gt;
  709. &lt;li&gt;You don&amp;#39;t need to have multi-AZ here, Aurora will handle fast failover if we have a replica&lt;/li&gt;
  710. &lt;li&gt;Additional read replicas can then be added later using &lt;code&gt;&amp;lt;service&amp;gt;—&amp;lt;environment&amp;gt;-aurora-&amp;lt;##&amp;gt;&lt;/code&gt;&lt;/li&gt;
  711. &lt;/ul&gt;
  712.  
  713. &lt;p&gt;At this point, we will have an Aurora read replica that is receiving replicated data changes off the master.&lt;/p&gt;
  714.  
  715. &lt;h3&gt;Aurora Read Replica Promotion&lt;/h3&gt;
  716.  
  717. &lt;p&gt;To isolate our newly created Aurora read replica so it can be its own master, we&amp;#39;re going to promote the replica. This process allows the instance to receive writes, and be the master for other replicas. We need to do this as otherwise we are limited to only one Aurora read replica off of a PostgreSQL master database. By starting the promotion process the on-going replication is broken between the PostgreSQL and Aurora databases.&lt;/p&gt;
  718.  
  719. &lt;h3&gt;Tweak the Database Parameters&lt;/h3&gt;
  720.  
  721. &lt;p&gt;To enable the ability for PostgreSQL to replicate to Aurora we need to make sure we have certain database parameters set.&lt;/p&gt;
  722.  
  723. &lt;h4&gt;Configure PostgreSQL for Replication&lt;/h4&gt;
  724.  
  725. &lt;p&gt;The first thing you need to do is change the &lt;code&gt;rds.logical_replication&lt;/code&gt; parameter to 1. As described in &lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.PostgreSQL.html#CHAP_Source.PostgreSQL.RDSPostgreSQL&quot;&gt;the documentation&lt;/a&gt; this parameter actually configures additional related parameters that allow the replication to process:&lt;/p&gt;
  726.  
  727. &lt;blockquote&gt;
  728. &lt;p&gt;As part of applying this parameter, AWS DMS sets the &lt;code&gt;wal_level&lt;/code&gt;, &lt;code&gt;max_wal_senders&lt;/code&gt;, &lt;code&gt;max_replication_slots&lt;/code&gt;, and &lt;code&gt;max_connections&lt;/code&gt; parameters.&lt;/p&gt;
  729. &lt;/blockquote&gt;
  730.  
  731. &lt;p&gt;Another parameter configuration that &lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.PostgreSQL.html#CHAP_Source.PostgreSQL.Prerequisites&quot;&gt;Amazon recommends&lt;/a&gt; is to set &lt;code&gt;wal_sender_timeout&lt;/code&gt; to 0.&lt;/p&gt;
  732.  
  733. &lt;blockquote&gt;
  734. &lt;p&gt;The &lt;code&gt;wal_sender_timeout&lt;/code&gt; parameter terminates replication connections that are inactive longer than the specified number of milliseconds. Although the default is 60 seconds, we recommend that you set this parameter to zero, which disables the timeout mechanism.&lt;/p&gt;
  735. &lt;/blockquote&gt;
  736.  
  737. &lt;p&gt;Without setting the timeout parameter, the streaming of Write-Ahead Logging (WAL) files can be terminated if the sender is under load and does not send a WAL file within the timeout period. To prevent any risk of this it is best to disable it during the on-going replication phase of the migration process.&lt;/p&gt;
  738.  
  739. &lt;p&gt;There are additional &lt;code&gt;wal&lt;/code&gt; settings available, but we &lt;em&gt;shouldn&amp;#39;t&lt;/em&gt; need to configure them.&lt;/p&gt;
  740.  
  741. &lt;h4&gt;Configure Aurora for Replication&lt;/h4&gt;
  742.  
  743. &lt;p&gt;On the Aurora instance, we want to limit any foreign key constraints from triggering as the full loads for tables are occurring. Fortunately, if we change the &lt;code&gt;session_replication_role&lt;/code&gt; parameter to &lt;code&gt;replica&lt;/code&gt;, it only keeps &lt;em&gt;replica&lt;/em&gt; related constraint triggers active. This effectively disables foreign key constraints for us.&lt;/p&gt;
  744.  
  745. &lt;h3&gt;Create Replication Instance&lt;/h3&gt;
  746.  
  747. &lt;p&gt;DMS uses a dedicated &lt;em&gt;replication instance&lt;/em&gt; to help facilitate the migration process. In a PostgreSQL to Aurora migration, this machine is responsible for connecting to both the source and target databases and transforming and transferring the data.&lt;/p&gt;
  748.  
  749. &lt;p&gt;The following steps expand on what is needed to set up a replication instance for the migration.&lt;/p&gt;
  750.  
  751. &lt;ul&gt;
  752. &lt;li&gt;Create a replication instance under DMS
  753.  
  754. &lt;ul&gt;
  755. &lt;li&gt;Name it as specific as possible for its purpose (i.e., production-api-replication-instance)&lt;/li&gt;
  756. &lt;li&gt;The &lt;em&gt;Instance Size&lt;/em&gt; might need to be tweaked based on various factors (&lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_ReplicationInstance.html#CHAP_ReplicationInstance.InDepth&quot;&gt;documentation on instance sizes&lt;/a&gt;)&lt;/li&gt;
  757. &lt;li&gt;For example, in our staging we used &lt;code&gt;dms.t2.large&lt;/code&gt;, while for production we used &lt;code&gt;dms.c4.large&lt;/code&gt;&lt;/li&gt;
  758. &lt;li&gt;Set the &lt;em&gt;VPC&lt;/em&gt; to the same as the source and target databases&lt;/li&gt;
  759. &lt;li&gt;Enable &lt;em&gt;Multi-AZ&lt;/em&gt; as it is needed for on-going replication&lt;/li&gt;
  760. &lt;li&gt;The instance doesn’t need to be &lt;em&gt;Publicly Accessible&lt;/em&gt; (as the internal connections are done within the VPC)&lt;/li&gt;
  761. &lt;li&gt;Ensure the &lt;em&gt;VPC Security Group&lt;/em&gt; under &lt;em&gt;Advance&lt;/em&gt; is set to &lt;code&gt;default&lt;/code&gt;&lt;/li&gt;
  762. &lt;li&gt;This allows for connections between the databases and the replication instance&lt;/li&gt;
  763. &lt;/ul&gt;&lt;/li&gt;
  764. &lt;/ul&gt;
  765.  
  766. &lt;h3&gt;Create Source and Target Endpoints&lt;/h3&gt;
  767.  
  768. &lt;p&gt;We need to define the &lt;em&gt;endpoints&lt;/em&gt; of our databases in DMS. One to represent the source database (PostgreSQL) and another to represent the target database (Aurora). The following steps are to be applied to each endpoint.&lt;/p&gt;
  769.  
  770. &lt;ul&gt;
  771. &lt;li&gt;The &lt;em&gt;Server Name&lt;/em&gt; is the DB’s endpoint (i.e., the connection URL)
  772.  
  773. &lt;ul&gt;
  774. &lt;li&gt;You cannot use a read replica as your source (it does not support on-going replication)&lt;/li&gt;
  775. &lt;/ul&gt;&lt;/li&gt;
  776. &lt;li&gt;The &lt;em&gt;User&lt;/em&gt; and &lt;em&gt;Password&lt;/em&gt; are for the master user account of the database
  777.  
  778. &lt;ul&gt;
  779. &lt;li&gt;The account &lt;em&gt;needs&lt;/em&gt; to be the master account. Otherwise, you need to follow the additional instructions listed in the &lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.PostgreSQL.html#CHAP_Source.PostgreSQL.RDSPostgreSQL&quot;&gt;documentation&lt;/a&gt;&lt;/li&gt;
  780. &lt;li&gt;The &lt;em&gt;Password&lt;/em&gt; cannot have certain characters (&lt;code&gt;: ; + %&lt;/code&gt;)&lt;/li&gt;
  781. &lt;/ul&gt;&lt;/li&gt;
  782. &lt;li&gt;Test the endpoint
  783.  
  784. &lt;ul&gt;
  785. &lt;li&gt;After a success ensure you refresh the schema&lt;/li&gt;
  786. &lt;/ul&gt;&lt;/li&gt;
  787. &lt;/ul&gt;
  788.  
  789. &lt;h3&gt;Create Task&lt;/h3&gt;
  790.  
  791. &lt;p&gt;The DMS &lt;em&gt;task&lt;/em&gt; is where we actually configure and use the &lt;em&gt;endpoints&lt;/em&gt; and &lt;em&gt;replication instance&lt;/em&gt; to accomplish the migration. There are a couple of options and considerations to be made while creating a task. The following steps outline how to create a task, and the settings to configure:&lt;/p&gt;
  792.  
  793. &lt;ul&gt;
  794. &lt;li&gt;Use the &lt;em&gt;source and target endpoints&lt;/em&gt; along with the &lt;em&gt;replica instance&lt;/em&gt; that was created for this migration task&lt;/li&gt;
  795. &lt;li&gt;Set the &lt;em&gt;Migration type&lt;/em&gt; to &lt;code&gt;Migrate existing data and replicate ongoing changes&lt;/code&gt;
  796.  
  797. &lt;ul&gt;
  798. &lt;li&gt;This ensures that we are doing the 2 phase approach:&lt;/li&gt;
  799. &lt;li&gt;Doing a full-load of the table&amp;#39;s data&lt;/li&gt;
  800. &lt;li&gt;Doing on-going replication of changes on the source database&lt;/li&gt;
  801. &lt;/ul&gt;&lt;/li&gt;
  802. &lt;li&gt;&lt;em&gt;Target table preparation mode&lt;/em&gt; should be &lt;code&gt;Do nothing&lt;/code&gt;
  803.  
  804. &lt;ul&gt;
  805. &lt;li&gt;The reason here is that we want to preserve the table&amp;#39;s metadata (i.e., indexes, defaults, constraints)&lt;/li&gt;
  806. &lt;li&gt;&lt;code&gt;Truncate&lt;/code&gt; could work, but is likely to fail due to constraints in the database&lt;/li&gt;
  807. &lt;li&gt;Remember that by using &lt;code&gt;Do nothing&lt;/code&gt; the target database needs to be truncated manually prior to running the task&lt;/li&gt;
  808. &lt;/ul&gt;&lt;/li&gt;
  809. &lt;li&gt;&lt;em&gt;Stop task after full load completes&lt;/em&gt; should be set to &lt;code&gt;Don&amp;#39;t stop&lt;/code&gt;.
  810.  
  811. &lt;ul&gt;
  812. &lt;li&gt;This ensures the on-going replication process starts immediately when a table is fully loaded&lt;/li&gt;
  813. &lt;/ul&gt;&lt;/li&gt;
  814. &lt;li&gt;For the table mappings, we want to migrate all tables (i.e., &lt;code&gt;where schema name is like &amp;#39;public&amp;#39; and table name is like &amp;#39;%&amp;#39;, include&lt;/code&gt;)&lt;/li&gt;
  815. &lt;li&gt;It is useful to enable the &lt;em&gt;validation&lt;/em&gt; and &lt;em&gt;logging&lt;/em&gt; to see how things are progressing during the migration&lt;/li&gt;
  816. &lt;/ul&gt;
  817.  
  818. &lt;p&gt;&lt;em&gt;Include LOB columns in replication&lt;/em&gt; is an interesting setting, LOBs are &lt;em&gt;Large Objects&lt;/em&gt; that exist during the migration. As DMS is possibly migrating to a different database type a transformation of data types occurs. To understand what we&amp;#39;re dealing with you need to look up the source database&amp;#39;s supported types in the documentation. For example, using the DMS documentation you can see what &lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.PostgreSQL.html#CHAP_Source.PostgreSQL.DataTypes&quot;&gt;PostgreSQL data types&lt;/a&gt; end up being LOBs.&lt;/p&gt;
  819.  
  820. &lt;p&gt;The following section will dig a little deeper into LOBs and the considerations that need to be considered.&lt;/p&gt;
  821.  
  822. &lt;h4&gt;Full or Limited LOB Mode&lt;/h4&gt;
  823.  
  824. &lt;p&gt;There are two options for handling LOBs during the migration: &lt;em&gt;Full&lt;/em&gt; or &lt;em&gt;Limited&lt;/em&gt; LOB Mode. LOBs are potentially massive objects that reside in the database, and they normally don&amp;#39;t have a fixed size in the column. The following &lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Tasks.LOBSupport.html&quot;&gt;documentation&lt;/a&gt; describes the two options:&lt;/p&gt;
  825.  
  826. &lt;p&gt;DMS provides you with the &lt;em&gt;Full LOB Mode&lt;/em&gt;:&lt;/p&gt;
  827.  
  828. &lt;blockquote&gt;
  829. &lt;p&gt;In full LOB mode, AWS DMS migrates all LOBs from source to target regardless of size. In this configuration, AWS DMS has no information about the maximum size of LOBs to expect. Thus, LOBs are migrated one at a time, piece by piece. Full LOB mode can be quite slow.&lt;/p&gt;
  830. &lt;/blockquote&gt;
  831.  
  832. &lt;p&gt;DMS also provides the &lt;em&gt;Limited LOB Mode&lt;/em&gt;:&lt;/p&gt;
  833.  
  834. &lt;blockquote&gt;
  835. &lt;p&gt;In limited LOB mode, you set a maximum size LOB that AWS DMS should accept. Doing so allows AWS DMS to pre-allocate memory and load the LOB data in bulk. LOBs that exceed the maximum LOB size are truncated and a warning is issued to the log file. In limited LOB mode, you get significant performance gains over full LOB mode. We recommend that you use limited LOB mode whenever possible.&lt;/p&gt;
  836. &lt;/blockquote&gt;
  837.  
  838. &lt;p&gt;Initially, it makes sense to just use &lt;em&gt;Full LOB Mode&lt;/em&gt; as it&amp;#39;ll preserve data by migrating LOBs regardless of size. The big issue with this choice is &lt;em&gt;speed&lt;/em&gt;. In our migration, we achieve full migration in 2 hours using &lt;em&gt;Limited LOB Mode&lt;/em&gt; and we estimated about 90 hours using &lt;em&gt;Full LOB Mode&lt;/em&gt;. The issue with using &lt;em&gt;Limited LOB Mode&lt;/em&gt; is that you can essentially lose data if the LOB&amp;#39;s size is less than the &lt;code&gt;Max LOB Size&lt;/code&gt; parameter.&lt;/p&gt;
  839.  
  840. &lt;p&gt;To work around this concern, the plan is to determine the max LOB size in the database and set the &lt;code&gt;Max LOB Size&lt;/code&gt; to slightly bigger than that value (i.e., multiply it by 2). By using a larger &lt;code&gt;Max LOB Size&lt;/code&gt; than the largest LOB we have in the database, we are ensured to have the full data migrated without any data loss.&lt;/p&gt;
  841.  
  842. &lt;p&gt;A manual way to check for the size of a LOB column is to use the following query: &lt;code&gt;SELECT max(pg_column_size(column_name)) FROM table_name;&lt;/code&gt;. This will return the max number of bytes used in that column. This is the value you&amp;#39;ll want to be larger than. The following is a Rails rake task that walks through every table&amp;#39;s column and identifies the max sizes for any LOB column (for PostgreSQL):&lt;/p&gt;
  843. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:scripts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  844. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Print out the max size of LOBs in the database. Usage: bin/rake scripts:max_lob_size&amp;#39;&lt;/span&gt;
  845. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;max_lob_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:environment&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  846. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  847.  
  848. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;LOB_TYPES&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%w(&lt;/span&gt;
  849. &lt;span class=&quot;sx&quot;&gt;      hstore&lt;/span&gt;
  850. &lt;span class=&quot;sx&quot;&gt;      composite&lt;/span&gt;
  851. &lt;span class=&quot;sx&quot;&gt;      array&lt;/span&gt;
  852. &lt;span class=&quot;sx&quot;&gt;      jsonb&lt;/span&gt;
  853. &lt;span class=&quot;sx&quot;&gt;      json&lt;/span&gt;
  854. &lt;span class=&quot;sx&quot;&gt;      polygon&lt;/span&gt;
  855. &lt;span class=&quot;sx&quot;&gt;      path&lt;/span&gt;
  856. &lt;span class=&quot;sx&quot;&gt;      xml&lt;/span&gt;
  857. &lt;span class=&quot;sx&quot;&gt;      tsquery&lt;/span&gt;
  858. &lt;span class=&quot;sx&quot;&gt;      tsvector&lt;/span&gt;
  859. &lt;span class=&quot;sx&quot;&gt;      bytea&lt;/span&gt;
  860. &lt;span class=&quot;sx&quot;&gt;      text&lt;/span&gt;
  861. &lt;span class=&quot;sx&quot;&gt;    )&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;freeze&lt;/span&gt;
  862.  
  863. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  864. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;schema_migrations&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starts_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;awsdms_&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  865.  
  866. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns_sql&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;HEREDOC&lt;/span&gt;
  867. &lt;span class=&quot;sh&quot;&gt;        SELECT *&lt;/span&gt;
  868. &lt;span class=&quot;sh&quot;&gt;        FROM information_schema.columns&lt;/span&gt;
  869. &lt;span class=&quot;sh&quot;&gt;        WHERE table_schema = &amp;#39;public&amp;#39;&lt;/span&gt;
  870. &lt;span class=&quot;sh&quot;&gt;          AND table_name   = &amp;#39;#{table_name}&amp;#39;&lt;/span&gt;
  871. &lt;span class=&quot;dl&quot;&gt;      HEREDOC&lt;/span&gt;
  872.  
  873. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns_results&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  874.  
  875. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns_results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  876. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;column_name&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  877. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;data_type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  878.  
  879. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;LOB_TYPES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  880. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_sql&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;HEREDOC&lt;/span&gt;
  881. &lt;span class=&quot;sh&quot;&gt;            SELECT max(pg_column_size(#{column_name})) FROM #{table_name};&lt;/span&gt;
  882. &lt;span class=&quot;dl&quot;&gt;          HEREDOC&lt;/span&gt;
  883. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_results&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  884.  
  885. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_hash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  886. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_hash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  887.  
  888. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:column_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_type&lt;/span&gt;
  889. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:max_bytes_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size_results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;max&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_i&lt;/span&gt;
  890. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  891.  
  892. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;
  893. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  894. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  895.  
  896. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Raw Dump&amp;quot;&lt;/span&gt;
  897. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;
  898.  
  899. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_bytes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:max_bytes_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;
  900. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Max bytes found in a LOB column is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_bytes&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  901. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  902. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  903. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  904. &lt;h4&gt;LOB Nullability Constraints&lt;/h4&gt;
  905.  
  906. &lt;p&gt;There are some concerns with &lt;code&gt;NOT NULL&lt;/code&gt; database constraints on columns that become LOBs during the migration. DMS has a certain process for dealing with tables with LOBs:&lt;/p&gt;
  907.  
  908. &lt;ul&gt;
  909. &lt;li&gt;The data for each column in a row is added, except for LOB columns&lt;/li&gt;
  910. &lt;li&gt;LOB columns are left with a &lt;code&gt;null&lt;/code&gt; placeholder&lt;/li&gt;
  911. &lt;li&gt;Afterwards, LOB data replaces the &lt;code&gt;null&lt;/code&gt; placeholders&lt;/li&gt;
  912. &lt;/ul&gt;
  913.  
  914. &lt;p&gt;This two-step process of dealing with LOB data types is where we have to be concerned with &lt;code&gt;NOT NULL&lt;/code&gt; constraints. The migration will fail if any LOB columns have a &lt;code&gt;NOT NULL&lt;/code&gt; constraint. In PostgreSQL, a common case of LOBs is &lt;code&gt;jsonb&lt;/code&gt; and &lt;code&gt;array&lt;/code&gt; columns. So we have to &lt;em&gt;remove&lt;/em&gt; these constraints on the target database just until the full table load phase is done. For example, the following statement would do the trick, &lt;code&gt;ALTER TABLE my_table ALTER COLUMN lob_column DROP NOT NULL;&lt;/code&gt;. Do not forget to add the &lt;code&gt;NOT NULL&lt;/code&gt; constraints back after the full table load phase is done.&lt;/p&gt;
  915.  
  916. &lt;h3&gt;Migration Time&lt;/h3&gt;
  917.  
  918. &lt;p&gt;At this point, the migration process is pretty much ready! Let&amp;#39;s break the process up into before/during/after migration.&lt;/p&gt;
  919.  
  920. &lt;h4&gt;Before Migration&lt;/h4&gt;
  921.  
  922. &lt;p&gt;Make sure that the:&lt;/p&gt;
  923.  
  924. &lt;ul&gt;
  925. &lt;li&gt;The DMS endpoints, task and replication instance are present and configured&lt;/li&gt;
  926. &lt;li&gt;The Aurora instance is ready:
  927.  
  928. &lt;ul&gt;
  929. &lt;li&gt;Truncated data&lt;/li&gt;
  930. &lt;li&gt;Disabled LOB nullability constraints&lt;/li&gt;
  931. &lt;/ul&gt;&lt;/li&gt;
  932. &lt;/ul&gt;
  933.  
  934. &lt;p&gt;One thing that is suggested is to run &lt;code&gt;Assess&lt;/code&gt; on the task so you can get a report of potential issues. In our case, there were a couple of &lt;em&gt;Partially supported datatypes : float8&lt;/em&gt; on a few columns. This ended up changing the rounding of floats (i.e., 1.4999999998 --&amp;gt; 1.5). It is worth noting that these differences occur after the migration process is completed and changes are being done on the new database type.&lt;/p&gt;
  935.  
  936. &lt;p&gt;For the migration, ideally, it is done during a &lt;em&gt;low&lt;/em&gt; activity period. In addition, if possible stop any background jobs just before the migration, and wait for the current jobs to finish. The jobs can resume processing after the migration, this is to reduce risk.&lt;/p&gt;
  937.  
  938. &lt;h4&gt;During Migration&lt;/h4&gt;
  939.  
  940. &lt;ul&gt;
  941. &lt;li&gt;Keep an eye on the source database&amp;#39;s health
  942.  
  943. &lt;ul&gt;
  944. &lt;li&gt;There will be additional load placed on it during the migration&lt;/li&gt;
  945. &lt;li&gt;If needed you could always lower the number of tables loaded in parallel during the &lt;em&gt;full load&lt;/em&gt; phase (under the advanced settings in the &lt;em&gt;Task&lt;/em&gt;)&lt;/li&gt;
  946. &lt;/ul&gt;&lt;/li&gt;
  947. &lt;li&gt;Monitor the task&amp;#39;s &lt;em&gt;Table Statistics&lt;/em&gt; tab to make sure tables are progressing well&lt;/li&gt;
  948. &lt;li&gt;Monitor the task&amp;#39;s &lt;em&gt;Task Monitoring&lt;/em&gt; tab to make sure the on-going replication is keeping up
  949.  
  950. &lt;ul&gt;
  951. &lt;li&gt;The &lt;code&gt;CDCIncomingChanges&lt;/code&gt; should be as close to 0 as possible&lt;/li&gt;
  952. &lt;li&gt;During the &lt;em&gt;full load&lt;/em&gt; phase, the &lt;code&gt;CDCIncomingChanges&lt;/code&gt; will climb as the on-going replication changes are stored until the tables have fully loaded into the target database&lt;/li&gt;
  953. &lt;/ul&gt;&lt;/li&gt;
  954. &lt;li&gt;Monitor the replication instance&amp;#39;s &lt;em&gt;Monitoring&lt;/em&gt; tab to make sure the &lt;code&gt;FreeStorageSpace&lt;/code&gt; and &lt;code&gt;FreeableMemory&lt;/code&gt; are not dropping too low
  955.  
  956. &lt;ul&gt;
  957. &lt;li&gt;If any of these are too low then the migration can fail&lt;/li&gt;
  958. &lt;/ul&gt;&lt;/li&gt;
  959. &lt;/ul&gt;
  960.  
  961. &lt;p&gt;The first phase is &lt;em&gt;full table load&lt;/em&gt;, where all the source data is dumped into the target. Be aware that large tables can take some time. After a table has been fully loaded into the target, the on-going replication phase starts for that table.&lt;/p&gt;
  962.  
  963. &lt;p&gt;The &lt;em&gt;on-going replication&lt;/em&gt; phase is where &lt;em&gt;inserts/deletes/updates&lt;/em&gt; are replicated from the source to the target. After all the tables are in this phase (all tables in a &lt;em&gt;Table Completed&lt;/em&gt; state), it is now safe to re-enable the LOB nullability constraints that were disabled earlier.&lt;/p&gt;
  964.  
  965. &lt;h5&gt;Validations&lt;/h5&gt;
  966.  
  967. &lt;p&gt;If you have validations enabled for the &lt;em&gt;Task&lt;/em&gt; then the &lt;em&gt;validation&lt;/em&gt; columns in the &lt;em&gt;Table Statistics&lt;/em&gt; will update during the migration. These validations put additional load on the source and target database as the row data is compared after that row has been migrated. It is an on-going validation process.&lt;/p&gt;
  968.  
  969. &lt;p&gt;Personally, I found the validation to be very flaky. It is either &lt;em&gt;really slow&lt;/em&gt; and it also reports validation errors that are not actual errors. In our case, we didn&amp;#39;t pay much attention to the validation failures as spot checking proved that the data was &lt;em&gt;fine&lt;/em&gt;. There were &lt;em&gt;minor&lt;/em&gt; cases where time columns were &lt;em&gt;slightly off&lt;/em&gt;. I am unsure how the validation actually works (i.e., when it does the checks), as the on-going replication could be lagging behind. According to the &lt;a href=&quot;https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Validating.html#CHAP_Validating.Limitations&quot;&gt;documentation&lt;/a&gt;, there are some limitations that might prevent it from being that useful:&lt;/p&gt;
  970.  
  971. &lt;ul&gt;
  972. &lt;li&gt;If one or more rows are being continuously modified during the validation, then AWS DMS can&amp;#39;t validate those rows. However, you can validate those &lt;em&gt;busy&lt;/em&gt; rows manually, after the task completes.&lt;/li&gt;
  973. &lt;li&gt;If AWS DMS detects more than 10,000 failed or suspended records, it will stop the validation. Before you proceed further, resolve any underlying problems with the data.&lt;/li&gt;
  974. &lt;/ul&gt;
  975.  
  976. &lt;p&gt;In either case under the target&amp;#39;s database, there is a new table called &lt;code&gt;awsdms_validation_failures_v1&lt;/code&gt; that contains information on the failures. The &lt;code&gt;KEY&lt;/code&gt; and &lt;code&gt;TABLE_NAME&lt;/code&gt; columns can be used to identify the record in question. It is then possible to check the source and target record and see if there are any issues. One other problem we had with spot-checking validation is that our PostgreSQL database used &lt;code&gt;UUID&lt;/code&gt;s for primary keys, this resulted in the &lt;code&gt;KEY&lt;/code&gt; column having truncated data on the &lt;code&gt;UUID&lt;/code&gt;.&lt;/p&gt;
  977.  
  978. &lt;h4&gt;After Migration&lt;/h4&gt;
  979.  
  980. &lt;p&gt;Hopefully, the migration went successfully, and both the source and target database are in-sync. At this point, the zero-downtime migration can occur -- simply point the application at the new database. It is advisable to wait for any replication lag or queued up &lt;code&gt;CDCIncomingChanges&lt;/code&gt; to drain before proceeding. You might have to wait a little bit for the connections of your application to cycle over to the new database, but you can monitor this in CloudWatch, or force the cycle (i.e., restart Rails Unicorn servers). Don&amp;#39;t forget to resume any background queues. After sufficient time, you can decommission the old database resources.&lt;/p&gt;
  981.  
  982. &lt;p&gt;In the event that the migration doesn&amp;#39;t go as planned, it is possible to revert back to the old database. The only issue is that any write data that occurred during the migration process (i.e., only on the Aurora database) wouldn&amp;#39;t be present on the old database (i.e., PostgreSQL database). This is simply an issue that cannot be worked around, and hopefully, the low activity period reduces the amount of data loss.&lt;/p&gt;
  983. </description>
  984.    </item>
  985.    
  986.    
  987.    
  988.    <item>
  989.      <title>Make the most of your Chat Channels</title>
  990.      <link>https://techblog.thescore.com/2017/06/30/make-the-most-of-your-chat-channels/</link>
  991.      <pubDate>Fri, 30 Jun 2017 00:00:00 +0000</pubDate>
  992.      <author></author>
  993.      <guid>https://techblog.thescore.com/2017/06/30/make-the-most-of-your-chat-channels</guid>
  994.      <description>&lt;center&gt;
  995. ![](/assets/communication-tower.jpg)
  996. _[Communication Tower](https://flic.kr/p/finMno) by [Cocoy Pusung](https://www.flickr.com/photos/95876508@N02/), on Flickr_
  997. &lt;/center&gt;
  998.  
  999. &lt;p&gt;Organizations tend to use some instant messaging platform in addition to email. Ideally, the chosen platform permeates throughout the ranks of the organization instead of having fractured communication between members. The real-time and group collaborative nature of these messaging platforms are quite appealing.&lt;/p&gt;
  1000.  
  1001. &lt;p&gt;I have personally used a couple of messaging platform so far (i.e., Hipchat, Gitter, Discord, Slack). Overall, they all offer the concept of channels. Channels are simply persisted chat &lt;em&gt;rooms&lt;/em&gt; in which members can communicate asynchronously. Topics and names of channels are left up to the members who use them.&lt;/p&gt;
  1002.  
  1003. &lt;p&gt;My goal is to outline some steps to better foster an effective use of channels in your messaging platform of choice. I am currently using Slack at theScore, and so the context of this post will be within Slack.&lt;/p&gt;
  1004.  
  1005. &lt;h1&gt;The Cleanse&lt;/h1&gt;
  1006.  
  1007. &lt;blockquote&gt;
  1008. &lt;p&gt;Macaitis recommends that if no one has used a channel for sixty days, it’s best to archive it. -- &lt;a href=&quot;https://expand.openviewpartners.com/former-slack-cmo-bill-macaitis-on-how-slack-uses-slack-868ffb495b71&quot;&gt;Former Slack CMO, Bill Macaitis, on How Slack Uses Slack&lt;/a&gt;&lt;/p&gt;
  1009. &lt;/blockquote&gt;
  1010.  
  1011. &lt;p&gt;An organization that uses chat channels for communication will accrue an abundance of such. Some of these channels are used more frequently than others. To keep things slightly under control, you should routinely clean the channels. As to &lt;em&gt;who&lt;/em&gt; should be doing this, I would say everyone should try to keep their communication platform clean. There are a few benefits to this:&lt;/p&gt;
  1012.  
  1013. &lt;ol&gt;
  1014. &lt;li&gt;Keeps a more narrow focus within the organization&lt;/li&gt;
  1015. &lt;li&gt;Easier for users to keep a handle on their channels&lt;/li&gt;
  1016. &lt;li&gt;Newcomers feel less overwhelmed at the number of channels&lt;/li&gt;
  1017. &lt;li&gt;Less ambiguity on where conversations/information should be put&lt;/li&gt;
  1018. &lt;/ol&gt;
  1019.  
  1020. &lt;p&gt;Archiving channels doesn&amp;#39;t have to be perfect, although there will be clear choices. If the need of the channel arises again, it can be recreated/unarchived as needed.&lt;/p&gt;
  1021.  
  1022. &lt;h1&gt;Types of Channels&lt;/h1&gt;
  1023.  
  1024. &lt;p&gt;Channels tend to fall under specific categories that define their behaviour or purpose. For example, &lt;a href=&quot;https://slack.global.ssl.fastly.net/5ccb/pdfs/admins_guide.pdf&quot;&gt;Slack provides a guideline&lt;/a&gt; on what these channel types are:&lt;/p&gt;
  1025.  
  1026. &lt;h2&gt;Global&lt;/h2&gt;
  1027.  
  1028. &lt;blockquote&gt;
  1029. &lt;p&gt;i.e., #general, #announcements, #everyone&lt;/p&gt;
  1030. &lt;/blockquote&gt;
  1031.  
  1032. &lt;p&gt;A global channel is one that &lt;em&gt;everyone&lt;/em&gt; is apart of. Normally this would be a general channel, however productivity can be limited depending on the number of members -- it&amp;#39;s more of a social channel. A common use case for using a global channel is for organizational announcements.&lt;/p&gt;
  1033.  
  1034. &lt;h2&gt;Location&lt;/h2&gt;
  1035.  
  1036. &lt;blockquote&gt;
  1037. &lt;p&gt;i.e., #toronto, #ontario, #canada&lt;/p&gt;
  1038. &lt;/blockquote&gt;
  1039.  
  1040. &lt;p&gt;An organization might be spread across many locations. These channels offer a way to group conversations that pertain to specific locations. How to name these channels depends on how your organization is structured geographically -- it might be based on cities, regions, or even offices within a city.&lt;/p&gt;
  1041.  
  1042. &lt;h2&gt;Team&lt;/h2&gt;
  1043.  
  1044. &lt;blockquote&gt;
  1045. &lt;p&gt;i.e., #engineering, #engineering-ios, #sales, #designers&lt;/p&gt;
  1046. &lt;/blockquote&gt;
  1047.  
  1048. &lt;p&gt;These channels are rather important in facilitating communication within teams. Realistically you would have high-level groups such as &lt;em&gt;#engineering&lt;/em&gt; that all your engineers are apart of. It would also be ideal to create &lt;em&gt;sub-teams&lt;/em&gt; to accommodate specializations such as &lt;em&gt;#engineering-android&lt;/em&gt; and &lt;em&gt;#engineering-web&lt;/em&gt;. By following this naming convention for teams and sub-teams, the channels are &lt;em&gt;grouped&lt;/em&gt; and sorted through the naming convention.&lt;/p&gt;
  1049.  
  1050. &lt;h2&gt;Project&lt;/h2&gt;
  1051.  
  1052. &lt;blockquote&gt;
  1053. &lt;p&gt;i.e., #sports-app, #chat-bot, #squadup&lt;/p&gt;
  1054. &lt;/blockquote&gt;
  1055.  
  1056. &lt;p&gt;Often projects are underway. To help communication within that project, which might include individuals across different teams, a project channel is useful. As projects come and go, it might be worth archiving channels for projects that are no longer current.&lt;/p&gt;
  1057.  
  1058. &lt;h2&gt;Topical&lt;/h2&gt;
  1059.  
  1060. &lt;blockquote&gt;
  1061. &lt;p&gt;i.e., #soccer, #javascript, #anime&lt;/p&gt;
  1062. &lt;/blockquote&gt;
  1063.  
  1064. &lt;p&gt;To me, these are the interesting channels! The previously mentioned channels types were more geared towards business communication, and generally are easily formed around the business needs. Topical channels could be on anything that interests a group of people. Generally, these channels are organically formed within an organization.&lt;/p&gt;
  1065.  
  1066. &lt;h2&gt;Temporary&lt;/h2&gt;
  1067.  
  1068. &lt;blockquote&gt;
  1069. &lt;p&gt;i.e., #xmas-party, #offsite-retreat, #brainstorm-session&lt;/p&gt;
  1070. &lt;/blockquote&gt;
  1071.  
  1072. &lt;p&gt;These channels are short-lived and generally used for time-sensitive events. They have a specific purpose, and rarely offer much value after that purpose is completed. These channels could be deleted or archived when they are no longer needed. Channels are &lt;em&gt;cheap&lt;/em&gt; to create, and so people shouldn&amp;#39;t be worried about making one-off channels to help them accomplish specific tasks.&lt;/p&gt;
  1073.  
  1074. &lt;h1&gt;Organizing Channels&lt;/h1&gt;
  1075.  
  1076. &lt;p&gt;As we saw there are many categories of channels. To help with the organization of the multiple channels, a naming convention could be used.&lt;/p&gt;
  1077.  
  1078. &lt;p&gt;One suggestion is to prefix all team channels with &lt;code&gt;team-&lt;/code&gt; (i.e., &lt;em&gt;#team-engineering&lt;/em&gt;). The same could be done with project channels using &lt;code&gt;project-&lt;/code&gt; (i.e., &lt;em&gt;#project-esports&lt;/em&gt;). The actual prefix doesn&amp;#39;t matter as long as it is unique (enough) and consistent. A benefit is that channels are ordered alphanumerically, making it easier to browse active teams/projects. If you wanted, you could even take the same approach with topics (i.e., &lt;em&gt;#topic-soccer&lt;/em&gt;).&lt;/p&gt;
  1079.  
  1080. &lt;p&gt;Another idea is to have a &lt;em&gt;#meta&lt;/em&gt; channel where you can talk about improving the use of the platform itself. For example, new topic channels can be posted there, same with renames and such. Ensure everyone is a part of the channel so information travels. If there is support for it, you could also pin/sticky some guidelines on the platform (i.e., link to this article).&lt;/p&gt;
  1081.  
  1082. &lt;h1&gt;Topical Channels (at theScore)&lt;/h1&gt;
  1083.  
  1084. &lt;p&gt;As previously mentioned, topical channels are the interesting channels that I want to touch on more here. I&amp;#39;ll just say that this is coming from an software organization&amp;#39;s perspective, as I am a part of theScore engineering team.&lt;/p&gt;
  1085.  
  1086. &lt;p&gt;Here are a few of our topical channels:&lt;/p&gt;
  1087.  
  1088. &lt;ul&gt;
  1089. &lt;li&gt;&lt;em&gt;#ping-pong&lt;/em&gt;&lt;/li&gt;
  1090. &lt;li&gt;&lt;em&gt;#overwatch&lt;/em&gt;&lt;/li&gt;
  1091. &lt;li&gt;&lt;em&gt;#pokemon-go&lt;/em&gt;&lt;/li&gt;
  1092. &lt;li&gt;&lt;em&gt;#podcasts&lt;/em&gt;&lt;/li&gt;
  1093. &lt;li&gt;&lt;em&gt;#board-games&lt;/em&gt;&lt;/li&gt;
  1094. &lt;li&gt;&lt;em&gt;#food&lt;/em&gt;&lt;/li&gt;
  1095. &lt;/ul&gt;
  1096.  
  1097. &lt;p&gt;While these channels do provide topical information on their specific topics, we didn&amp;#39;t really have engineering topic channels.&lt;/p&gt;
  1098.  
  1099. &lt;h2&gt;Birth of Engineering Topical Channels&lt;/h2&gt;
  1100.  
  1101. &lt;p&gt;We were doing some recent work with &lt;a href=&quot;https://facebook.github.io/react/&quot;&gt;React&lt;/a&gt; and &lt;a href=&quot;http://graphql.org/&quot;&gt;GraphQL&lt;/a&gt;, and we saw the birth of two engineering topical channels:&lt;/p&gt;
  1102.  
  1103. &lt;ul&gt;
  1104. &lt;li&gt;&lt;em&gt;#graphql&lt;/em&gt;&lt;/li&gt;
  1105. &lt;li&gt;&lt;em&gt;#react&lt;/em&gt;&lt;/li&gt;
  1106. &lt;/ul&gt;
  1107.  
  1108. &lt;p&gt;Before this work, we did not &lt;em&gt;really&lt;/em&gt; have engineering topical channels. This caused slight inefficiencies in the flow of knowledge through our engineering team. We made these new channels known at the engineering level, and also invited people who are actively working in the areas. This approach injected people who cared about the topic into the channel. As we go about our day and we find something interesting related to one of these topics, it becomes easy to drop that information into the channel. In addition, more specific questions can be asked in these channels as their topic is fairly narrow.&lt;/p&gt;
  1109.  
  1110. &lt;p&gt;Eventually, you end up seeing people who are not &lt;em&gt;directly&lt;/em&gt; working in that topic area, but are still in the channel. To me this suggests people have an interest.&lt;/p&gt;
  1111.  
  1112. &lt;h2&gt;Educational Value of Topical Communication&lt;/h2&gt;
  1113.  
  1114. &lt;p&gt;These specific topical channels on programming languages/frameworks/concepts are extremely beneficial to individuals, and the organization.&lt;/p&gt;
  1115.  
  1116. &lt;ul&gt;
  1117. &lt;li&gt;Provides a place for focused conversations to take place. For example, if someone had a general GraphQL question where do you go to ask it? Possibly in one our engineering channels, or maybe a specific team that is using it? In this case, having a dedicated topical channel for &lt;em&gt;#graphql&lt;/em&gt; would be beneficial.&lt;/li&gt;
  1118. &lt;li&gt;Promotes people to widen their interests, as they can simply join a channel and &lt;em&gt;slowly&lt;/em&gt; absorb information. For example, if I was interested in &lt;em&gt;#machine-learning&lt;/em&gt;, I could join the channel and occasionally I&amp;#39;ll see people post links to articles, conference talks, and just general conversation on the topic.&lt;/li&gt;
  1119. &lt;/ul&gt;
  1120.  
  1121. &lt;p&gt;There is great educational value in these channels, especially in larger organizations where there are many teams and projects. These channels offer a place to share findings and communication to prevent knowledge silos.&lt;/p&gt;
  1122.  
  1123. &lt;h2&gt;Effective Slack Plan&lt;/h2&gt;
  1124.  
  1125. &lt;p&gt;Now at theScore, this is our plan in improving our usage of Slack channels:&lt;/p&gt;
  1126.  
  1127. &lt;ol&gt;
  1128. &lt;li&gt;Archive channels with no activity in the last 60 days&lt;/li&gt;
  1129. &lt;li&gt;Rename team channels with the &lt;code&gt;team-&lt;/code&gt; prefix&lt;/li&gt;
  1130. &lt;li&gt;Rename project channels with the &lt;code&gt;proj-&lt;/code&gt; prefix&lt;/li&gt;
  1131. &lt;li&gt;Create a 2-level hierarchy for teams (i.e., &lt;em&gt;#eng&lt;/em&gt;, #eng-ios)&lt;/li&gt;
  1132. &lt;li&gt;Create engineering topical channels (i.e., &lt;em&gt;#rails&lt;/em&gt;, &lt;em&gt;#android&lt;/em&gt;, &lt;em&gt;#swift&lt;/em&gt;)&lt;/li&gt;
  1133. &lt;li&gt;Create a &lt;em&gt;#meta&lt;/em&gt; channel (along with a note to some channel conventions)&lt;/li&gt;
  1134. &lt;/ol&gt;
  1135.  
  1136. &lt;p&gt;With routine maintenance, we will keep our Slack channels focused and organized. Hopefully we will see the benefit to education/knowledge sharing with the increased organization of our chat channels.&lt;/p&gt;
  1137. </description>
  1138.    </item>
  1139.    
  1140.    
  1141.    
  1142.    <item>
  1143.      <title>Cut Code Review Noise on GitHub</title>
  1144.      <link>https://techblog.thescore.com/2017/03/28/cut-code-review-noise-on-github/</link>
  1145.      <pubDate>Tue, 28 Mar 2017 00:00:00 +0000</pubDate>
  1146.      <author></author>
  1147.      <guid>https://techblog.thescore.com/2017/03/28/cut-code-review-noise-on-github</guid>
  1148.      <description>&lt;center&gt;
  1149. ![](/assets/scissors.jpg)
  1150. _[Painting scissors with light 4 by Zechariah  Judy, on Flickr](https://www.flickr.com/photos/9918311@N02/4268582634/in/photostream/)_
  1151. &lt;/center&gt;
  1152.  
  1153. &lt;p&gt;For this post I&amp;#39;m not going to detail my preferred approach for working through a source code change using GitHub&amp;#39;s pull requests -- another post will likely contain this. I&amp;#39;m instead going to touch on GitHub&amp;#39;s review requests feature, and present an effective and efficient way to handle code reviews with GitHub&amp;#39;s interface.&lt;/p&gt;
  1154.  
  1155. &lt;h2&gt;GitHub Review Requests and Reactions&lt;/h2&gt;
  1156.  
  1157. &lt;p&gt;GitHub, in late 2016, introduced &lt;a href=&quot;https://github.com/blog/2291-introducing-review-requests&quot;&gt;review requests&lt;/a&gt; to their platform. This was a welcome addition to code reviewing pull requests. It exposed a mechanism to request reviews without using &lt;em&gt;@mention&lt;/em&gt; or chat to get people&amp;#39;s attention.&lt;/p&gt;
  1158.  
  1159. &lt;p&gt;GitHub made it easier to identify pull requests that require your attention by &lt;a href=&quot;https://github.com/blog/2306-filter-pull-request-reviews-and-review-requests&quot;&gt;filtering review requests&lt;/a&gt;. Additionally, GitHub also allowed review requests to integrate with their &lt;a href=&quot;https://github.com/blog/2051-protected-branches-and-required-status-checks&quot;&gt;protected branches&lt;/a&gt; feature. It became possible to prevent merging a pull request until at least one reviewer approved that pull request.&lt;/p&gt;
  1160.  
  1161. &lt;p&gt;Last year GitHub released &lt;a href=&quot;https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments&quot;&gt;reactions&lt;/a&gt; to simply reduce noise in large issues and pull requests. With this now you can simply add an emoji reaction instead of a new comment.&lt;/p&gt;
  1162.  
  1163. &lt;p&gt;Overall, these are all incremental steps in the right direction towards the goal of a collaborative environment within GitHub. More and more, I find myself using review requests to communicate with other developers. Requesting a review now sends the appropriate notification to the individual. In the past, I would have pasted a link to the pull request in our chat room, or made an &lt;em&gt;@mention&lt;/em&gt; comment. By moving away from the old style of getting reviews, I find there is less &lt;em&gt;chatter&lt;/em&gt; and &lt;em&gt;noise&lt;/em&gt; for coordinating code reviews.&lt;/p&gt;
  1164.  
  1165. &lt;h2&gt;Committed Reviewers&lt;/h2&gt;
  1166.  
  1167. &lt;p&gt;I&amp;#39;m going to introduce the concept of &lt;em&gt;committed reviewers&lt;/em&gt;:&lt;/p&gt;
  1168.  
  1169. &lt;blockquote&gt;
  1170. &lt;p&gt;A &lt;em&gt;reviewer&lt;/em&gt; who is &lt;em&gt;committed&lt;/em&gt; to the overall quality and correctness of the pull request.&lt;/p&gt;
  1171. &lt;/blockquote&gt;
  1172.  
  1173. &lt;p&gt;This implies that committed reviewers are individuals who give the final &lt;em&gt;approval&lt;/em&gt; for the pull request before merging.&lt;/p&gt;
  1174.  
  1175. &lt;p&gt;This concept might not be applicable for various projects or organizations. I do recommend it, however, as it tends to promote higher quality code ending up in your codebase.&lt;/p&gt;
  1176.  
  1177. &lt;p&gt;With GitHub&amp;#39;s request reviewers feature, this means that if you are requested, you are now a committed reviewer. The pull request cannot be merged until all committed reviewers approve it. This prevents a scenario where a reviewer could still be working through a pull request when it&amp;#39;s merged, wasting time and potentially missing issues. I have seen this happen when authors request numerous reviewers just to expedite the process.&lt;/p&gt;
  1178.  
  1179. &lt;h2&gt;Coordinating Code Reviews&lt;/h2&gt;
  1180.  
  1181. &lt;p&gt;Imagine we&amp;#39;re on a team of 4 people. You just created a pull request in GitHub. Now you want some eyes on the new changes you are proposing to put into the codebase.&lt;/p&gt;
  1182.  
  1183. &lt;p&gt;You recall the old ways of using an &lt;em&gt;@mention&lt;/em&gt; just to get reviewers, same with pasting the link in our chat. You don&amp;#39;t want to disturb the team with unnecessary noise. Instead, it is time to use the new request reviewers interface in GitHub.&lt;/p&gt;
  1184.  
  1185. &lt;center&gt;
  1186. ![](/assets/reviewers-state-1.png)
  1187. &lt;/center&gt;
  1188.  
  1189. &lt;p&gt;You decide to directly request reviews from Jane and Bob, as you know they are familiar with this part of the system. You decided to request only two reviews as we have an informal rule of requiring two approvals before merging in any pull request.&lt;/p&gt;
  1190.  
  1191. &lt;center&gt;
  1192. ![](/assets/reviewers-state-2.png)
  1193. &lt;/center&gt;
  1194.  
  1195. &lt;p&gt;Bob leaves a request for changes inquiring on one aspect of your code. In one of his comments he indicates that Mary had encountered a similar problem and her solution was slightly different from yours.&lt;/p&gt;
  1196.  
  1197. &lt;center&gt;
  1198. ![](/assets/reviewers-state-3.png)
  1199. &lt;/center&gt;
  1200.  
  1201. &lt;p&gt;You read over the requested changes from Bob and make the corrections.&lt;/p&gt;
  1202.  
  1203. &lt;p&gt;Mary chimes in regarding that comment and leaves some insight there for you. You read it over and leave a GitHub Reaction to express your thanks, which indicates that you acknowledged her comment.&lt;/p&gt;
  1204.  
  1205. &lt;center&gt;
  1206. ![](/assets/reviewers-state-4.png)
  1207. &lt;/center&gt;
  1208.  
  1209. &lt;p&gt;You now need to signal to Bob that you addressed his concerns so he can look at the new changes you added. Instead of getting his attention via chat or a &lt;em&gt;@mention&lt;/em&gt; comment, you can remove and request him again for a review.&lt;/p&gt;
  1210.  
  1211. &lt;blockquote&gt;
  1212. &lt;p&gt;Unfortunately, GitHub does not provide a one button click to request a review again from someone who is already a reviewer.&lt;/p&gt;
  1213. &lt;/blockquote&gt;
  1214.  
  1215. &lt;p&gt;Bob receives a notification that he has been requested for a review and looks over the changes, finally approving it.&lt;/p&gt;
  1216.  
  1217. &lt;p&gt;Jane gets back and also approves your pull request.&lt;/p&gt;
  1218.  
  1219. &lt;center&gt;
  1220. ![](/assets/reviewers-state-5.png)
  1221. &lt;/center&gt;
  1222.  
  1223. &lt;p&gt;At this specific point you have the two approvals that we as a team decided is required before merging a pull requests. Mary&amp;#39;s in a &lt;em&gt;comment&lt;/em&gt; state, which is fine as it indicates that she is not a &lt;em&gt;committed reviewer&lt;/em&gt;.&lt;/p&gt;
  1224.  
  1225. &lt;p&gt;You finally get around to merging in your pull request when you notice that Mary has added herself as a reviewer. She decided to give your pull request a complete review now as a committed reviewer. The intent was communicated through the request review.&lt;/p&gt;
  1226.  
  1227. &lt;center&gt;
  1228. ![](/assets/reviewers-state-6.png)
  1229. &lt;/center&gt;
  1230.  
  1231. &lt;p&gt;Even though you got two approvals, you know that Mary is committed to reviewing your pull requests. At this point you wait for her results as to not waste her time, or to potentially miss any issues she might raise.&lt;/p&gt;
  1232.  
  1233. &lt;center&gt;
  1234. ![](/assets/reviewers-state-7.png)
  1235. &lt;/center&gt;
  1236.  
  1237. &lt;p&gt;Everything looks great. You have approvals across the board, so hit that merge button!&lt;/p&gt;
  1238.  
  1239. &lt;h3&gt;Less Noise and Wasted Time&lt;/h3&gt;
  1240.  
  1241. &lt;p&gt;A couple of things you might have noticed as we ran through that code review scenario:&lt;/p&gt;
  1242.  
  1243. &lt;p&gt;No unnecessary commenting on the pull request to indicate that an individual has acknowledged something. GitHub Reactions provide an unobtrusive way for an individual to express themselves. Often reactions are replacing low-value comments (i.e,. &lt;em&gt;+1&lt;/em&gt;, &lt;em&gt;LGTM&lt;/em&gt;, &lt;em&gt;awesome!&lt;/em&gt;).&lt;/p&gt;
  1244.  
  1245. &lt;p&gt;No unnecessary &lt;em&gt;@mention&lt;/em&gt; comments to indicate that someone should review the pull request. In the past, we would have &lt;em&gt;@mention&lt;/em&gt; possible reviewers in the description or as comments in the pull request. The reviewers are now clearly indicated in the reviewers section of the pull request.&lt;/p&gt;
  1246.  
  1247. &lt;p&gt;No unnecessary &lt;em&gt;@mention&lt;/em&gt; is needed to indicate a reviewer&amp;#39;s request for change has been addressed. Previously, it was common to &lt;em&gt;@mention&lt;/em&gt; a reviewer when their concerns were addressed, thus causing noise with comments.&lt;/p&gt;
  1248.  
  1249. &lt;p&gt;With the above points on &lt;em&gt;@mention&lt;/em&gt; you could also factor in that this communication could have been in chat (public channels or privately).&lt;/p&gt;
  1250.  
  1251. &lt;p&gt;Another scenario that we have encountered in the past is that the reviewer would come back preemptively to review the pull request as they noticed new commits. The problem is that sometimes new changes are still being worked on and pushed up incrementally. This can potentially waste the reviewer&amp;#39;s time as they have to look over more changes soon after.&lt;/p&gt;
  1252.  
  1253. &lt;p&gt;As a reviewer, you know that you will receive a notification via GitHub when your attention is needed for a pull request. It also becomes easier to scan pull requests for where your action is required.&lt;/p&gt;
  1254.  
  1255. &lt;h2&gt;Dealing with Notifications&lt;/h2&gt;
  1256.  
  1257. &lt;p&gt;There are a couple of options when dealing with notifications surrounding GitHub:&lt;/p&gt;
  1258.  
  1259. &lt;ul&gt;
  1260. &lt;li&gt;Built-in web notification&lt;/li&gt;
  1261. &lt;li&gt;Email notifications&lt;/li&gt;
  1262. &lt;li&gt;Third-party application (depends on operating system)&lt;/li&gt;
  1263. &lt;/ul&gt;
  1264.  
  1265. &lt;p&gt;I personally never got much benefit from the web notification, however, this could just be how I consume information. Without an actual notification appearing in my notification center (macOS), information doesn&amp;#39;t reach me well.&lt;/p&gt;
  1266.  
  1267. &lt;p&gt;Email notifications are currently my preferred approach, as I can receive request review notifications via email. In addition, with email you can get fancy with filters to further reduce notification to only what you want.&lt;/p&gt;
  1268.  
  1269. &lt;p&gt;I use &lt;a href=&quot;https://ptsochantaris.github.io/trailer/&quot;&gt;Trailer.app&lt;/a&gt; in addition to email. With Trailer I am able to target specific repositories for native notification. Prior to review requests, this would have been my ideal approach for dealing with notifications at my work machine. There is active development to support the recent addition of GitHub review requests and reactions.&lt;/p&gt;
  1270.  
  1271. &lt;p&gt;Regardless of delivery mechanism, take some time to figure how to deal with notifications. Each user will have different needs. For my case, I&amp;#39;m really only interested in emails about &lt;em&gt;Comments on Issues and Pull Requests&lt;/em&gt; and &lt;em&gt;Pull Request reviews&lt;/em&gt; on my &lt;em&gt;Participated Conversations&lt;/em&gt; (configured via &lt;a href=&quot;https://github.com/settings/notifications&quot;&gt;GitHub&amp;#39;s Notifications&lt;/a&gt;).&lt;/p&gt;
  1272.  
  1273. &lt;h2&gt;Keeping it all in GitHub&lt;/h2&gt;
  1274.  
  1275. &lt;p&gt;Going back to the above scenario, no use of direct form of communication was used to facilitate the code review, everything was kept within GitHub. Of course, deeper discussions should use those mediums, but the key is that the coordination of the code review was kept entirely in the GitHub platform. This reduces chatter and noise that we receive throughout the day, and keeps the GitHub pull request succinct.&lt;/p&gt;
  1276.  
  1277. &lt;p&gt;Using review requests clearly states the next action for the reviewer and author. With both parties actively using the provided utilities in GitHub, code review collaboration becomes much more manageable.&lt;/p&gt;
  1278.  
  1279. &lt;hr&gt;
  1280.  
  1281. &lt;h2&gt;TL;DR&lt;/h2&gt;
  1282.  
  1283. &lt;ul&gt;
  1284. &lt;li&gt;The idea is to reduce the unnecessary noise within a pull request using GitHub&amp;#39;s features.&lt;/li&gt;
  1285. &lt;li&gt;GitHub review requests keep state of each reviewer during the code review process.&lt;/li&gt;
  1286. &lt;li&gt;Make use of re-requesting a review when you have addressed a reviewer&amp;#39;s concerns.&lt;/li&gt;
  1287. &lt;li&gt;Make use of reactions to acknowledge a comment if possible.&lt;/li&gt;
  1288. &lt;li&gt;Be aware of committed reviewers and ensure everyone knows of their roles.&lt;/li&gt;
  1289. &lt;li&gt;Ensure that all committed reviewers have approved the pull request before merging.&lt;/li&gt;
  1290. &lt;/ul&gt;
  1291. </description>
  1292.    </item>
  1293.    
  1294.    
  1295.    
  1296.    <item>
  1297.      <title>Keep on Learning, but don't Forget to Remember</title>
  1298.      <link>https://techblog.thescore.com/2016/06/07/keep-on-learning-but-dont-forget-to-remember/</link>
  1299.      <pubDate>Tue, 07 Jun 2016 00:00:00 +0000</pubDate>
  1300.      <author></author>
  1301.      <guid>https://techblog.thescore.com/2016/06/07/keep-on-learning-but-dont-forget-to-remember</guid>
  1302.      <description>&lt;p&gt;Learning is a way of life for a developer. Frequently we are exposed to techniques and tips such as: keybindings, shell commands, utilities, new functions/methods, new applications. While learning, we want to keep on remembering the previous techniques and tips that we&amp;#39;ve learned so that we can keep them &lt;em&gt;fresh&lt;/em&gt; and on our tool belt. It is not uncommon to forget some learned knowledge if you don&amp;#39;t use it often.&lt;/p&gt;
  1303.  
  1304. &lt;p&gt;I would like to present my own problem and solution surrounding this idea.&lt;/p&gt;
  1305.  
  1306. &lt;h2&gt;Problem: Customization of vim/zsh&lt;/h2&gt;
  1307.  
  1308. &lt;p&gt;Vim and zsh are my editor and shell of choice, both decisions I made early in my career. I have since spent countless hours using both and each are indispensable to my development process. As typical of any developer I am always looking for ways to improve my process. Near the beginning of my career I discovered that both &lt;a href=&quot;http://vimawesome.com/&quot;&gt;vim&lt;/a&gt; and &lt;a href=&quot;https://github.com/unixorn/awesome-zsh-plugins&quot;&gt;zsh&lt;/a&gt; have thriving plugin communities.&lt;/p&gt;
  1309.  
  1310. &lt;p&gt;I saw that there were &lt;a href=&quot;https://github.com/spf13/spf13-vim&quot;&gt;popular&lt;/a&gt; vim &lt;a href=&quot;https://github.com/carlhuda/janus&quot;&gt;distributions&lt;/a&gt; that come pre-packaged with opinionated set of keybindings, plugins and themes. I originally used one of these and immediately felt overwhelmed with the added functionality. I eventually came to terms with the error of my ways, and went back to vanilla vim. Learning from my mistake I decided to slowly incorporate features I felt were useful as I came to need them.&lt;/p&gt;
  1311.  
  1312. &lt;p&gt;I continue to stumble upon new plugins that simplifies tasks. I add custom keybindings to perform certain motions. I create custom vim functions. While observing colleagues I take mental notes of vim motions that I have yet to take advantage of. I occasionally look through my setup and notice features and keybindings that I have not used in a while. Sometimes I remove them, but other times I&amp;#39;ll keep it and make a try and use it.&lt;/p&gt;
  1313.  
  1314. &lt;p&gt;My use of zsh is nearly an identical story to that of my use with vim. In either case I routinely accumulate and prune tips that I find useful. My largest gripe is &lt;em&gt;forgetting&lt;/em&gt; about new tips that I want to make part of my normal tool set. In most cases these have yet to become habit and common knowledge to me through repetitive exposure and use.&lt;/p&gt;
  1315.  
  1316. &lt;h2&gt;Solution&lt;/h2&gt;
  1317.  
  1318. &lt;p&gt;I decided to write my accumulation of tips down as one-liners in &lt;code&gt;tips.txt&lt;/code&gt; files, while using directories under &lt;code&gt;~/.tips&lt;/code&gt; for categorization:&lt;/p&gt;
  1319. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;~/.tips❯ tree
  1320. .
  1321. ├── ruby
  1322. │   └── rspec
  1323. │       └── tips.txt
  1324. ├── vim
  1325. │   ├── plugins
  1326. │   │   ├── ctrlp
  1327. │   │   │   └── tips.txt
  1328. │   │   └── nerdtree
  1329. │   │       └── tips.txt
  1330. │   └── vanilla
  1331. │       └── tips.txt
  1332. └── zsh
  1333.    └── tips.txt
  1334. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1335. &lt;p&gt;Ultimately my goal is to be presented with a random tip when I open a new shell. To accomplish this the following code snippet is placed near the start of my &lt;code&gt;.zshrc&lt;/code&gt; (also works in a &lt;code&gt;.bashrc&lt;/code&gt;):&lt;/p&gt;
  1336. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;# Displays a random tip from the .tips directory when opening the shell
  1337. # Requires gshuf (brew install coreutils)
  1338. (
  1339.  TIP_PATH=$(find ~/.tips -type f -name tips.txt | gshuf -n1)    # Pick a random tips.txt file
  1340.  TIP_TILE=${TIP_PATH#&amp;quot;$HOME/.tips/&amp;quot;}                            # i.e., ~/.tips/vim/vanilla/tips.txt  ---&amp;gt;  vim/vanilla/tips.txt
  1341.  echo &amp;quot;From ${TIP_TILE%.txt}:&amp;quot;                                  # i.e., &amp;quot;From vim/vanilla/tips:&amp;quot;
  1342.  gshuf -n1 &amp;lt; &amp;quot;$TIP_PATH&amp;quot;                                        # Displays a random line from the tip file
  1343. )
  1344. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1345. &lt;p&gt;Now throughout the day when I open many new shells, I am presented with a random recorded tip:&lt;/p&gt;
  1346. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;From vim/plugins/nerdtree/tips:
  1347. `&amp;lt;f2&amp;gt;` toggles nerdtree open/close
  1348. ~/.tips❯
  1349. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;From zsh/tips:
  1350. `j` allows you to jump to marked directories (via `jump`)
  1351. ~/.tips❯
  1352. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1353. &lt;h2&gt;Don&amp;#39;t Forget to Remember&lt;/h2&gt;
  1354.  
  1355. &lt;p&gt;As I continue to learn new things I&amp;#39;ll be added them to the appropriate &lt;code&gt;tips.txt&lt;/code&gt; within my &lt;code&gt;.tips&lt;/code&gt; directory. If I am presented with a tip that is no longer relevant I can simply remove it. Overall this seems like a great solution for recollecting tips.&lt;/p&gt;
  1356. </description>
  1357.    </item>
  1358.    
  1359.    
  1360.    
  1361.    <item>
  1362.      <title>Using State Machines to Handle Workflows</title>
  1363.      <link>https://techblog.thescore.com/2016/04/19/using-state-machines-to-handle-workflows/</link>
  1364.      <pubDate>Tue, 19 Apr 2016 00:00:00 +0000</pubDate>
  1365.      <author></author>
  1366.      <guid>https://techblog.thescore.com/2016/04/19/using-state-machines-to-handle-workflows</guid>
  1367.      <description>&lt;p&gt;Have you registered for a new user account in an app before? How about adding your payment info for buying something online? Those are two examples of what a “workflow” can be. Workflows are usually associated with a set of screens that appear in different orders depending on what the user selects. For example, registering with Facebook vs. email can change the sequence and number of screens presented to the user. We will demonstrate how state machines can be used to handle this logic in an iOS app.&lt;/p&gt;
  1368.  
  1369. &lt;h2&gt;What are state machines?&lt;/h2&gt;
  1370.  
  1371. &lt;p&gt;State machines contain a set of states with transitions between them. Each transition corresponds to an event, and when the event occurs, the state machine moves from one state to the next. For handling workflows, we will be using a finite state machine. Below is an abstract example.&lt;/p&gt;
  1372.  
  1373. &lt;p&gt;&lt;img src=&quot;http://i.imgur.com/4B4cs7g.png&quot; alt=&quot;Imgur&quot;&gt;&lt;/p&gt;
  1374.  
  1375. &lt;p&gt;The left arrow pointing into &lt;strong&gt;State A&lt;/strong&gt; denotes the initial state. When moving between states, it should be easy to determine where to go because transitions are uniquely defined. For example, &lt;strong&gt;State A&lt;/strong&gt; cannot have another transition labelled &lt;strong&gt;T1&lt;/strong&gt; going to &lt;strong&gt;State C&lt;/strong&gt;. &lt;/p&gt;
  1376.  
  1377. &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I could have labelled all the transitions differently (T1, T2, T3, T4), but it&amp;#39;s not necessary if we&amp;#39;re careful about reuse!&lt;/p&gt;
  1378.  
  1379. &lt;h2&gt;Example Scenario&lt;/h2&gt;
  1380.  
  1381. &lt;p&gt;Users are asked to register an account with us before they begin using the app. Our goal is to collect some basic information for the user, but also give them the ability to register later. We are given some screen designs by our designer to use for the registration process.  &lt;/p&gt;
  1382.  
  1383. &lt;p&gt;&lt;img src=&quot;http://i.imgur.com/PQoAOb5.png&quot; alt=&quot;Imgur&quot;&gt;&lt;/p&gt;
  1384.  
  1385. &lt;p&gt;We can begin by creating our list of &lt;strong&gt;states&lt;/strong&gt; for the registration process. Let’s start by marking each of the screens as a state. We’ll also need to add a &lt;strong&gt;finished&lt;/strong&gt; state even though it doesn’t correspond to a screen so that we can link our transitions properly.&lt;/p&gt;
  1386. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistrationStates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1387.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt;
  1388.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateProfile&lt;/span&gt;
  1389.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FacebookProfile&lt;/span&gt;
  1390.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SelectAvatar&lt;/span&gt;
  1391.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TermsAndConditions&lt;/span&gt;
  1392.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Finished&lt;/span&gt;
  1393. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1394. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1395. &lt;p&gt;&lt;strong&gt;Events&lt;/strong&gt; will be used to determine which screen we display next. In our registration process, the next screen will be shown when the user taps on a button, so we can use the list of user actions for our events.&lt;/p&gt;
  1396. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistrationActions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1397.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt;
  1398.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;
  1399.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Back&lt;/span&gt;
  1400.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegisterLater&lt;/span&gt;
  1401.    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FacebookRegister&lt;/span&gt;
  1402. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1403. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1404. &lt;p&gt;Now we can create our registration state machine based on our designs and screen flow.&lt;/p&gt;
  1405.  
  1406. &lt;p&gt;&lt;img src=&quot;http://i.imgur.com/Qnn5E8d.png&quot; alt=&quot;Imgur&quot;&gt;&lt;/p&gt;
  1407.  
  1408. &lt;h3&gt;Bonus&lt;/h3&gt;
  1409.  
  1410. &lt;p&gt;Since there are multiple transitions entering &lt;strong&gt;Select Avatar&lt;/strong&gt;. Some people might want the back button in &lt;strong&gt;Select Avatar&lt;/strong&gt; to return to either &lt;strong&gt;Facebook Profile&lt;/strong&gt; or &lt;strong&gt;Create Profile&lt;/strong&gt;, depending on where the user came from. &lt;/p&gt;
  1411.  
  1412. &lt;p&gt;&lt;img src=&quot;http://i.imgur.com/QNXcDym.png&quot; alt=&quot;Imgur&quot;&gt;&lt;/p&gt;
  1413.  
  1414. &lt;p&gt;This situation can be handled by storing logic outside of the state machine. One way you can do this is by setting a flag outside of the state machine (perhaps right before we navigate to that screen). Then, when the user taps back from select avatar, we would check the flag to determine which event to fire.&lt;/p&gt;
  1415.  
  1416. &lt;h2&gt;Implementation&lt;/h2&gt;
  1417.  
  1418. &lt;p&gt;Right now, our state machine is written as a Swift generic. Following the definition of a finite state machine, this is what it expects and how it behaves:&lt;/p&gt;
  1419.  
  1420. &lt;ul&gt;
  1421. &lt;li&gt;Contains a set of states&lt;/li&gt;
  1422. &lt;li&gt;Contains a set of events&lt;/li&gt;
  1423. &lt;li&gt;Contains transition functions, that accepts a state and event, then returns a target state &lt;/li&gt;
  1424. &lt;li&gt;Initial state&lt;/li&gt;
  1425. &lt;li&gt;Final state&lt;/li&gt;
  1426. &lt;/ul&gt;
  1427.  
  1428. &lt;p&gt;To use the state machine for our registration flow, we provide it with our list of states, events, and initial state.&lt;/p&gt;
  1429. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;registrationStateMachine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegistrationStates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistrationActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initialState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1430. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1431. &lt;p&gt;Now we need to connect our states by providing possible transitions (based on user actions) for each state. We’ve utilized subscripting to make this assignment more readable. To make it easier to visualize, we can refer to the state machine diagram we created earlier. I’ve included it here again for reference. &lt;/p&gt;
  1432.  
  1433. &lt;p&gt;&lt;img src=&quot;http://i.imgur.com/MVM5cRF.png&quot; alt=&quot;Imgur&quot;&gt;&lt;/p&gt;
  1434. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registrationStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  1435.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  1436.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Login&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Finished&lt;/span&gt;
  1437. &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  1438.  
  1439. &lt;span class=&quot;n&quot;&gt;registrationStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  1440.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SelectAvatar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  1441.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FacebookRegister&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FacebookProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  1442.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterLater&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TermsAndConditions&lt;/span&gt;
  1443. &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  1444.  
  1445. &lt;span class=&quot;n&quot;&gt;registrationStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FacebookProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  1446.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SelectAvatar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  1447.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterLater&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TermsAndConditions&lt;/span&gt;
  1448. &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  1449.  
  1450. &lt;span class=&quot;n&quot;&gt;registrationStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SelectAvatar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  1451.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TermsAndConditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  1452.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Back&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateProfile&lt;/span&gt;
  1453. &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  1454.  
  1455. &lt;span class=&quot;n&quot;&gt;registrationStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TermsAndConditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  1456.    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Finished&lt;/span&gt;
  1457. &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  1458. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1459. &lt;h3&gt;Usage&lt;/h3&gt;
  1460.  
  1461. &lt;p&gt;Since our state machine is separate from the UI logic of our registration flow, we will need to send events to our state machine to determine what to display next. We’ve mapped events to user actions, so we can trigger events in our button responder methods. I’ve added a protocol to handle sending events to the state machine.&lt;/p&gt;
  1462. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistrationActionProtocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1463.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;notifyStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistrationActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1464. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1465. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;didTapEmailRegister&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1466.    &lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;notifyStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1467. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1468.  
  1469. &lt;span class=&quot;kr&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;didTapSkip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1470.    &lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;notifyStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterLater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1471. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1472. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1473. &lt;p&gt;Next, we’ve used &lt;code&gt;if let&lt;/code&gt; to unwrap &lt;code&gt;nextState&lt;/code&gt; because the state machine will return &lt;code&gt;nil&lt;/code&gt; if there is no valid state for the given event. When a non-nil state is returned, we can map it to a screen with a switch statement, then push the view controller onto our navigation controller.&lt;/p&gt;
  1474. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;notifyStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegistrationActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1475.    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nextState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;registrationStateMachine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1476.        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1477.            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  1478.                &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;navigationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pushViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createProfileViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1479.  
  1480.            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FacebookProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  1481.                &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;navigationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pushViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;facebookProfileViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1482.            &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
  1483.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1484.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1485. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1486. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1487. &lt;p&gt;That’s it! You now have a state machine that handles registration flow. By decoupling it from the UI logic, you can freely modify screen display order without affecting other registration components.&lt;/p&gt;
  1488.  
  1489. &lt;p&gt;You can find the sample project &lt;a href=&quot;https://github.com/JessicaIp/SimpleStateMachine.git&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
  1490.  
  1491. &lt;h2&gt;Summary&lt;/h2&gt;
  1492.  
  1493. &lt;p&gt;State machines can be used to represent a variety of workflows in an app with minimal code duplication. They are particularly useful when the flow is complex or constantly changing.&lt;/p&gt;
  1494. </description>
  1495.    </item>
  1496.    
  1497.    
  1498.    
  1499.    <item>
  1500.      <title>UITableView & UICollectionView: Killing stringly typed cells with Swift</title>
  1501.      <link>https://techblog.thescore.com/2016/04/04/Typed-UITableView-&-UICollectionView-Dequeuing-in-Swift/</link>
  1502.      <pubDate>Mon, 04 Apr 2016 00:00:00 +0000</pubDate>
  1503.      <author></author>
  1504.      <guid>https://techblog.thescore.com/2016/04/04/Typed-UITableView-&-UICollectionView-Dequeuing-in-Swift</guid>
  1505.      <description>&lt;p&gt;UITableView &amp;amp; UICollectionView are the bread and butter of many iOS applications. However, dequeuing cells with string identifiers can result in brittle code that doesn&amp;#39;t scale as table complexity increases.  We&amp;#39;re going to look at using Swift&amp;#39;s type system to get rid of the ad-hoc typing we get from using string identifiers and eliminate the need for type casts in the process.&lt;/p&gt;
  1506.  
  1507. &lt;h2&gt;Introduction&lt;/h2&gt;
  1508.  
  1509. &lt;p&gt;It&amp;#39;s helpful to look at the original context in which an API was used in order to understand the problems it solves. To many of us the following code will be all too familiar.&lt;/p&gt;
  1510. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objective-c&quot; data-lang=&quot;objective-c&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tableView:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cellForRowAtIndexPath:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSIndexPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;indexPath&lt;/span&gt;
  1511. &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1512. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSString&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CellIdentifier&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;MyCellIdentifier&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  1513. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CellIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  1514.  
  1515. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1516. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initWithStyle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UITableViewCellStyleDefault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reuseIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CellIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  1517. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1518.  
  1519. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Do stuff with cell&lt;/span&gt;
  1520.  
  1521. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;
  1522. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1523. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1524. &lt;p&gt;This convention has not conceptually changed since its introduction. The code is highly localized: the definition of the identifier, the dequeuing of the cell, and the allocation of a cell with a given identifier are all in one place, making it easy to reason about. This also makes the casting of the cell&amp;#39;s type seem innocuous.&lt;/p&gt;
  1525.  
  1526. &lt;p&gt;As apps become increasingly complex, &lt;code&gt;UITableViewDelegate&lt;/code&gt;s often started displaying multiple data types, leading to patterns like the following:&lt;/p&gt;
  1527. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objective-c&quot; data-lang=&quot;objective-c&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tableView:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cellForRowAtIndexPath:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSIndexPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;indexPath&lt;/span&gt;
  1528. &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1529. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  1530.  
  1531. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1532. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSString&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CellIdentifier&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;MyCellIdentifier&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  1533. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CellIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  1534.  
  1535. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1536. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initWithStyle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UITableViewCellStyleDefault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reuseIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CellIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  1537. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1538.  
  1539. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Do stuff with cell&lt;/span&gt;
  1540.  
  1541. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myCell&lt;/span&gt;
  1542. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1543. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1544. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSString&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherCellIdentifier&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;MyOtherCellIdentifier&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  1545. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyOtherCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyOtherCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherCellIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  1546.  
  1547. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1548. &lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyOtherCell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initWithStyle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UITableViewCellStyleDefault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reuseIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherCellIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  1549. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1550.  
  1551. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Do stuff with other cell&lt;/span&gt;
  1552.  
  1553. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  1554. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1555.  
  1556. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;
  1557. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1558. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1559. &lt;p&gt;Most developers are still pretty comfortable with this. Locality is high and the code is fairly easy to reason about, but that cast is there again. I copied the else case and changed the bits I needed but I&amp;#39;m still using strings as a type system to discern the type of cell that should be dequeued. This could become a problem as my table grows in complexity.&lt;/p&gt;
  1560.  
  1561. &lt;p&gt;iOS 5 added prototype cells, which sticks the reuse identifier in a storyboard. It also added &lt;code&gt;- (void)registerNib:(UINib *)nibforCellReuseIdentifier:(NSString *)identifier&lt;/code&gt; and iOS 6 gave us &lt;code&gt;- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier&lt;/code&gt; which is often called from &lt;code&gt;-(void)viewDidLoad&lt;/code&gt;. These methods allow us to neatly separate out the concerns of cell configuration and instantiation/registration, but now it&amp;#39;s spread all over our class, and our stringly typed cells are likely to bite us at some point in the future.&lt;/p&gt;
  1562.  
  1563. &lt;p&gt;Luckily, Swift gives us some tools to help deal with this.&lt;/p&gt;
  1564.  
  1565. &lt;h2&gt;Providing a consistent method of determining identifers&lt;/h2&gt;
  1566.  
  1567. &lt;p&gt;Identifiers are how we determine type information for cells in &lt;code&gt;UITableView&lt;/code&gt; and &lt;code&gt;UICollectionView&lt;/code&gt;. They work well for small applications but degrade as the complexity of the &lt;code&gt;UITableView&lt;/code&gt; (and thus the number of types) increases. Providing a consistent method of determining identifiers will go a long way towards making life easier.&lt;/p&gt;
  1568. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1569.    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1570.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSStringFromClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1571.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1572. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1573.  
  1574. &lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewHeaderFooterView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1575.    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1576.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSStringFromClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1577.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1578. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1579. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1580. &lt;p&gt;Now we have an easy way of producing an identifier based on the type. Additionally, subclasses can provide their own implementations if the default implementations don&amp;#39;t suffice. This approach isn&amp;#39;t uncommon, and you could do a lot worse than getting here and calling it a day.&lt;/p&gt;
  1581.  
  1582. &lt;p&gt;Note that we use &lt;code&gt;NSStringFromClass(self)&lt;/code&gt; instead of &lt;code&gt;String(self)&lt;/code&gt; for the default implementation. This is because NSStringFromClass includes the module name which can be helpful for differentiating from the &lt;code&gt;MyApp.MyCell&lt;/code&gt; and &lt;code&gt;SomeThirdPartyDependecy.MyCell&lt;/code&gt; in the event that the two are used in the same tableview at some point in the future. &lt;/p&gt;
  1583.  
  1584. &lt;h2&gt;Using Types for Registration&lt;/h2&gt;
  1585.  
  1586. &lt;p&gt;But why would we stop there? Now that every type has a method of producing an identifier why not just use the type itself for registration?&lt;/p&gt;
  1587. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Cells&lt;/span&gt;
  1588. &lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1589.    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cellClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1590.        &lt;span class=&quot;n&quot;&gt;registerClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forCellReuseIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  1591.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1592.  
  1593.    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UINib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1594.        &lt;span class=&quot;n&quot;&gt;registerNib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forCellReuseIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  1595.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1596. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1597.  
  1598. &lt;span class=&quot;c1&quot;&gt;// Header / Footer&lt;/span&gt;
  1599. &lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1600.    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewHeaderFooterView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headerFooterClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1601.        &lt;span class=&quot;n&quot;&gt;registerClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forHeaderFooterViewReuseIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  1602.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1603.  
  1604.    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewHeaderFooterView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UINib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forHeaderFooterClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1605.        &lt;span class=&quot;n&quot;&gt;registerNib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forHeaderFooterViewReuseIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  1606.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1607. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1608. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1609. &lt;p&gt;This is starting to look pretty good, we have strong types and can use the types themselves as registration rather than providing a potentially error prone &lt;code&gt;String -&amp;gt; Type mapping&lt;/code&gt;. This is also pretty trivial, the nice bit here is that generics give us a bit of type safety around what we can register.&lt;/p&gt;
  1610.  
  1611. &lt;h2&gt;Using Types to Dequeue&lt;/h2&gt;
  1612.  
  1613. &lt;p&gt;We had been using the above for quite a while and it took us a bit longer for us to put together then next piece. Which uses generics and the type system to get an appropriate type back out.&lt;/p&gt;
  1614. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1615.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dequeueReusableCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1616.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
  1617.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1618.  
  1619.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dequeueReusableCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;NSIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1620.        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1621.            &lt;span class=&quot;bp&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Error: cell with identifier: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; for index path: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is not &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1622.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1623.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;
  1624.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1625.  
  1626.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dequeueResuableHeaderFooterView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewHeaderFooterView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1627.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dequeueReusableHeaderFooterViewWithIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(`&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;`.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
  1628.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1629. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1630. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1631. &lt;p&gt;Some of you may be looking at that &lt;code&gt;fatalError()&lt;/code&gt; and getting cagey but I assure you it`s all right. Way back in June, &lt;a href=&quot;https://www.natashatherobot.com&quot;&gt;Natasha&lt;/a&gt; wrote a &lt;a href=&quot;https://www.natashatherobot.com/ios-using-the-wrong-dequeuereusablecellwithidentifier/&quot;&gt;post&lt;/a&gt; about using the right dequeue method. Let&amp;#39;s have a look at that type signature again.&lt;/p&gt;
  1632. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;NSIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt;
  1633. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1634. &lt;p&gt;There&amp;#39;s no &lt;code&gt;?&lt;/code&gt; at the end but that string as an identifier means one of two things. Either UIKit is lying to us or somewhere deep in it&amp;#39;s bowels it&amp;#39;s going to have a fit and crash on you. Turns out it&amp;#39;s the latter and you get a &lt;code&gt;NSInternalConsistencyException&lt;/code&gt;. The docs say: &lt;/p&gt;
  1635.  
  1636. &lt;blockquote&gt;
  1637. &lt;p&gt;IMPORTANT &lt;/p&gt;
  1638.  
  1639. &lt;p&gt;You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before
  1640. calling this method.&lt;/p&gt;
  1641. &lt;/blockquote&gt;
  1642.  
  1643. &lt;p&gt;This makes sense, how can &lt;code&gt;UITableView&lt;/code&gt; guarantee that there is going to be a cell unless that identifier is used to register a cell class? If the identifier isn&amp;#39;t registered then there&amp;#39;s been an error, possibly something as simple as a typo on the part of the programmer.
  1644. The motivation for Natasha&amp;#39;s article is to get away from having to unwrap optional cells. Unfortunately if we have custom cell subclasses we still end up with either:&lt;/p&gt;
  1645. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;MyCellIdentifier&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1646.    &lt;span class=&quot;c1&quot;&gt;// Do stuff with cell&lt;/span&gt;
  1647. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1648. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1649. &lt;p&gt;or&lt;/p&gt;
  1650. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCellWithIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;MyCellIdentifier&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;
  1651. &lt;span class=&quot;c1&quot;&gt;// Do stuff with cell&lt;/span&gt;
  1652. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1653. &lt;p&gt;Neither of these are particularily nice looking: we either introduce a different &lt;code&gt;if let&lt;/code&gt; to check that the type is the one expected or we perform a force cast to the type and hope that there isn&amp;#39;t a mapping mis-match between cell and identifier. Both solutions will still crash if the identifier has never been registered and they litter your code with a bunch of explicit type information. Our new dequeue method looks much cleaner.&lt;/p&gt;
  1654. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1655. &lt;span class=&quot;c1&quot;&gt;// Do stuff with cell&lt;/span&gt;
  1656. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1657. &lt;p&gt;Putting it all together we get:&lt;/p&gt;
  1658. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyTableViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1659.    &lt;span class=&quot;kr&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1660.        &lt;span class=&quot;kc&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  1661.  
  1662.        &lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cellClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1663.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1664.  
  1665.    &lt;span class=&quot;kr&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfRowsInSection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1666.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  1667.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1668.  
  1669.    &lt;span class=&quot;kr&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cellForRowAtIndexPath&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;NSIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITableViewCell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1670.        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dequeueReusableCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forIndexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1671.  
  1672.        &lt;span class=&quot;c1&quot;&gt;// Do stuff with cell&lt;/span&gt;
  1673.  
  1674.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;
  1675.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1676. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1677. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1678. &lt;p&gt;This looks pretty good. We no longer need to rely on strings for registration and dequeuing. We don&amp;#39;t have to add type annotations or casting to use a custom cell. And if we get a fatal error the problem is easy to debug as it simply means that the cell you are trying to use was never registered with this class. &lt;/p&gt;
  1679.  
  1680. &lt;p&gt;You can grab the complete code &lt;a href=&quot;https://gist.github.com/tapi/60a6ee1cbfeabd12180ad88d9714591f&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
  1681. </description>
  1682.    </item>
  1683.    
  1684.    
  1685.    
  1686.    <item>
  1687.      <title>Defined Methods in Rake Tasks; You're Gonna Have a Bad Time</title>
  1688.      <link>https://techblog.thescore.com/2016/03/17/defined_methods-in-rake-tasks-you-re-gonna-have-a-bad-time/</link>
  1689.      <pubDate>Thu, 17 Mar 2016 00:00:00 +0000</pubDate>
  1690.      <author></author>
  1691.      <guid>https://techblog.thescore.com/2016/03/17/defined_methods-in-rake-tasks-you-re-gonna-have-a-bad-time</guid>
  1692.      <description>&lt;p&gt;Rake tasks provide a nice way to handle common tasks surrounding a ruby project. Within Rails projects they are nearly unavoidable and even have their own directory from which they are autoloaded. Eventually a project will grow in size and complexity to warrant multiple &lt;em&gt;task&lt;/em&gt; files for better separation of concerns. This alone is nothing to be worried about, but it&amp;#39;s when you start using methods in your task files where &lt;em&gt;you&amp;#39;re gonna have a bad time&lt;/em&gt;.&lt;/p&gt;
  1693.  
  1694. &lt;p&gt;Let&amp;#39;s setup a dummy Rails project that has a task file that calculates and saves blog metrics.&lt;/p&gt;
  1695. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/tasks/blog_metrics_task.rake&lt;/span&gt;
  1696. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Calculate and save blog metrics&amp;#39;&lt;/span&gt;
  1697. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:blog_metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1698. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calculate_blog_metrics&lt;/span&gt;
  1699. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1700. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1701.  
  1702. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate_blog_metrics&lt;/span&gt;
  1703. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Calculating blog metrics&amp;quot;&lt;/span&gt;
  1704. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1705.  
  1706. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1707. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving blog metrics&amp;quot;&lt;/span&gt;
  1708. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1709. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1710. &lt;p&gt;When we run our task it does exactly what we wanted and expected it to do.&lt;/p&gt;
  1711. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;$ rake blog_metrics
  1712. Calculating blog metrics
  1713. Saving blog Metrics
  1714. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1715. &lt;p&gt;No problem! Now lets fast-forward in time to when we want to add another task that creates a new blog post.&lt;/p&gt;
  1716. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/tasks/create_blog_post_task.rake&lt;/span&gt;
  1717. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Create and save a new blog post&amp;#39;&lt;/span&gt;
  1718. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create_blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1719. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generate_default_blog_post&lt;/span&gt;
  1720. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1721. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1722.  
  1723. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generate_default_blog_post&lt;/span&gt;
  1724. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Generating a default blog post&amp;quot;&lt;/span&gt;
  1725. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1726.  
  1727. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1728. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving defualt blog post&amp;quot;&lt;/span&gt;
  1729. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1730. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1731. &lt;p&gt;When we run our new task it does exactly what we wanted and expected it to do.&lt;/p&gt;
  1732. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;$ rake create_blog_post
  1733. Generating a default blog post
  1734. Saving default blog post
  1735. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1736. &lt;p&gt;Another success! Now here is where things get interesting. Let&amp;#39;s go back and run the first &lt;em&gt;correctly working&lt;/em&gt; task.&lt;/p&gt;
  1737. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;$ rake blog_metrics
  1738. Calculating blog metrics
  1739. Saving default blog post
  1740. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1741. &lt;p&gt;Woah... it&amp;#39;s the &lt;code&gt;#save&lt;/code&gt; that was defined in the other task file -- &lt;code&gt;create_blog_post_task.rake&lt;/code&gt;.&lt;/p&gt;
  1742.  
  1743. &lt;p&gt;This is kind of shocking and might have caught you off guard. Rails automatically loads all rake tasks (i.e., requires their file) when executing any rake task. The &lt;em&gt;gotcha&lt;/em&gt; here is that the defined methods in the loaded tasks files end up defined on the global namespace. These defined methods are therefore accessible across rake files, so it is &lt;em&gt;possible&lt;/em&gt; for methods to clash and be redefined if their signatures match.&lt;/p&gt;
  1744.  
  1745. &lt;p&gt;To better illustrate the order of events:&lt;/p&gt;
  1746.  
  1747. &lt;ol&gt;
  1748. &lt;li&gt;&lt;code&gt;rake blog_metrics&lt;/code&gt;&lt;/li&gt;
  1749. &lt;li&gt;&lt;em&gt;Rails autoloads all rake tasks in alphanumeric order&lt;/em&gt;&lt;/li&gt;
  1750. &lt;li&gt;&lt;code&gt;lib/tasks/blog_metrics_task.rake&lt;/code&gt; &lt;em&gt;is loaded and defines&lt;/em&gt; &lt;code&gt;#calculate_blog_metrics&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code&gt;#save&lt;/code&gt;&lt;/li&gt;
  1751. &lt;li&gt;&lt;code&gt;lib/tasks/create_blog_post_task.rake&lt;/code&gt; &lt;em&gt;is loaded and defines&lt;/em&gt; &lt;code&gt;# generate_default_blog_post&lt;/code&gt; &lt;em&gt;and &lt;strong&gt;redefines&lt;/strong&gt;&lt;/em&gt; &lt;code&gt;#save&lt;/code&gt;&lt;/li&gt;
  1752. &lt;li&gt;&lt;code&gt;blog_metrics&lt;/code&gt; &lt;em&gt;task is executed using last defined &lt;code&gt;#save&lt;/code&gt;, which was defined in the other task file&lt;/em&gt;&lt;/li&gt;
  1753. &lt;/ol&gt;
  1754.  
  1755. &lt;p&gt;No worries right? Rake provides a &amp;#39;namespace&amp;#39; DSL. So we can modify our tasks to use this.&lt;/p&gt;
  1756. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:blog_metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1757. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Calculate and save blog metrics&amp;#39;&lt;/span&gt;
  1758. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:run&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1759. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calculate_blog_metrics&lt;/span&gt;
  1760. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1761. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1762.  
  1763. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate_blog_metrics&lt;/span&gt;
  1764. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Calculating blog metrics&amp;quot;&lt;/span&gt;
  1765. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1766.  
  1767. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1768. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving blog metrics&amp;quot;&lt;/span&gt;
  1769. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1770. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1771. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1772. &lt;p&gt;We should be in the clear now.&lt;/p&gt;
  1773. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;$ rake blog_metrics:run
  1774. Calculating blog metrics
  1775. Saving default blog post
  1776. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1777. &lt;p&gt;Nope! The namespace DSL does nothing for the defined methods. So we still have the same problem.&lt;/p&gt;
  1778.  
  1779. &lt;p&gt;There are a couple of solutions to this problem:&lt;/p&gt;
  1780.  
  1781. &lt;ol&gt;
  1782. &lt;li&gt;Rename the methods, and ensure all future methods are uniquely named&lt;/li&gt;
  1783. &lt;li&gt;Inline the contents of the defined methods&lt;/li&gt;
  1784. &lt;li&gt;Extract the methods into a module/class and use that&lt;/li&gt;
  1785. &lt;/ol&gt;
  1786.  
  1787. &lt;h2&gt;Solution #1 - Uniquely Named Methods&lt;/h2&gt;
  1788.  
  1789. &lt;p&gt;It is possible to simply ensure that we uniquely name our methods so that they do no clash and end up redefining each other.&lt;/p&gt;
  1790. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/tasks/blog_metrics_task.rake&lt;/span&gt;
  1791. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Calculate and save blog metrics&amp;#39;&lt;/span&gt;
  1792. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:blog_metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1793. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calculate_blog_metrics&lt;/span&gt;
  1794. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1795. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1796.  
  1797. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate_blog_metrics&lt;/span&gt;
  1798. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Calculating blog metrics&amp;quot;&lt;/span&gt;
  1799. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1800.  
  1801. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save_blog_metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1802. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving blog metrics&amp;quot;&lt;/span&gt;
  1803. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1804. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/tasks/create_blog_post_task.rake&lt;/span&gt;
  1805. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Create and save a new blog post&amp;#39;&lt;/span&gt;
  1806. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create_blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1807. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generate_default_blog_post&lt;/span&gt;
  1808. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1809. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1810.  
  1811. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generate_default_blog_post&lt;/span&gt;
  1812. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Generating a default blog post&amp;quot;&lt;/span&gt;
  1813. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1814.  
  1815. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save_default_blog_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1816. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving defualt blog post&amp;quot;&lt;/span&gt;
  1817. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1818. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1819. &lt;p&gt;This works and is a quick fix, although it is not exactly sustainable and requires you to be conscientious when naming new methods.&lt;/p&gt;
  1820.  
  1821. &lt;h2&gt;Solution #2 - Inline Method Contents&lt;/h2&gt;
  1822.  
  1823. &lt;p&gt;To ensure that method redefinition doesn&amp;#39;t occur we can simply remove the methods and inline their content.&lt;/p&gt;
  1824. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/tasks/blog_metrics_task.rake&lt;/span&gt;
  1825. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Calculate and save blog metrics&amp;#39;&lt;/span&gt;
  1826. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:blog_metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1827. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Calculating blog metrics&amp;quot;&lt;/span&gt;
  1828. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Inline calculating work&lt;/span&gt;
  1829.  
  1830. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving blog metrics&amp;quot;&lt;/span&gt;
  1831. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Inline saving work&lt;/span&gt;
  1832. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1833. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/tasks/create_blog_post_task.rake&lt;/span&gt;
  1834. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Create and save a new blog post&amp;#39;&lt;/span&gt;
  1835. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create_blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1836. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Generating a default blog post&amp;quot;&lt;/span&gt;
  1837. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Inline blog post generation work&lt;/span&gt;
  1838.  
  1839. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving default blog post&amp;quot;&lt;/span&gt;
  1840. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Inline saving work&lt;/span&gt;
  1841. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1842. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1843. &lt;p&gt;This is also a quick fix, and might be optimal depending on the size, complexity, and reuse of the method&amp;#39;s content.&lt;/p&gt;
  1844.  
  1845. &lt;h2&gt;Solution #3 - Extract Methods into Module/Class&lt;/h2&gt;
  1846.  
  1847. &lt;p&gt;Removing the methods from the rake files themselves is another valid solution. The methods can be extracted into their own class or module and used within the task files.&lt;/p&gt;
  1848. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/blog_metric_calculator.rb&lt;/span&gt;
  1849. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BlogMetricCalculator&lt;/span&gt;
  1850. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;metrics&lt;/span&gt;
  1851. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Calculating blog metrics&amp;quot;&lt;/span&gt;
  1852. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1853.  
  1854. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1855. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving blog metrics&amp;quot;&lt;/span&gt;
  1856. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1857. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1858.  
  1859. &lt;span class=&quot;c1&quot;&gt;# lib/tasks/blog_metrics_task.rake&lt;/span&gt;
  1860. &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;lib/blog_metric_calculator&amp;#39;&lt;/span&gt;
  1861.  
  1862. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Calculate and save blog metrics&amp;#39;&lt;/span&gt;
  1863. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:blog_metrics&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1864. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BlogMetricCalculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;
  1865. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1866. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1867. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# lib/blog_post_creator.rb&lt;/span&gt;
  1868. &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;BlogPostCreator&lt;/span&gt;
  1869. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_default_blog_post&lt;/span&gt;
  1870. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Generating a default blog post&amp;quot;&lt;/span&gt;
  1871. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1872.  
  1873. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1874. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Saving default blog post&amp;quot;&lt;/span&gt;
  1875. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1876. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1877.  
  1878. &lt;span class=&quot;c1&quot;&gt;# lib/tasks/create_blog_post_task.rake&lt;/span&gt;
  1879. &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;lib/blog_post_creator&amp;#39;&lt;/span&gt;
  1880.  
  1881. &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Create and save a new blog post&amp;#39;&lt;/span&gt;
  1882. &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create_blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  1883. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BlogPostCreator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_default_blog_post&lt;/span&gt;
  1884. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BlogPostCreator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1885. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  1886. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1887. &lt;p&gt;This is the preferred method if there is sufficient complexity involved. By extracting the methods you begin to build up a set of related concerns within a module/class. By having an external entity outside of the rake tasks themselves you can now &lt;em&gt;test&lt;/em&gt; the defined functionality!&lt;/p&gt;
  1888. </description>
  1889.    </item>
  1890.    
  1891.    
  1892.    
  1893.    <item>
  1894.      <title>Introduction to Fisticuffs</title>
  1895.      <link>https://techblog.thescore.com/2015/12/18/fisticuffs/</link>
  1896.      <pubDate>Fri, 18 Dec 2015 00:00:00 +0000</pubDate>
  1897.      <author></author>
  1898.      <guid>https://techblog.thescore.com/2015/12/18/fisticuffs</guid>
  1899.      <description>&lt;p&gt;&lt;a href=&quot;https://github.com/scoremedia/Fisticuffs&quot;&gt;Fisticuffs&lt;/a&gt; is a compact Swift framework for view-model bindings on iOS, inspired by &lt;a href=&quot;http://knockoutjs.com&quot;&gt;KnockoutJS&lt;/a&gt;.  It lets developers quickly set up responsive applications without needing to add intermediate view-updating logic.&lt;/p&gt;
  1900.  
  1901. &lt;h2&gt;Background&lt;/h2&gt;
  1902.  
  1903. &lt;p&gt;The &lt;strong&gt;Model-view-viewmodel (MVVM)&lt;/strong&gt; pattern has gained a lot of traction in the iOS community over the past couple years, and for good reason.  Separating out the display logic (&lt;em&gt;&amp;quot;view model&amp;quot;&lt;/em&gt;) from the actual display (&lt;em&gt;&amp;quot;view&amp;quot;&lt;/em&gt;) has great benefits for code organization and testability.&lt;/p&gt;
  1904.  
  1905. &lt;p&gt;Fisticuffs simplifies the cycle of updating views based on view-model changes by extending UIKit components for data binding. By encapsulating presentation logic in view-models we can create testable view code. Adding a binding layer like Fisticuffs lets us quickly tie this presentation logic to a view without having to write a lot of wiring.&lt;/p&gt;
  1906.  
  1907. &lt;h2&gt;Overview&lt;/h2&gt;
  1908.  
  1909. &lt;p&gt;At the core of Fisticuffs are 2 classes:&lt;/p&gt;
  1910.  
  1911. &lt;ul&gt;
  1912. &lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Observable&lt;/code&gt;&lt;/strong&gt; - Wraps a value, notifying any subscribers when its value changes.  Think of &lt;code&gt;Observable&lt;/code&gt; as a read-write property.&lt;/p&gt;
  1913. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Johnny Appleseed&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1914. &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;John Smith&amp;quot;&lt;/span&gt;
  1915. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
  1916. &lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Computed&lt;/code&gt;&lt;/strong&gt; - Read-only value computed from one or more &lt;code&gt;Observable&lt;/code&gt;s.  Additionally, since &lt;code&gt;Computed&lt;/code&gt; has knowledge of which &lt;code&gt;Observable&lt;/code&gt;s it depends on, it automatically notifies its subscribers when any of its dependencies are changed.  Think of &lt;code&gt;Computed&lt;/code&gt; as a computed property.&lt;/p&gt;
  1917. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Computed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1918.    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Hello, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;
  1919. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1920. &lt;span class=&quot;bp&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// &amp;quot;Hello, John Smith&amp;quot;&lt;/span&gt;
  1921. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
  1922. &lt;/ul&gt;
  1923.  
  1924. &lt;h3&gt;UI Binding&lt;/h3&gt;
  1925.  
  1926. &lt;p&gt;Fisticuffs extends many UIKit classes to support binding &lt;code&gt;Observable&lt;/code&gt; &amp;amp; &lt;code&gt;Computed&lt;/code&gt;&amp;#39;s to their properties.  Building on the previous snippets, we could do something like this:&lt;/p&gt;
  1927. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nameTextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITextField&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
  1928. &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;greetingLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UILabel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
  1929.  
  1930. &lt;span class=&quot;n&quot;&gt;nameTextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1931. &lt;span class=&quot;n&quot;&gt;greetingLabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1932. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1933. &lt;p&gt;In this case, the value of &lt;code&gt;greetingLabel.text&lt;/code&gt; will &lt;strong&gt;automatically update&lt;/strong&gt; as the user enters text in the &lt;code&gt;nameTextField&lt;/code&gt;.&lt;/p&gt;
  1934.  
  1935. &lt;h2&gt;Example&lt;/h2&gt;
  1936.  
  1937. &lt;p&gt;Traditionally, without a bindings framework, one might implement a login page like so:&lt;/p&gt;
  1938. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoginViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1939.    &lt;span class=&quot;kr&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;usernameField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;
  1940.    &lt;span class=&quot;kr&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;passwordField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;
  1941.    &lt;span class=&quot;kr&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;submitButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UIButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;
  1942.  
  1943.    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;isLoading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  1944.  
  1945.    &lt;span class=&quot;kr&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1946.        &lt;span class=&quot;kc&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  1947.        &lt;span class=&quot;n&quot;&gt;updateSubmitButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  1948.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1949.  
  1950.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateSubmitButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1951.        &lt;span class=&quot;n&quot;&gt;submitButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isLoading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  1952.            &lt;span class=&quot;n&quot;&gt;usernameField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;isEmpty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  1953.            &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;isEmpty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  1954.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1955.  
  1956.    &lt;span class=&quot;kr&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usernameFieldEditingChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1957.        &lt;span class=&quot;n&quot;&gt;updateSubmitButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  1958.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1959.  
  1960.    &lt;span class=&quot;kr&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwordFieldEditingChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1961.        &lt;span class=&quot;n&quot;&gt;updateSubmitButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  1962.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1963.  
  1964.    &lt;span class=&quot;kr&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;submitButtonTouchUpInside&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1965.        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usernameField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;
  1966.        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwordField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;
  1967.        &lt;span class=&quot;c1&quot;&gt;// ... do login with `username` / `password` ...&lt;/span&gt;
  1968.        &lt;span class=&quot;n&quot;&gt;isLoading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  1969.        &lt;span class=&quot;n&quot;&gt;updateSubmitButtonState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  1970.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1971. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1972. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  1973. &lt;p&gt;While this approach works, there are a few pain points:&lt;/p&gt;
  1974.  
  1975. &lt;ul&gt;
  1976. &lt;li&gt;&lt;p&gt;It&amp;#39;s somewhat fragile.  Any maintainer has to know when to call &lt;code&gt;updateSubmitButtonState()&lt;/code&gt;, and it may be challenging to figure out where we are missing an &lt;code&gt;updateSubmitButtonState()&lt;/code&gt; call if the &lt;code&gt;submitButton.enabled&lt;/code&gt; property is not being updated.&lt;/p&gt;&lt;/li&gt;
  1977. &lt;li&gt;&lt;p&gt;The display and business logic are deeply intertwined, making it difficult to test.&lt;/p&gt;&lt;/li&gt;
  1978. &lt;/ul&gt;
  1979.  
  1980. &lt;p&gt;Using an MVVM approach with Fisticuffs, we can solve both those problems:&lt;/p&gt;
  1981. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoginViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1982.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1983.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1984.  
  1985.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  1986.  
  1987.    &lt;span class=&quot;kr&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;canSubmit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Computed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loading&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
  1988.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;isEmpty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  1989.            &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;isEmpty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  1990.            &lt;span class=&quot;n&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  1991.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1992.  
  1993.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doLogin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  1994.        &lt;span class=&quot;n&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  1995.        &lt;span class=&quot;c1&quot;&gt;// ... do login here with `self.username` &amp;amp; `self.password`&lt;/span&gt;
  1996.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1997. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  1998.  
  1999. &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoginViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2000.    &lt;span class=&quot;kr&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;usernameField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;
  2001.    &lt;span class=&quot;kr&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;passwordField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UITextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;
  2002.    &lt;span class=&quot;kr&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;submitButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;UIButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;
  2003.  
  2004.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;viewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoginViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  2005.  
  2006.    &lt;span class=&quot;kr&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2007.        &lt;span class=&quot;kc&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  2008.  
  2009.        &lt;span class=&quot;c1&quot;&gt;// Bind our text fields to the corresponding properties on our view model&lt;/span&gt;
  2010.        &lt;span class=&quot;n&quot;&gt;usernameField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2011.        &lt;span class=&quot;n&quot;&gt;passwordField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2012.  
  2013.        &lt;span class=&quot;c1&quot;&gt;// Bind the `enabled` state of the title button to the `canSubmit` field&lt;/span&gt;
  2014.        &lt;span class=&quot;n&quot;&gt;submitButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b_enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canSubmit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2015.  
  2016.        &lt;span class=&quot;c1&quot;&gt;// Call the `doLogin` function when the `submitButton` is tapped&lt;/span&gt;
  2017.        &lt;span class=&quot;n&quot;&gt;submitButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b_onTap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doLogin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2018.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2019. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2020. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2021. &lt;p&gt;We set up a view model (&lt;code&gt;LoginViewModel&lt;/code&gt;) that encapsulates all our business logic.  Since it has no dependencies on &lt;em&gt;UIKit&lt;/em&gt;, it makes it trivial to write tests.&lt;/p&gt;
  2022.  
  2023. &lt;p&gt;Additionally, because Fisticuffs automatically handles tracking dependencies, it&amp;#39;ll &lt;strong&gt;automatically update&lt;/strong&gt; the submit button&amp;#39;s &lt;code&gt;enabled&lt;/code&gt; property any time any of the dependencies changes.  This frees the initial developer and any maintainers from tracking those updates manually.&lt;/p&gt;
  2024.  
  2025. &lt;center&gt;
  2026. ![In action](/assets/animation.gif)
  2027. &lt;/center&gt;
  2028.  
  2029. &lt;h2&gt;Summary&lt;/h2&gt;
  2030.  
  2031. &lt;p&gt;Fisticuffs is a great little tool for quickly binding view-models to views. We hope you find it useful, and please check out the project &lt;a href=&quot;https://github.com/scoremedia/Fisticuffs&quot;&gt;on GitHub&lt;/a&gt;!&lt;/p&gt;
  2032. </description>
  2033.    </item>
  2034.    
  2035.    
  2036.    
  2037.    <item>
  2038.      <title>Sorting by Relative Popularity</title>
  2039.      <link>https://techblog.thescore.com/2015/12/11/sorting-players/</link>
  2040.      <pubDate>Fri, 11 Dec 2015 00:00:00 +0000</pubDate>
  2041.      <author></author>
  2042.      <guid>https://techblog.thescore.com/2015/12/11/sorting-players</guid>
  2043.      <description>&lt;p&gt;To drive up user engagement at theScore, we recently introduced an onboarding screen that&amp;#39;s shown when you open the app for the first time. First, you get a list of sports teams that are popular in your area, and the option to subscribe to some of them:&lt;/p&gt;
  2044.  
  2045. &lt;p&gt;&lt;img src=&quot;/assets/follow_teams.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
  2046.  
  2047. &lt;p&gt;Now based on the teams you choose, it would be nice to also recommend some players for you to follow. So how would one go about choosing which players to recommend out of all those teams?&lt;/p&gt;
  2048.  
  2049. &lt;p&gt;Looks like I&amp;#39;m gonna have to sort user content again! &lt;a href=&quot;https://noamswebsite.wordpress.com/2014/01/23/sorting-posts-by-user-engagement-level-with-elasticsearch-implementation/&quot;&gt;last time&lt;/a&gt;, I sorted user posts by &amp;quot;interestingness&amp;quot;; this time around I&amp;#39;ll be sorting players from a set of sports teams. Once again we&amp;#39;ll look at why sorting things based on popularity alone is a bad idea, we&amp;#39;ll get a primer on standard deviation, and finally a bit of scripting to put it all together.&lt;/p&gt;
  2050.  
  2051. &lt;p&gt;Let&amp;#39;s assume we have three six-player teams, where each player has the following number of subscribers:&lt;/p&gt;
  2052. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;big_team_1:
  2053.  
  2054.  1. 100 000
  2055.  2. 110 000
  2056.  3. 90 000
  2057.  4. 80 500
  2058.  5. 140 000
  2059.  6. 140 500
  2060.  
  2061.  
  2062. big_team_2:
  2063.  
  2064.  1. 120 000
  2065.  2. 250 000
  2066.  3. 180 000
  2067.  4. 135 000
  2068.  5. 157 000
  2069.  6. 202 000
  2070.  
  2071.  
  2072. small_team:
  2073.  
  2074.  1. 3 000
  2075.  2. 100
  2076.  3. 234
  2077.  4. 301
  2078.  5. 250
  2079.  6. 400
  2080. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2081. &lt;p&gt;Now let&amp;#39;s consider some properties we want from our algorithm:&lt;/p&gt;
  2082.  
  2083. &lt;ul&gt;
  2084. &lt;li&gt;&lt;p&gt;&lt;strong&gt;A compute-once, static value:&lt;/strong&gt; we don&amp;#39;t want to run our algorithm on every user. We want to give each player in our database a static &lt;code&gt;recommendation_score&lt;/code&gt;; a numeric value that is cheap to index.&lt;/p&gt;&lt;/li&gt;
  2085. &lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple:&lt;/strong&gt; the algorithm should be simple and use elementary methods. Recommending players is a small part of the app; there should be little code maintenance involved.&lt;/p&gt;&lt;/li&gt;
  2086. &lt;li&gt;&lt;p&gt;&lt;strong&gt;Variety:&lt;/strong&gt; The purpose of the onboarding process is to get you engaged, so we want to recommend a variety of players from different sports and leagues.&lt;/p&gt;&lt;/li&gt;
  2087. &lt;/ul&gt;
  2088.  
  2089. &lt;h3&gt;The Naive Approach&lt;/h3&gt;
  2090.  
  2091. &lt;blockquote&gt;
  2092. &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Even though our actual script was written in Ruby, I will be using the &lt;a href=&quot;http://julialang.org/&quot; target=&quot;_blank&quot;&gt;Julia programming language&lt;/a&gt; for all my examples. You can find the complete implementation at the bottom of this post.&lt;/p&gt;
  2093. &lt;/blockquote&gt;
  2094.  
  2095. &lt;p&gt;The first obvious solution is to simply sort players by their &lt;code&gt;subscription_count&lt;/code&gt;. The more popular the player, the more recommendable he is. Here is our naive sorting function:&lt;/p&gt;
  2096. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;naive_sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2097. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  2098. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  2099. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2100. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
  2101. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  2102. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2103. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2104. &lt;p&gt;Which yields:&lt;/p&gt;
  2105. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;5-element Array{PlayerRecommender.Player,1}:
  2106. Big Team 2 Player | subscribers: 250000
  2107. Big Team 2 Player | subscribers: 202000
  2108. Big Team 2 Player | subscribers: 180000
  2109. Big Team 2 Player | subscribers: 157000
  2110. Big Team 1 Player | subscribers: 140500
  2111. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2112. &lt;p&gt;The problem with this approach is that we only get results from the most popular teams. So if you&amp;#39;re a fan of both a very popular NFL team and another team that is not as popular (your local basketball team, perhaps), even the least popular player from the NFL team will be recommended to you, whereas the most popular player from your favorite local basketball team will not show up in your list at all!&lt;/p&gt;
  2113.  
  2114. &lt;h3&gt;A Better Approach&lt;/h3&gt;
  2115.  
  2116. &lt;p&gt;What are we really looking for?&lt;/p&gt;
  2117.  
  2118. &lt;p&gt;Well, I think the players we want to recommend are not necessarily the ones who are most famous, but rather, the ones who are most popular &lt;em&gt;compared to the rest of their team&lt;/em&gt;, regardless of how popular that team is (we already know you&amp;#39;re a fan of the team or you wouldn&amp;#39;t have subscribed to it in the first place).&lt;/p&gt;
  2119.  
  2120. &lt;p&gt;In other words, we want an algorithm that answers the following question:&lt;/p&gt;
  2121.  
  2122. &lt;blockquote&gt;
  2123. &lt;p&gt;Which players &lt;em&gt;deviate&lt;/em&gt; the most in popularity from the rest of their team?&lt;/p&gt;
  2124. &lt;/blockquote&gt;
  2125.  
  2126. &lt;p&gt;Luckily, there&amp;#39;s a mathematical tool for figuring out just this: &lt;a href=&quot;https://en.wikipedia.org/wiki/Standard_deviation&quot; target=&quot;_blank&quot;&gt;standard deviation&lt;/a&gt;.&lt;/p&gt;
  2127.  
  2128. &lt;p&gt;&lt;hr&gt;&lt;/p&gt;
  2129.  
  2130. &lt;h4&gt;Standard Deviation tl;dr&lt;/h4&gt;
  2131.  
  2132. &lt;p&gt;Standard deviation is a pretty straight-forward concept: Take a set of values, and figure out the average value for that set (also known as the &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Arithmetic_mean&quot; target=&quot;_blank&quot;&gt;arithmetic mean&lt;/a&gt;&lt;/em&gt;). The standard deviation simply tells us by how much the value of the typical element in our set deviates from the average.&lt;/p&gt;
  2133.  
  2134. &lt;p&gt;The mathematical representation of this calculation is:&lt;/p&gt;
  2135.  
  2136. &lt;p&gt;$$ s_N = \sqrt{\frac{1}{N} \sum_{i=1}^{N} (x_i - \overline{x})^2} $$&lt;/p&gt;
  2137.  
  2138. &lt;p&gt;Where \(N\) is the population size, and \(\overline{x}\) is the arithmetic mean, which itself is represented by:&lt;/p&gt;
  2139.  
  2140. &lt;p&gt;$$ \overline{x}_N=\frac{1}{N}\sum_{i=1}^{N} a_i $$&lt;/p&gt;
  2141.  
  2142. &lt;p&gt;For example, the following two sets have the same average value, but clearly the values are spread out differently:&lt;/p&gt;
  2143. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close_to_average&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  2144. &lt;span class=&quot;n&quot;&gt;spread_out&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  2145.  
  2146. &lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close_to_average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
  2147. &lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spread_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
  2148.  
  2149. &lt;span class=&quot;n&quot;&gt;standard_deviation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close_to_average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.59&lt;/span&gt;
  2150. &lt;span class=&quot;n&quot;&gt;standard_deviation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spread_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;9.51&lt;/span&gt;
  2151. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2152. &lt;p&gt;&lt;hr&gt;&lt;/p&gt;
  2153.  
  2154. &lt;p&gt;Thus we arrive at our simple scoring function. All we need to do is find players whose deviation from the average is substantially higher than that of their teammates:&lt;/p&gt;
  2155. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2156. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;
  2157. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  2158. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  2159. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  2160. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;
  2161. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2162. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2163. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2164. &lt;p&gt;Using this scoring function on our original teams, we get the following results:&lt;/p&gt;
  2165. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;5-element Array{PlayerRecommender.Player,1}:
  2166. Small Team Player | subscribers: 3000 | score: 2.2276261544470644
  2167. Big Team 2 Player | subscribers: 250000 | score: 1.749555170961297
  2168. Big Team 1 Player | subscribers: 140500 | score: 1.3134034577576315
  2169. Big Team 1 Player | subscribers: 140000 | score: 1.291753950212176
  2170. Big Team 2 Player | subscribers: 202000 | score: 0.6445729577225832
  2171. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2172. &lt;p&gt;Much better. This time around, our top player actually has the least number of subscribers, but this makes sense, because even though he belongs to a team that is not very popular, his subscription count is tenfold that of his teammates; clearly someone to keep an eye on! (Perhaps a rising star in a college league? Certainly wouldn&amp;#39;t want our recommendation script to ignore that one.)&lt;/p&gt;
  2173.  
  2174. &lt;h3&gt;Limitations&lt;/h3&gt;
  2175.  
  2176. &lt;p&gt;This algorithm has many limitations.&lt;/p&gt;
  2177.  
  2178. &lt;ul&gt;
  2179. &lt;li&gt;&lt;p&gt;New players won&amp;#39;t be very well represented (they will by nature have low subscription counts).&lt;/p&gt;&lt;/li&gt;
  2180. &lt;li&gt;&lt;p&gt;All-star teams might result in nobody being particularly recommendable. Though this one might be less of a problem: thanks to our good ol&amp;#39; friend the &lt;a href=&quot;https://en.wikipedia.org/wiki/Gaussian_function&quot;&gt;bell curve&lt;/a&gt;, even among rare anomalies, there are &lt;a href=&quot;http://fivethirtyeight.com/features/lionel-messi-is-impossible/&quot; target=&quot;_blank&quot;&gt;rare anomalies&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
  2181. &lt;/ul&gt;
  2182.  
  2183. &lt;p&gt;While there are ways to address these limitations and improve the accuracy of the algorithm (for example, taking into account the rate of change in &lt;code&gt;subscription_count&lt;/code&gt;), one has to remember the purpose of this feature: to drive up user engagement during onboarding. Is the added complexity of such changes worth the minimal improvement in the recommendations?&lt;/p&gt;
  2184.  
  2185. &lt;p&gt;Point is, it&amp;#39;s Friday night and I should go out for a beer now. I&amp;#39;m also looking forward to testing out the enormous Chinese fermentation jug I bought yesterday. It looks something like this, but a LOT bigger:&lt;/p&gt;
  2186.  
  2187. &lt;p&gt;&lt;img src=&quot;/assets/ferm_crock.jpeg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
  2188.  
  2189. &lt;p&gt;And it was only $30. What a bargain.&lt;/p&gt;
  2190.  
  2191. &lt;p&gt;&lt;hr /&gt;&lt;/p&gt;
  2192.  
  2193. &lt;p&gt;Here is the code used in these examples (working as of Julia 0.4.1). Our actual code at theScore is in Ruby.&lt;/p&gt;
  2194. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-julia&quot; data-lang=&quot;julia&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PlayerRecommender&lt;/span&gt;
  2195. &lt;span class=&quot;k&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;big_team_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;big_team_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;small_team&lt;/span&gt;
  2196. &lt;span class=&quot;k&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;naive_sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;
  2197.  
  2198. &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;
  2199. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
  2200. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AbstractString&lt;/span&gt;
  2201. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Float64&lt;/span&gt;
  2202.  
  2203. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2204. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2205. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2206. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2207.  
  2208. &lt;span class=&quot;c&quot;&gt;# Pretty print Player&lt;/span&gt;
  2209. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2210. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; | subscribers: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; | score: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2211. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2212.  
  2213. &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Team&lt;/span&gt;
  2214. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2215. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Float64&lt;/span&gt;
  2216. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Float64&lt;/span&gt;
  2217. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AbstractString&lt;/span&gt;
  2218.  
  2219. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2220. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;
  2221. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
  2222. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2223.  
  2224. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2225. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2226. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2227.  
  2228.  
  2229. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;naive_sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2230. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  2231. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  2232. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2233. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
  2234. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  2235. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2236.  
  2237. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2238. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_subscriptions_avg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2239. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2240. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2241.  
  2242. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  2243. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  2244. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2245. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
  2246. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  2247. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2248.  
  2249. &lt;span class=&quot;c&quot;&gt;# Private&lt;/span&gt;
  2250.  
  2251. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2252. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2253. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2254.  
  2255. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev_from_average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2256. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2257. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2258.  
  2259. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2260. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2261. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2262.  
  2263. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2264. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;
  2265. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  2266. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  2267. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  2268. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation_score&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;
  2269. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2270. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2271.  
  2272. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_subscriptions_avg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2273. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  2274. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2275.  
  2276. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;squares&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2277. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2278. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2279.  
  2280. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2281. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2282. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2283.  
  2284. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2285. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;squares&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev_from_average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  2286. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2287.  
  2288. &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscriptions_avg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2289. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
  2290. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2291. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2292. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
  2293.    </item>
  2294.    
  2295.    
  2296.    
  2297.    <item>
  2298.      <title>Jbuilder to AMS - A tested journey</title>
  2299.      <link>https://techblog.thescore.com/2015/11/27/jbuilder-to-ams-a-tested-journey/</link>
  2300.      <pubDate>Fri, 27 Nov 2015 00:00:00 +0000</pubDate>
  2301.      <author></author>
  2302.      <guid>https://techblog.thescore.com/2015/11/27/jbuilder-to-ams-a-tested-journey</guid>
  2303.      <description>&lt;p&gt;&lt;a href=&quot;https://blog.codeship.com/building-a-json-api-with-rails-5/&quot;&gt;Rails 5&lt;/a&gt; is coming and ships with the &lt;code&gt;--api&lt;/code&gt; option. This will generate a fresh but slimmed down Rails app whose job is to serve as a JSON API. With this option you&amp;#39;ll get &lt;a href=&quot;https://github.com/rails-api/active_model_serializers&quot;&gt;Active Model Serializers - AMS&lt;/a&gt; installed by default. This is a great library that is only getting better with all of the work that the AMS is doing on it.&lt;/p&gt;
  2304.  
  2305. &lt;p&gt;Not everyone is creating a new Rails app though... most of us have existing ones, which may use RABL, Jbuilder, or a number of other options to generate JSON responses.&lt;/p&gt;
  2306.  
  2307. &lt;h2&gt;Knock knock, who&amp;#39;s there? Legacy code.&lt;/h2&gt;
  2308.  
  2309. &lt;p&gt;Let&amp;#39;s say we have a JSON API written in &lt;a href=&quot;https://github.com/rails/jbuilder&quot;&gt;Jbuilder&lt;/a&gt; (or RABL) and we want to switch it over to using AMS... what&amp;#39;s the first step? And what about after that? In this article I&amp;#39;ll be walking us through converting our app to use AMS through a series of steps to make sure that the actual JSON response doesn&amp;#39;t change.&lt;/p&gt;
  2310.  
  2311. &lt;h2&gt;Our App&lt;/h2&gt;
  2312.  
  2313. &lt;p&gt;Our app for this demo will consist of 3 models: League, Team, Player. We&amp;#39;ll be looking at the &lt;code&gt;teams#show&lt;/code&gt; endpoint which includes information about the team, the league it is apart of, along with a list of that team&amp;#39;s players.&lt;/p&gt;
  2314.  
  2315. &lt;p&gt;Our &lt;code&gt;Team&lt;/code&gt; model looks like this:&lt;/p&gt;
  2316. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  2317. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:league&lt;/span&gt;
  2318. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:players&lt;/span&gt;
  2319. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2320. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2321. &lt;p&gt;Our controller is pretty bare bones right now but it does the trick:&lt;/p&gt;
  2322. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TeamsController&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  2323. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;
  2324. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2325. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2326. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2327. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2328. &lt;p&gt;Now that we&amp;#39;ve got the model and controller out of the way, let&amp;#39;s visit the path &lt;code&gt;/teams/1&lt;/code&gt; to take a look at the JSON that is produced:&lt;/p&gt;
  2329. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2330. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2331. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto FC&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2332. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;updated_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2015-11-27T19:01:49.846Z&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2333. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;players&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  2334. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2335. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2336. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Sebastian Giovinco&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2337. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;position&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Forward&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2338. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;nickname&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Atomic Ant&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2339. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;jersey_number&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2340. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;nationality&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Italian&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2341. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;updated_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2015-11-27T19:01:49.852Z&amp;quot;&lt;/span&gt;
  2342. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  2343. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2344. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2345. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Sebastian Giovinco&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2346. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;position&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Forward&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2347. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;nickname&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Atomic Ant&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2348. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;jersey_number&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2349. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;nationality&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Italian&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2350. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;updated_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2015-11-27T19:01:49.853Z&amp;quot;&lt;/span&gt;
  2351. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2352. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  2353. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;league&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2354. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2355. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;MLS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2356. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;slug&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;mls&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2357. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;updated_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2015-11-27T19:01:49.835Z&amp;quot;&lt;/span&gt;
  2358. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2359. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2360. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2361. &lt;p&gt;Yes there are 2 Giovincos... it&amp;#39;s a test app! Here is what our current Jbuilder view looks like to generate that response:&lt;/p&gt;
  2362. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:updated_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2363.  
  2364. &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;league&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2365. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;league&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
  2366. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;league&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
  2367. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;league&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;
  2368. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;league&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated_at&lt;/span&gt;
  2369. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2370.  
  2371. &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2372. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:nationality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:nickname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:jersey_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:updated_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2373. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2374. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2375. &lt;h2&gt;Testing the endpoints&lt;/h2&gt;
  2376.  
  2377. &lt;p&gt;There will be some serious refactoring involved to migrate from Jbuilder to AMS.&lt;/p&gt;
  2378.  
  2379. &lt;blockquote&gt;
  2380. &lt;p&gt;Code refactoring is the process of restructuring existing computer code - changing the factoring - without changing its external behavior.&lt;/p&gt;
  2381. &lt;/blockquote&gt;
  2382.  
  2383. &lt;p&gt;According to that definition that I took off of Wikipedia, refactoring involves changing the code, but not the external behavour. So what is the external behaviour in the case of an endpoint? It is the JSON that the endpoint returns. You could also argue that the speed in which it generates that JSON is also its external behaviour, but for today we&amp;#39;ll be focusing on just making sure the JSON remains the same.&lt;/p&gt;
  2384.  
  2385. &lt;p&gt;To do this we&amp;#39;ll write some tests! There are many ways to ensure that our JSON responses remain the same, but this is a similar approach to how we&amp;#39;ve been testing our endpoints at theScore. What we&amp;#39;ll do is hit the endpoint, record the result, and from then on test to ensure that what the API is returning is equal to the saved response we have.&lt;/p&gt;
  2386.  
  2387. &lt;p&gt;Our test will create a few database objects, call the endpoint, and then verify the response:&lt;/p&gt;
  2388. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;rails_helper&amp;#39;&lt;/span&gt;
  2389.  
  2390. &lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TeamsController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2391. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;returns correct output for single team&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2392. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2393. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2394. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2395.  
  2396. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verify_endpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/teams/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  2397. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2398. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2399. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2400. &lt;p&gt;The &lt;code&gt;verify_endpoint&lt;/code&gt; method isn&amp;#39;t one that comes with rspec... it comes from a small module I wrote to help automate recording the endpoint responses and comparing pre-recorded responses with the current response.&lt;/p&gt;
  2401. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;RequestHelpers&lt;/span&gt;
  2402. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FILTERED_FIELDS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;updated_at&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;freeze&lt;/span&gt;
  2403. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CAPTURE_BASE_PATH&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;spec/captures&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;freeze&lt;/span&gt;
  2404.  
  2405. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;verify_endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2406. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
  2407. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;
  2408. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_expected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_or_write_capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2409. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compare_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2410. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2411.  
  2412. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;
  2413.  
  2414. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read_or_write_capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2415. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parameterize&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.json&amp;quot;&lt;/span&gt;
  2416. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  2417. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2418. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Errno&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENOENT&lt;/span&gt;
  2419. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write_capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2420. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2421. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2422.  
  2423. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read_capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2424. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CAPTURE_BASE_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  2425. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2426.  
  2427. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write_capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2428. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_body&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2429. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pretty_generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pretty_json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2430. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CAPTURE_BASE_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pretty_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2431. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2432. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2433.  
  2434. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2435. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_body&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  2436. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_expected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  2437. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2438. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2439.  
  2440. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Remove filtered fields and order keys to make comparisons easier&lt;/span&gt;
  2441. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2442. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each_with_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hsh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2443. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  2444.  
  2445. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hsh&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2446. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2447. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2448. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2449. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2450. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2451. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FILTERED_FIELDS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2452. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:filtered&lt;/span&gt;
  2453. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  2454. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
  2455. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2456. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2457. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2458. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2459. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2460. &lt;p&gt;The most complicated method here is the &lt;code&gt;filter&lt;/code&gt; method, whose job is basically to filter out fields which will end up changing from test to test, like the &lt;code&gt;updated_at&lt;/code&gt; field.&lt;/p&gt;
  2461.  
  2462. &lt;p&gt;Just to make sure I&amp;#39;m explaining all the code in this example, I&amp;#39;m using &lt;a href=&quot;https://github.com/thoughtbot/factory_girl&quot;&gt;FactoryGirl&lt;/a&gt; to generate the data used in this test. The factories look like this:&lt;/p&gt;
  2463. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FactoryGirl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2464. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:league&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2465. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;MLS&amp;#39;&lt;/span&gt;
  2466. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;mls&amp;#39;&lt;/span&gt;
  2467. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2468. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2469.  
  2470. &lt;span class=&quot;no&quot;&gt;FactoryGirl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2471. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:player&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2472. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Sebastian Giovinco&amp;#39;&lt;/span&gt;
  2473. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Atomic Ant&amp;#39;&lt;/span&gt;
  2474. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jersey_number&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;10&amp;#39;&lt;/span&gt;
  2475. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nationality&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Italian&amp;#39;&lt;/span&gt;
  2476. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Forward&amp;#39;&lt;/span&gt;
  2477. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;
  2478. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2479. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2480.  
  2481. &lt;span class=&quot;no&quot;&gt;FactoryGirl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2482. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2483. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Toronto FC&amp;#39;&lt;/span&gt;
  2484. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;league&lt;/span&gt;
  2485. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2486. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2487. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2488. &lt;h2&gt;Time for AMS&lt;/h2&gt;
  2489.  
  2490. &lt;p&gt;Now that we can verify the responses that our endpoints give, we can create AMS classes. Here are the 3 serializers I have, which &lt;strong&gt;should&lt;/strong&gt; produce the same JSON result.&lt;/p&gt;
  2491. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;LeagueSerializer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveModel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serializer&lt;/span&gt;
  2492. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2493. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2494. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:updated_at&lt;/span&gt;
  2495. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2496.  
  2497. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;PlayerSerializer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveModel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serializer&lt;/span&gt;
  2498. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2499. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2500. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2501. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:nickname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2502. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:jersey_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2503. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:nationality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2504. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:updated_at&lt;/span&gt;
  2505. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2506.  
  2507. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TeamSerializer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveModel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serializer&lt;/span&gt;
  2508. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2509. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2510. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:updated_at&lt;/span&gt;
  2511. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:players&lt;/span&gt;
  2512. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:league&lt;/span&gt;
  2513. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2514. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2515. &lt;p&gt;We&amp;#39;ll have to make a small change to the controller so that it uses the AMS classes instead of attempting to find a view.&lt;/p&gt;
  2516. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TeamsController&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  2517. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;
  2518. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2519. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;serializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TeamSerializer&lt;/span&gt;
  2520. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2521. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2522. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2523. &lt;p&gt;Now it&amp;#39;s time to run the test &lt;code&gt;bundle exec rspec&lt;/code&gt;, and to verify that they continue to pass after making our change. It&amp;#39;s easy to forget a field... so let&amp;#39;s leave one out on purpose just to make sure that the tests are working as expected. Plus there is something quite satisfying about making the test turn from red to green.&lt;/p&gt;
  2524. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-&amp;quot;league&amp;quot; =&amp;gt; {&amp;quot;id&amp;quot;=&amp;gt;1, &amp;quot;name&amp;quot;=&amp;gt;&amp;quot;MLS&amp;quot;, &amp;quot;slug&amp;quot;=&amp;gt;&amp;quot;mls&amp;quot;, &amp;quot;updated_at&amp;quot;=&amp;gt;:filtered},
  2525. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;+&amp;quot;league&amp;quot; =&amp;gt; {&amp;quot;id&amp;quot;=&amp;gt;1, &amp;quot;name&amp;quot;=&amp;gt;&amp;quot;MLS&amp;quot;, &amp;quot;updated_at&amp;quot;=&amp;gt;:filtered},
  2526. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2527. &lt;p&gt;We can see that we forgot the &lt;code&gt;slug&lt;/code&gt; field on the league... so we&amp;#39;ll add that to the &lt;code&gt;LeagueSerializer&lt;/code&gt; and now the test is passing again.&lt;/p&gt;
  2528.  
  2529. &lt;h2&gt;Preparing for failure&lt;/h2&gt;
  2530.  
  2531. &lt;p&gt;We now basically have 2 working ways to generate the JSON in our API... we will eventually get rid of the Jbuilder code but for now let&amp;#39;s leave it. If AMS code gives us problems that we didn&amp;#39;t plan for (performance for example), all it takes is changing our controller to switch back to the previous version while we work out the kinks. After it&amp;#39;s been running in production for a few days and you&amp;#39;re sure there are no issues, feel free to go back and remove the Jbuilder view that is no longer needed.&lt;/p&gt;
  2532.  
  2533. &lt;h2&gt;Concluding thoughts&lt;/h2&gt;
  2534.  
  2535. &lt;p&gt;In this post we changed our code from using Jbuilder to AMS, but that wasn&amp;#39;t really the main point. The main point I wanted to get across was both how to test your JSON endpoints, but also how to approach code refactoring in general. Without tests, how do you know the code you refactored still works as expected?&lt;/p&gt;
  2536.  
  2537. &lt;p&gt;You can get the entire source code used in this article at &lt;a href=&quot;https://github.com/leighhalliday/soccer_app&quot;&gt;https://github.com/leighhalliday/soccer_app&lt;/a&gt;&lt;/p&gt;
  2538. </description>
  2539.    </item>
  2540.    
  2541.    
  2542.    
  2543.    <item>
  2544.      <title>Go – from playing to production</title>
  2545.      <link>https://techblog.thescore.com/2015/11/09/go-from-playing-to-production/</link>
  2546.      <pubDate>Mon, 09 Nov 2015 00:00:00 +0000</pubDate>
  2547.      <author></author>
  2548.      <guid>https://techblog.thescore.com/2015/11/09/go-from-playing-to-production</guid>
  2549.      <description>&lt;h2&gt;Proposition&lt;/h2&gt;
  2550.  
  2551. &lt;ul&gt;
  2552. &lt;li&gt;There are so many new languages popping up that it&amp;#39;s hard to keep up with all of them.&lt;/li&gt;
  2553. &lt;li&gt;It&amp;#39;s nice to be able to quickly evaluate a language by prototyping a small project or two without investing too much time learning it.&lt;/li&gt;
  2554. &lt;li&gt;Accustomisation to a certain setup/workflow; for example, being able to describe and install dependencies (like Ruby&amp;#39;s &lt;a href=&quot;http://bundler.io/gemfile.html&quot;&gt;Gemfile&lt;/a&gt; for &lt;a href=&quot;http://bundler.io&quot;&gt;Bundler&lt;/a&gt;)&lt;/li&gt;
  2555. &lt;li&gt;Start simple, evolve to production.&lt;/li&gt;
  2556. &lt;/ul&gt;
  2557.  
  2558. &lt;p&gt;If the above holds for you then here&amp;#39;s a practical guide on how to get started with &lt;a href=&quot;http://golang.org&quot;&gt;Go&lt;/a&gt;.&lt;/p&gt;
  2559.  
  2560. &lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: this is an opinionated approach.&lt;/p&gt;
  2561.  
  2562. &lt;h2&gt;1. Play&lt;/h2&gt;
  2563.  
  2564. &lt;p&gt;&lt;a href=&quot;https://play.golang.org/&quot;&gt;Play web interface&lt;/a&gt; offers the fastest way to experiment with &lt;a href=&quot;http://golang.org&quot;&gt;Go&lt;/a&gt;.
  2565. It lets you run commands and save your gist for sharing and later use. Useful for both beginners and experienced users.&lt;/p&gt;
  2566.  
  2567. &lt;h4&gt;Exercises&lt;/h4&gt;
  2568.  
  2569. &lt;ul&gt;
  2570. &lt;li&gt;go to the &lt;a href=&quot;https://play.golang.org/&quot;&gt;Play web interface&lt;/a&gt;&lt;/li&gt;
  2571. &lt;li&gt;press &lt;code&gt;Run&lt;/code&gt; to execute&lt;/li&gt;
  2572. &lt;li&gt;press &lt;code&gt;Share&lt;/code&gt; to save your code&lt;/li&gt;
  2573. &lt;/ul&gt;
  2574.  
  2575. &lt;h2&gt;2. Tour&lt;/h2&gt;
  2576.  
  2577. &lt;p&gt;&lt;a href=&quot;https://tour.golang.org&quot;&gt;Tour&lt;/a&gt; walks you through exercises describing facets of &lt;a href=&quot;http://golang.org&quot;&gt;Go&lt;/a&gt;.
  2578. Offers solid starting point for a learner.&lt;/p&gt;
  2579.  
  2580. &lt;h4&gt;Exercises&lt;/h4&gt;
  2581.  
  2582. &lt;ul&gt;
  2583. &lt;li&gt;complete &lt;a href=&quot;https://tour.golang.org&quot;&gt;Tour&lt;/a&gt; &amp;nbsp; (at your convenience)&lt;/li&gt;
  2584. &lt;/ul&gt;
  2585.  
  2586. &lt;h2&gt;3. &lt;code&gt;go run&lt;/code&gt;&lt;/h2&gt;
  2587.  
  2588. &lt;p&gt;Getting the language running on your machine is the next step once the above options are no longer satisfying.&lt;/p&gt;
  2589.  
  2590. &lt;p&gt;Given the content of &lt;code&gt;hello_world.go&lt;/code&gt;:&lt;/p&gt;
  2591. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// hello_world.go&lt;/span&gt;
  2592. &lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;
  2593. &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
  2594. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2595. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Hello go world!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2596. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2597. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2598. &lt;p&gt;Continue with exercises, but before that make sure to clone the &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production&quot;&gt;example repo&lt;/a&gt;&lt;/p&gt;
  2599.  
  2600. &lt;h4&gt;Exercises&lt;/h4&gt;
  2601.  
  2602. &lt;ul&gt;
  2603. &lt;li&gt;&lt;a href=&quot;https://golang.org/doc/install&quot;&gt;install Go&lt;/a&gt;&lt;/li&gt;
  2604. &lt;li&gt;&lt;code&gt;export GOPATH=~/golang &amp;amp;&amp;amp; export PATH=$PATH:$GOPATH/bin &amp;amp;&amp;amp; mkdir $GOPATH&lt;/code&gt; set up required environment&lt;/li&gt;
  2605. &lt;li&gt;&lt;code&gt;cd 03-go-run/&lt;/code&gt; &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/tree/e494e7483972971ea2b60ac0791430e6913f9f63/03-go-run&quot;&gt;(code)&lt;/a&gt;&lt;/li&gt;
  2606. &lt;li&gt;run &lt;code&gt;go run hello_world.go&lt;/code&gt; to get the &lt;code&gt;&amp;quot;hello go world&amp;quot;&lt;/code&gt; output&lt;/li&gt;
  2607. &lt;li&gt;run &lt;code&gt;go fmt hello_world.go&lt;/code&gt; to auto-format the code&lt;/li&gt;
  2608. &lt;/ul&gt;
  2609.  
  2610. &lt;h2&gt;4. &lt;code&gt;go test&lt;/code&gt;&lt;/h2&gt;
  2611.  
  2612. &lt;p&gt;Tests help evolve code and ensure it works.
  2613. So in order to test that the &lt;code&gt;hello&lt;/code&gt; string is generated properly let&amp;#39;s refactor the example and add a test.&lt;/p&gt;
  2614. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;
  2615.  
  2616. &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
  2617.  
  2618. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2619. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;go&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  2620. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2621.  
  2622. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2623. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;hello %s world&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2624. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2625. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2626. &lt;p&gt;and test&lt;/p&gt;
  2627. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;
  2628.  
  2629. &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;testing&amp;quot;&lt;/span&gt;
  2630.  
  2631. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TestHello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2632. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;hello go world&amp;quot;&lt;/span&gt;
  2633. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;got&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Go&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2634.  
  2635. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;got&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2636. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;\nExp: %s\nGot: %s&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;got&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2637. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2638. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2639. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2640. &lt;h4&gt;Exercises&lt;/h4&gt;
  2641.  
  2642. &lt;ul&gt;
  2643. &lt;li&gt;&lt;code&gt;cd 04-go-test&lt;/code&gt; &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/tree/e494e7483972971ea2b60ac0791430e6913f9f63/04-go-test&quot;&gt;(code)&lt;/a&gt;&lt;/li&gt;
  2644. &lt;li&gt;&lt;code&gt;go fmt&lt;/code&gt; both files&lt;/li&gt;
  2645. &lt;li&gt;run &lt;code&gt;go test&lt;/code&gt;, notice the failing test&lt;/li&gt;
  2646. &lt;li&gt;do not fix the test yet&lt;/li&gt;
  2647. &lt;/ul&gt;
  2648.  
  2649. &lt;p&gt;Failing test output:&lt;/p&gt;
  2650. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;--- FAIL: TestHello (0.00s)&lt;/span&gt;
  2651. &lt;span class=&quot;go&quot;&gt;        hello_world_test.go:11:&lt;/span&gt;
  2652. &lt;span class=&quot;go&quot;&gt;                Exp: hello go world&lt;/span&gt;
  2653. &lt;span class=&quot;go&quot;&gt;                Got: hello Go world&lt;/span&gt;
  2654. &lt;span class=&quot;go&quot;&gt;FAIL&lt;/span&gt;
  2655. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2656. &lt;h2&gt;5. Continuous testing and Makefile&lt;/h2&gt;
  2657.  
  2658. &lt;p&gt;&lt;code&gt;Makefile&lt;/code&gt; lets you describe automation for routine tasks and save some typing.
  2659. In this case we want to have project rebuilt every time a file changes.&lt;/p&gt;
  2660. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-make&quot; data-lang=&quot;make&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  2661. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;go&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
  2662.  
  2663. &lt;span class=&quot;nf&quot;&gt;re-test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  2664. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;reflex&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-r&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;\.go&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;MAKE&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
  2665.  
  2666. &lt;span class=&quot;nf&quot;&gt;dev-deps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  2667. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;go&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;get&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;github.com/cespare/reflex
  2668. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2669. &lt;h4&gt;Exercise:&lt;/h4&gt;
  2670.  
  2671. &lt;ul&gt;
  2672. &lt;li&gt;&lt;code&gt;cd 05-ContinuousTestingAndMakefile&lt;/code&gt; &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/tree/e494e7483972971ea2b60ac0791430e6913f9f63/05-ContinuousTestingAndMakefile&quot;&gt;(code)&lt;/a&gt;&lt;/li&gt;
  2673. &lt;li&gt;run &lt;code&gt;make dev-deps&lt;/code&gt; to install development dependencies&lt;/li&gt;
  2674. &lt;li&gt;run &lt;code&gt;make re-test&lt;/code&gt; and watch the test fail&lt;/li&gt;
  2675. &lt;li&gt;fix the failing test and see it &lt;code&gt;PASS&lt;/code&gt; immediately!&lt;/li&gt;
  2676. &lt;/ul&gt;
  2677.  
  2678. &lt;h2&gt;6. Packages, Project Layout&lt;/h2&gt;
  2679.  
  2680. &lt;p&gt;To demonstrate use of packages and basic project layout, our &lt;code&gt;helloworld&lt;/code&gt; will be converted into a library.&lt;/p&gt;
  2681.  
  2682. &lt;p&gt;NOTE: Currently there&amp;#39;s no official and convenient way to manage dependencies.
  2683. &lt;a href=&quot;https://getgb.io&quot;&gt;Gb&lt;/a&gt; was chosen as a tool to govern project layout, building and handling dependencies.&lt;/p&gt;
  2684.  
  2685. &lt;p&gt;&lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/tree/e494e7483972971ea2b60ac0791430e6913f9f63/06-PackagesAndProjectLayout&quot;&gt;New layout&lt;/a&gt; now looks like this:&lt;/p&gt;
  2686. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;tree&lt;/span&gt;
  2687. &lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
  2688. &lt;span class=&quot;go&quot;&gt;├── Makefile&lt;/span&gt;
  2689. &lt;span class=&quot;go&quot;&gt;├── bin&lt;/span&gt;
  2690. &lt;span class=&quot;go&quot;&gt;│   ├── helloworld&lt;/span&gt;
  2691. &lt;span class=&quot;go&quot;&gt;│   └── helloworld-server&lt;/span&gt;
  2692. &lt;span class=&quot;go&quot;&gt;└── src&lt;/span&gt;
  2693. &lt;span class=&quot;go&quot;&gt;    ├── cmd&lt;/span&gt;
  2694. &lt;span class=&quot;go&quot;&gt;    │   ├── helloworld&lt;/span&gt;
  2695. &lt;span class=&quot;go&quot;&gt;    │   │   └── main.go&lt;/span&gt;
  2696. &lt;span class=&quot;go&quot;&gt;    │   └── helloworld-server&lt;/span&gt;
  2697. &lt;span class=&quot;go&quot;&gt;    │       └── main.go&lt;/span&gt;
  2698. &lt;span class=&quot;go&quot;&gt;    └── helloworld&lt;/span&gt;
  2699. &lt;span class=&quot;go&quot;&gt;        ├── hello_world.go&lt;/span&gt;
  2700. &lt;span class=&quot;go&quot;&gt;        └── hello_world_test.go&lt;/span&gt;
  2701. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2702. &lt;h4&gt;Changes:&lt;/h4&gt;
  2703.  
  2704. &lt;ul&gt;
  2705. &lt;li&gt;the &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/e494e7483972971ea2b60ac0791430e6913f9f63/05-ContinuousTestingAndMakefile/hello_world.go#L10-L12&quot;&gt;&lt;code&gt;hello&lt;/code&gt; function&lt;/a&gt; has been renamed to &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/e494e7483972971ea2b60ac0791430e6913f9f63/06-PackagesAndProjectLayout/src/helloworld/hello_world.go#L6-L8&quot;&gt;&lt;code&gt;Hello&lt;/code&gt; to be exported&lt;/a&gt; and accessible from outside the package&lt;/li&gt;
  2706. &lt;li&gt;&lt;code&gt;helloworld&lt;/code&gt; is now a package (&lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/e494e7483972971ea2b60ac0791430e6913f9f63/06-PackagesAndProjectLayout/src/helloworld/hello_world.go#L2&quot;&gt;note &lt;code&gt;package helloworld&lt;/code&gt;&lt;/a&gt; instead &lt;code&gt;package main&lt;/code&gt; in &lt;code&gt;src/helloworld/*.go&lt;/code&gt;)&lt;/li&gt;
  2707. &lt;li&gt;there are now separate &amp;quot;entry points&amp;quot; (or executables) source of which located under &lt;code&gt;src/cmd&lt;/code&gt;&lt;/li&gt;
  2708. &lt;li&gt;&lt;code&gt;Makefile&lt;/code&gt; now has a &lt;code&gt;build&lt;/code&gt; rule that builds the entry points/executables to &lt;code&gt;bin&lt;/code&gt; directory&lt;/li&gt;
  2709. &lt;li&gt;&lt;a href=&quot;https://getgb.io&quot;&gt;Gb&lt;/a&gt; is our new &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/128a56179c6a51a79a069e1e77a0418d16a366a1/06-PackagesAndProjectLayout/Makefile#L14&quot;&gt;development dependency&lt;/a&gt;&lt;/li&gt;
  2710. &lt;/ul&gt;
  2711.  
  2712. &lt;h4&gt;Exercise:&lt;/h4&gt;
  2713.  
  2714. &lt;ul&gt;
  2715. &lt;li&gt;&lt;code&gt;cd 06-PackagesAndProjectLayout&lt;/code&gt; &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/tree/e494e7483972971ea2b60ac0791430e6913f9f63/06-PackagesAndProjectLayout&quot;&gt;(code)&lt;/a&gt;&lt;/li&gt;
  2716. &lt;li&gt;run &lt;code&gt;make dev-deps&lt;/code&gt; to install new dependencies&lt;/li&gt;
  2717. &lt;li&gt;run &lt;code&gt;make re-test&lt;/code&gt;, notice failing test&lt;/li&gt;
  2718. &lt;li&gt;fix the failing test&lt;/li&gt;
  2719. &lt;li&gt;run &lt;code&gt;make build&lt;/code&gt; to get binaries built&lt;/li&gt;
  2720. &lt;li&gt;run &lt;code&gt;bin/helloworld&lt;/code&gt; ensure it prints &lt;code&gt;hello go world&lt;/code&gt;&lt;/li&gt;
  2721. &lt;li&gt;run &lt;code&gt;bin/helloworld-server&lt;/code&gt; and point your browser to &lt;a href=&quot;http://localhost:8080/?name=Go&quot;&gt;http://localhost:8080/?name=Go&lt;/a&gt; to get a web &amp;quot;hello Go world&amp;quot;&lt;/li&gt;
  2722. &lt;/ul&gt;
  2723.  
  2724. &lt;h2&gt;7. Dependencies&lt;/h2&gt;
  2725.  
  2726. &lt;p&gt;Let&amp;#39;s now add a dependency and describe the process.
  2727. In this example we&amp;#39;re going to use &lt;a href=&quot;https://github.com/julienschmidt/httprouter&quot;&gt;httprouter&lt;/a&gt; to add a &lt;em&gt;nicer&lt;/em&gt; route for our web server hello example.&lt;/p&gt;
  2728.  
  2729. &lt;p&gt;Adding a dependency with &lt;a href=&quot;https://getgb.io&quot;&gt;Gb&lt;/a&gt; is running a command:&lt;/p&gt;
  2730. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;gb vendor fetch github.com/julienschmidt/httprouter&lt;/span&gt;
  2731. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2732. &lt;p&gt;As result we now have &lt;code&gt;vendor/&lt;/code&gt; directory that looks like this:&lt;/p&gt;
  2733. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;tree -L 4 vendor/&lt;/span&gt;
  2734. &lt;span class=&quot;go&quot;&gt;vendor/&lt;/span&gt;
  2735. &lt;span class=&quot;go&quot;&gt;├── manifest&lt;/span&gt;
  2736. &lt;span class=&quot;go&quot;&gt;└── src&lt;/span&gt;
  2737. &lt;span class=&quot;go&quot;&gt;    └── github.com&lt;/span&gt;
  2738. &lt;span class=&quot;go&quot;&gt;        └── julienschmidt&lt;/span&gt;
  2739. &lt;span class=&quot;go&quot;&gt;            └── httprouter&lt;/span&gt;
  2740. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2741. &lt;p&gt;where &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/e494e7483972971ea2b60ac0791430e6913f9f63/07-Dependencies/vendor/manifest&quot;&gt;&lt;code&gt;vendor/manifest&lt;/code&gt;&lt;/a&gt; is the file which tracks dependencies added with &lt;code&gt;gb vendor fetch&lt;/code&gt; command.
  2742. Check &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/e494e7483972971ea2b60ac0791430e6913f9f63/07-Dependencies/vendor/manifest&quot;&gt;&lt;code&gt;vendor/manifest&lt;/code&gt;&lt;/a&gt; into your &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_control&quot;&gt;SCM&lt;/a&gt; to be able to retrieve dependencies later with &lt;code&gt;gb vendor restore&lt;/code&gt; (without having to check them in to &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_control&quot;&gt;SCM&lt;/a&gt;).&lt;/p&gt;
  2743.  
  2744. &lt;p&gt;Now that that dependency is ready to be used let&amp;#39;s update the &lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production/blob/e494e7483972971ea2b60ac0791430e6913f9f63/07-Dependencies/src/cmd/helloworld-server/main.go&quot;&gt;server code&lt;/a&gt; to look like:&lt;/p&gt;
  2745. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;
  2746.  
  2747. &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  2748. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
  2749. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;helloworld&amp;quot;&lt;/span&gt;
  2750. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;net/http&amp;quot;&lt;/span&gt;
  2751.  
  2752. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;github.com/julienschmidt/httprouter&amp;quot;&lt;/span&gt;
  2753. &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2754.  
  2755. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2756. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;httprouter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  2757. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2758. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/:name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2759.  
  2760. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:8080&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2761. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2762.  
  2763. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;httprouter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2764. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ByName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2765. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  2766. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Unknown&amp;quot;&lt;/span&gt;
  2767. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2768. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Fprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;helloworld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  2769. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  2770. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2771. &lt;h4&gt;Changes:&lt;/h4&gt;
  2772.  
  2773. &lt;ul&gt;
  2774. &lt;li&gt;import section now includes vendored dependency &lt;code&gt;&amp;quot;github.com/julienschmidt/httprouter&amp;quot;&lt;/code&gt;&lt;/li&gt;
  2775. &lt;li&gt;new router &lt;code&gt;router := httprouter.New()&lt;/code&gt;&lt;/li&gt;
  2776. &lt;li&gt;new route with named parameter &lt;code&gt;/:name&lt;/code&gt;&lt;/li&gt;
  2777. &lt;li&gt;&lt;code&gt;index&lt;/code&gt; function signature change(as required by the router API)&lt;/li&gt;
  2778. &lt;/ul&gt;
  2779.  
  2780. &lt;h4&gt;Exercise:&lt;/h4&gt;
  2781.  
  2782. &lt;ul&gt;
  2783. &lt;li&gt;re-build and run &lt;code&gt;bin/helloworld-server&lt;/code&gt;&lt;/li&gt;
  2784. &lt;li&gt;point your browser to &lt;a href=&quot;http://localhost:8080/Go&quot;&gt;http://localhost:8080/Go&lt;/a&gt; and notice &amp;quot;Hello Go World&amp;quot; response&lt;/li&gt;
  2785. &lt;li&gt;add a &lt;code&gt;Makefile&lt;/code&gt; rule that builds and runs the server&lt;/li&gt;
  2786. &lt;li&gt;add a &lt;code&gt;Makefile&lt;/code&gt; rule that re-builds server and runs on a file change&lt;/li&gt;
  2787. &lt;li&gt;check-in &lt;code&gt;vendor/manifest&lt;/code&gt; file to your &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_control&quot;&gt;SCM&lt;/a&gt;&lt;/li&gt;
  2788. &lt;li&gt;write &lt;code&gt;Makefile&lt;/code&gt; rule to restore dependencies&lt;/li&gt;
  2789. &lt;/ul&gt;
  2790.  
  2791. &lt;h2&gt;8. Deployment&lt;/h2&gt;
  2792.  
  2793. &lt;p&gt;Deploys are just copying the binary to a server and launching it once it&amp;#39;s built for the platform it&amp;#39;s going to be running on.
  2794. Building could be done through &lt;a href=&quot;http://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5&quot;&gt;cross-compilation&lt;/a&gt; or on a build machine.&lt;/p&gt;
  2795.  
  2796. &lt;p&gt;But it&amp;#39;s left as an exercise to the reader ;)&lt;/p&gt;
  2797.  
  2798. &lt;h2&gt;References&lt;/h2&gt;
  2799.  
  2800. &lt;ul&gt;
  2801. &lt;li&gt;&lt;a href=&quot;https://github.com/maryanhratson/go-from-playing-to-production&quot;&gt;repo with example code&lt;/a&gt;&lt;/li&gt;
  2802. &lt;li&gt;Official &lt;a href=&quot;https://golang.org/doc/code.html#GOPATH&quot;&gt;GOPATH&lt;/a&gt; description&lt;/li&gt;
  2803. &lt;li&gt;Official &lt;a href=&quot;https://golang.org/doc/install&quot;&gt;Getting Started Guide&lt;/a&gt;&lt;/li&gt;
  2804. &lt;li&gt;Official &lt;a href=&quot;https://golang.org/doc/code.html&quot;&gt;How to Write Go Code&lt;/a&gt;&lt;/li&gt;
  2805. &lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/testing/&quot;&gt;testing package documentation&lt;/a&gt;&lt;/li&gt;
  2806. &lt;li&gt;&amp;quot;&lt;a href=&quot;http://blog.golang.org/6years&quot;&gt;an official package vendoring mechanism&lt;/a&gt;&amp;quot; should be announced with Go 1.6&lt;/li&gt;
  2807. &lt;li&gt;&lt;a href=&quot;https://golang.org/doc/&quot;&gt;more docs&lt;/a&gt;&lt;/li&gt;
  2808. &lt;/ul&gt;
  2809.  
  2810. &lt;p&gt;Thanks for reading and &lt;a href=&quot;http://blog.golang.org/6years&quot;&gt;happy birthday to &lt;code&gt;Go&lt;/code&gt;&lt;/a&gt;!&lt;/p&gt;
  2811. </description>
  2812.    </item>
  2813.    
  2814.    
  2815.    
  2816.    <item>
  2817.      <title>Timezones in Python</title>
  2818.      <link>https://techblog.thescore.com/2015/11/03/timezones-in-python/</link>
  2819.      <pubDate>Tue, 03 Nov 2015 00:00:00 +0000</pubDate>
  2820.      <author></author>
  2821.      <guid>https://techblog.thescore.com/2015/11/03/timezones-in-python</guid>
  2822.      <description>&lt;p&gt;After struggling for a while to determine how to convert from one timezone to another in Python I thought I&amp;#39;d write a quick little guide on it. Even if the only person it helps is me a few months from now I&amp;#39;ll consider it a success!&lt;/p&gt;
  2823.  
  2824. &lt;p&gt;It all started when I received the error &lt;code&gt;ValueError: Not naive datetime (tzinfo is already set)&lt;/code&gt;. A little bit later I received the error &lt;code&gt;ValueError: astimezone() cannot be applied to a naive datetime&lt;/code&gt;. It was about this time that I realized I needed to learn what was actually going on.&lt;/p&gt;
  2825.  
  2826. &lt;h2&gt;The naive datetime&lt;/h2&gt;
  2827.  
  2828. &lt;p&gt;Datetimes in Python can either be &lt;code&gt;Naive&lt;/code&gt; or &lt;code&gt;Aware&lt;/code&gt;. A naive datetime is a datetime that doesn&amp;#39;t know its own timezone. It could be in &lt;code&gt;UTC&lt;/code&gt;, it could be in &lt;code&gt;US/Eastern&lt;/code&gt;, but it doesn&amp;#39;t know. All it knows is which date and time that it has.&lt;/p&gt;
  2829. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;
  2830. &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2831. &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tzinfo&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# None&lt;/span&gt;
  2832. &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%a&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %b &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %H:%M:%S %Y&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Wed Nov 11 13:00:00 2015&lt;/span&gt;
  2833. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2834. &lt;p&gt;At this point we don&amp;#39;t know if it is 11AM EST, UTC... it is up for the programmer to keep track of which timezone they are working in.&lt;/p&gt;
  2835.  
  2836. &lt;h2&gt;Making it aware&lt;/h2&gt;
  2837.  
  2838. &lt;p&gt;To make our naive datetime aware of its timezone, we can use the &lt;code&gt;pytz&lt;/code&gt; library. Let&amp;#39;s make our date be in UTC time. We&amp;#39;ll do this by using the &lt;code&gt;localize&lt;/code&gt; method of a timezone.&lt;/p&gt;
  2839. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pytz&lt;/span&gt;
  2840. &lt;span class=&quot;n&quot;&gt;utc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pytz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;UTC&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2841. &lt;span class=&quot;n&quot;&gt;aware_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;utc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;localize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2842. &lt;span class=&quot;n&quot;&gt;aware_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tzinfo&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;UTC&amp;gt;&lt;/span&gt;
  2843. &lt;span class=&quot;n&quot;&gt;aware_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%a&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %b &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %H:%M:%S %Y&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Wed Nov 11 13:00:00 2015&lt;/span&gt;
  2844. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2845. &lt;h2&gt;Converting to another timezone&lt;/h2&gt;
  2846.  
  2847. &lt;p&gt;Now let&amp;#39;s take our aware date (in UTC) and convert it to a date in the US/Eastern timezone. To do this we can again use &lt;code&gt;pytz&lt;/code&gt; along with a method of &lt;code&gt;datetime&lt;/code&gt; called &lt;code&gt;astimezone&lt;/code&gt;.&lt;/p&gt;
  2848. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eastern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pytz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Eastern&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2849. &lt;span class=&quot;n&quot;&gt;eastern_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aware_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;astimezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eastern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2850. &lt;span class=&quot;n&quot;&gt;eastern_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tzinfo&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;DstTzInfo &amp;#39;US/Eastern&amp;#39; EST-1 day, 19:00:00 STD&amp;gt;&lt;/span&gt;
  2851. &lt;span class=&quot;n&quot;&gt;eastern_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%a&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %b &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %H:%M:%S %Y&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Wed Nov 11 08:00:00 2015&lt;/span&gt;
  2852. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2853. &lt;h2&gt;Wrapping things up&lt;/h2&gt;
  2854.  
  2855. &lt;p&gt;We started with a naive date which was unaware of which timezone it was in. We made it aware by localizing it to the UTC timezone, and then we took that aware date and converted it into the US/Eastern timezone.&lt;/p&gt;
  2856. </description>
  2857.    </item>
  2858.    
  2859.    
  2860.    
  2861.    <item>
  2862.      <title>Respect HTTP Caching</title>
  2863.      <link>https://techblog.thescore.com/2015/09/24/respect-http-caching/</link>
  2864.      <pubDate>Thu, 24 Sep 2015 00:00:00 +0000</pubDate>
  2865.      <author></author>
  2866.      <guid>https://techblog.thescore.com/2015/09/24/respect-http-caching</guid>
  2867.      <description>&lt;p&gt;API developers place plenty of consideration on how to handle scaling. A few techniques and approaches: Application caching, sharding, larger/more servers, background queues, HTTP caching, and Content Delivery Networks (CDNs). A well architected API employs a combination of these solutions to achieve the desired scalability. Of the scaling solutions, very few are actually exposed to the consumers of an API. One strategy in which the end-users actively participate in is &lt;em&gt;HTTP Caching&lt;/em&gt;.&lt;/p&gt;
  2868.  
  2869. &lt;p&gt;By respecting HTTP caching on a client&amp;#39;s application, network communications complete quicker and in some cases are unnecessary. Network response payloads are sometimes omitted due to a 304 HTTP response. Finally if you use HTTP caching, application servers will love you.&lt;/p&gt;
  2870.  
  2871. &lt;h2&gt;HTTP Caching&lt;/h2&gt;
  2872.  
  2873. &lt;p&gt;If an endpoint is to be hit repeatedly, HTTP caching can dramatically reduce the load on the server. There are a number of ways one can use HTTP caching. Similar to other scaling techniques some of these can be used in tandem.&lt;/p&gt;
  2874.  
  2875. &lt;h3&gt;Cache-Control&lt;/h3&gt;
  2876.  
  2877. &lt;p&gt;On an HTTP response a &lt;code&gt;cache-control&lt;/code&gt; header may present. This header provides the client insight on how they should approach making similar requests. For example:&lt;/p&gt;
  2878. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Cache-Control: max-age=60, must-revalidate
  2879. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2880. &lt;p&gt;The &lt;code&gt;max-age=60&lt;/code&gt; specifies that the request should be cached and used locally for a maximum duration of one minute. The &lt;code&gt;must-revalidate&lt;/code&gt; forces subsequent requests to still communicate to the server to respect the &lt;em&gt;freshness&lt;/em&gt; of the response (i.e., information which indicates that the content of the response has updated). These two simple cache-control headers provide both &lt;em&gt;non-stale&lt;/em&gt; responses with reduced payload, provided the endpoint is respecting the max-age (i.e., not changing frequently).&lt;/p&gt;
  2881.  
  2882. &lt;p&gt;To better understand cache-controls, our coworker at theScore, Thuva Tharam, has an &lt;a href=&quot;http://techblog.thescore.com/2014/11/19/are-your-cache-control-directives-doing-what-they-are-supposed-to-do/&quot;&gt;excellent blog post&lt;/a&gt; written on the subject. The individual cache-control directives and their effects are described. Suggestions and examples are provided to illustrate how to use cache-control effectively.&lt;/p&gt;
  2883.  
  2884. &lt;h3&gt;Last-Modified&lt;/h3&gt;
  2885.  
  2886. &lt;p&gt;As previously mentioned, it is possible for a cached request to &lt;code&gt;re-validate&lt;/code&gt; with the server to check for &lt;em&gt;freshness&lt;/em&gt;. Subsequent requests have an additional &lt;code&gt;If-Modified-Since&lt;/code&gt; which contains the value of the response&amp;#39;s &lt;code&gt;Last-Modified&lt;/code&gt; header. This allows the server to compare the provided time on subsequent requests against the responses and return the full response or simply a &lt;em&gt;304 status code&lt;/em&gt;.&lt;/p&gt;
  2887.  
  2888. &lt;p&gt;When a server returns a full response it takes more time to compute/collect the necessary information along with rendering time for the response. With the correct &lt;code&gt;cache-control&lt;/code&gt; along with a &lt;code&gt;Last-Modified&lt;/code&gt;, a server spends less time constructing the response. A &lt;em&gt;304 status code&lt;/em&gt; response is quick, and has a small payload size compared to a normal full response.&lt;/p&gt;
  2889.  
  2890. &lt;h3&gt;ETags&lt;/h3&gt;
  2891.  
  2892. &lt;p&gt;Even when the &lt;code&gt;Last-Modified&lt;/code&gt; value has changed, the actual content might still be the same. If the server takes advantage of ETags then there will be an &lt;code&gt;ETag&lt;/code&gt; header on the response. The benefit of using ETags shines on requests that do not change often. ETag caching does not expire based on time like the previous two caching mechanisms, and is instead a function of the response&amp;#39;s content. This form of caching is ideal as it provides caching while still allowing for freshness of updates to the content.&lt;/p&gt;
  2893.  
  2894. &lt;p&gt;When a subsequent request is made a &lt;code&gt;If-None-Match&lt;/code&gt; header is sent along with the previous request&amp;#39;s ETag. The server will then use the &lt;em&gt;ETag&lt;/em&gt; to determine &lt;em&gt;freshness&lt;/em&gt;. If the ETag of the request match the that of the server&amp;#39;s response a &lt;em&gt;304 status code&lt;/em&gt; is returned.&lt;/p&gt;
  2895.  
  2896. &lt;h2&gt;Respecting HTTP Caching for API Consumers&lt;/h2&gt;
  2897.  
  2898. &lt;blockquote&gt;
  2899. &lt;p&gt;API Consumers -- Client applications that makes more than one HTTP request to an external web service&lt;/p&gt;
  2900. &lt;/blockquote&gt;
  2901.  
  2902. &lt;p&gt;API Consumers can take on different forms such as a browser or desktop/mobile/web application.&lt;/p&gt;
  2903.  
  2904. &lt;h3&gt;Browsers&lt;/h3&gt;
  2905.  
  2906. &lt;p&gt;Modern browsers have no problem handling HTTP caching. It is also easy to verify that they are respecting HTTP caching by using their &lt;em&gt;Developer Tools&lt;/em&gt; interface. The following image shows two requests made to the same API endpoint (https://api.github.com/users/kevinjalbert) in Google Chrome&amp;#39;s Network Tab of the Developer Tools:&lt;/p&gt;
  2907.  
  2908. &lt;p&gt;First API request results in a 200 status code (standard success response):
  2909. &lt;img src=&quot;/assets/200-status-code-chrome.png&quot; alt=&quot;First Request results in a Status Code 200&quot;&gt;&lt;/p&gt;
  2910.  
  2911. &lt;p&gt;Second API request results in a 304 status code (resource has not been modified since the last request, no body is returned in response):
  2912. &lt;img src=&quot;/assets/304-status-code-chrome.png&quot; alt=&quot;Second Request results in a Status Code 304&quot;&gt;&lt;/p&gt;
  2913.  
  2914. &lt;h3&gt;Mobile Applications&lt;/h3&gt;
  2915.  
  2916. &lt;p&gt;Android and iOS have native mechanisms in place that developers can use to handle HTTP caching on any outbound requests. On iOS there is &lt;a href=&quot;https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/index.html#//apple_ref/occ/cl/NSURLCache&quot;&gt;NSURLCache&lt;/a&gt; and on Android there is &lt;a href=&quot;http://developer.android.com/reference/android/net/http/HttpResponseCache.html&quot;&gt;HttpResponseCache&lt;/a&gt;. If possible all HTTP requests made from mobile devices should be using the caching mechanisms.&lt;/p&gt;
  2917.  
  2918. &lt;h3&gt;Desktop Applications&lt;/h3&gt;
  2919.  
  2920. &lt;p&gt;Many programming languages can be used when creating desktop applications. In these cases it is best to check whether there is a built HTTP request caching mechanism akin to what Android and iOS have.&lt;/p&gt;
  2921.  
  2922. &lt;h3&gt;Web Applications&lt;/h3&gt;
  2923.  
  2924. &lt;p&gt;It is not uncommon to have web services talking to other web services through a defined API. In these situations there might not be a defined networking layer to push API requests through. For example, within a Ruby/Rails application one might use &lt;a href=&quot;https://github.com/lostisland/faraday&quot;&gt;Faraday&lt;/a&gt; to handle network connections. HTTP caching is not enabled by default on Faraday and is instead an opt-in option.&lt;/p&gt;
  2925.  
  2926. &lt;p&gt;The following is an example of using Faraday with &lt;a href=&quot;https://github.com/plataformatec/faraday-http-cache&quot;&gt;Faraday::HttpCache&lt;/a&gt; middleware to handle HTTP caching.&lt;/p&gt;
  2927. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;active_support&amp;#39;&lt;/span&gt;
  2928. &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;faraday&amp;#39;&lt;/span&gt;
  2929. &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;faraday-http-cache&amp;#39;&lt;/span&gt;
  2930.  
  2931. &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ApiConsumer&lt;/span&gt;
  2932. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Requester&lt;/span&gt;
  2933. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2934. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://api.github.com/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2935. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached&lt;/span&gt;
  2936. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HttpCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  2937. &lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;STDOUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  2938. &lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;instrumenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;
  2939. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2940. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_adapter&lt;/span&gt;
  2941. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2942. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2943.  
  2944. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
  2945. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;
  2946.  
  2947. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2948. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2949. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2950.  
  2951. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time_taken_ms&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;
  2952. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Time Taken: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time_taken_ms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; ms&amp;quot;&lt;/span&gt;
  2953.  
  2954. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;
  2955. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2956. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2957. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2958.  
  2959. &lt;span class=&quot;c1&quot;&gt;# Subscribes to all Faraday::HttpCache events&lt;/span&gt;
  2960. &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;http_cache.faraday&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  2961. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2962. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Cache Status: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cache_status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  2963. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2964.  
  2965. &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Faraday with HTTP Caching&amp;quot;&lt;/span&gt;
  2966. &lt;span class=&quot;n&quot;&gt;api_requester&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApiConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Requester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2967. &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2968. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_requester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;users/kevinjalbert&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2969. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  2970. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2971.  
  2972. &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
  2973.  
  2974. &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Faraday without HTTP Caching&amp;quot;&lt;/span&gt;
  2975. &lt;span class=&quot;n&quot;&gt;api_requester&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApiConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Requester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2976. &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  2977. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_requester&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;users/kevinjalbert&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  2978. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  2979. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  2980. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  2981. &lt;p&gt;Executing the above example produces the following log statements. With caching the time taken per request decreases tremendously on subsequent requests. By respecting the HTTP cache-controls of the API, the subsequent request can be used until the cache-control directives say otherwise (i.e., ETag/Last-Modified changes, or the cache expires exceeding the max-age).&lt;/p&gt;
  2982. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Faraday with HTTP Caching
  2983. D, [2015-09-20T23:06:27.570751 #15189] DEBUG -- : HTTP Cache: [GET /users/kevinjalbert] miss, store
  2984. Cache Status: miss
  2985. Time Taken: 1396.139 ms
  2986. D, [2015-09-20T23:06:29.577798 #15189] DEBUG -- : HTTP Cache: [GET /users/kevinjalbert] fresh
  2987. Cache Status: fresh
  2988. Time Taken: 1.857 ms
  2989. D, [2015-09-20T23:06:31.580719 #15189] DEBUG -- : HTTP Cache: [GET /users/kevinjalbert] fresh
  2990. Cache Status: fresh
  2991. Time Taken: 0.971 ms
  2992. D, [2015-09-20T23:06:33.585455 #15189] DEBUG -- : HTTP Cache: [GET /users/kevinjalbert] fresh
  2993. Cache Status: fresh
  2994. Time Taken: 1.139 ms
  2995. D, [2015-09-20T23:06:35.587833 #15189] DEBUG -- : HTTP Cache: [GET /users/kevinjalbert] fresh
  2996. Cache Status: fresh
  2997. Time Taken: 1.133 ms
  2998.  
  2999. Faraday without HTTP Caching
  3000. Time Taken: 807.486 ms
  3001. Time Taken: 277.589 ms
  3002. Time Taken: 186.115 ms
  3003. Time Taken: 475.249 ms
  3004. Time Taken: 1130.691 ms
  3005. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
  3006.    </item>
  3007.    
  3008.    
  3009.    
  3010.    <item>
  3011.      <title>Faster RSpec Regression Testing</title>
  3012.      <link>https://techblog.thescore.com/2015/09/21/faster-rspec-regression-testing/</link>
  3013.      <pubDate>Mon, 21 Sep 2015 00:00:00 +0000</pubDate>
  3014.      <author></author>
  3015.      <guid>https://techblog.thescore.com/2015/09/21/faster-rspec-regression-testing</guid>
  3016.      <description>&lt;h2&gt;Testing Approaches&lt;/h2&gt;
  3017.  
  3018. &lt;ol&gt;
  3019. &lt;li&gt;&lt;em&gt;Immediately&lt;/em&gt; on the code that is being developed to help guide development. Generally using a &lt;em&gt;subset&lt;/em&gt; of the test suite.&lt;/li&gt;
  3020. &lt;li&gt;&lt;em&gt;Afterwards&lt;/em&gt; on the complete codebase to ensure no regressions appear. This is always done using the &lt;em&gt;complete&lt;/em&gt; test suite.&lt;/li&gt;
  3021. &lt;/ol&gt;
  3022.  
  3023. &lt;p&gt;In an ideal environment, the complete test suite is run after every meaningful code change. This approach works for a small project, but it can grow into a time consuming process. Developers may only run a subset of tests that exercise the modified source code. Faster test execution allows for tighter feedback loops between testing and developing.&lt;/p&gt;
  3024.  
  3025. &lt;p&gt;Eventually the active development stops as the feature is completed. A developer wants to have a high-level of confidence in the code that they wrote before committing the changes. By running the complete test suite, one can have a certain level of assurance that the code to be committed will not cause issues. Ideally this second phase of testing is carried out locally by the developer, but also on a continuous integration server to prevent regressions.&lt;/p&gt;
  3026.  
  3027. &lt;h2&gt;RSpecing &lt;code&gt;it&lt;/code&gt; up&lt;/h2&gt;
  3028.  
  3029. &lt;p&gt;RSpec tests are executed using the &lt;code&gt;it&lt;/code&gt; method. The code within the &lt;code&gt;it&lt;/code&gt; block is executed after applying context using &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;before&lt;/code&gt;, which can be seen as the pre-amble of the test. In other words, for every &lt;code&gt;it&lt;/code&gt; block the test will execute the required pre-amble before the actual &lt;code&gt;it&lt;/code&gt; block.&lt;/p&gt;
  3030.  
  3031. &lt;h3&gt;Many &lt;code&gt;it&lt;/code&gt; blocks&lt;/h3&gt;
  3032.  
  3033. &lt;p&gt;During development it is useful to use the following technique to understand as much detail of failing tests:&lt;/p&gt;
  3034. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# A trivial example to show the verification of&lt;/span&gt;
  3035. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# multiple conditions using many &amp;#39;it&amp;#39; blocks&lt;/span&gt;
  3036. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;test with many it blocks&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3037. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3038. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3039. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3040. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3041. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Failures:
  3042.  
  3043.  1) test with many it blocks should eq &amp;quot;B&amp;quot;
  3044.     Failure/Error: it { expect(&amp;#39;b&amp;#39;.upcase).to eq(&amp;#39;B&amp;#39;) }
  3045.  
  3046.       expected: &amp;quot;B&amp;quot;
  3047.            got: &amp;quot;C&amp;quot;
  3048.  
  3049.  2) test with many it blocks should eq &amp;quot;C&amp;quot;
  3050.     Failure/Error: it { expect(&amp;#39;b&amp;#39;.upcase).to eq(&amp;#39;C&amp;#39;) }
  3051.  
  3052.       expected: &amp;quot;C&amp;quot;
  3053.            got: &amp;quot;B&amp;quot;
  3054. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3055. &lt;p&gt;The results of the testing present all of the failures. This approach is important during iterative development as fixing one test might cause others to fail.&lt;/p&gt;
  3056.  
  3057. &lt;h3&gt;Combining &lt;code&gt;it&lt;/code&gt; blocks&lt;/h3&gt;
  3058.  
  3059. &lt;p&gt;Another approach that can be used with RSpec is to combine the many &lt;code&gt;it&lt;/code&gt; blocks into one:&lt;/p&gt;
  3060. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# A trivial example to show the verification of&lt;/span&gt;
  3061. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# multiple conditions using a combined &amp;#39;it&amp;#39; block&lt;/span&gt;
  3062. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;test with a combined it block&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3063. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3064. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3065. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3066. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3067. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3068. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3069. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Failures:
  3070.  
  3071.  1) test with a combined it block should eq &amp;quot;B&amp;quot;
  3072.     Failure/Error: expect(&amp;#39;c&amp;#39;.upcase).to eq(&amp;#39;B&amp;#39;)
  3073.  
  3074.       expected: &amp;quot;B&amp;quot;
  3075.            got: &amp;quot;C&amp;quot;
  3076. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3077. &lt;p&gt;This time the results present &lt;em&gt;only the first&lt;/em&gt; failure, even though two assertions are incorrect. This approach provides less information than the first example. Although there is a different benefit, it runs less examples (tests and their complete pre-amble). This approach leads to quicker test executions, with the loss of the complete listing of failures. Arguably this approach is better suited when a feature is complete and the tests remain present to detect regressions.&lt;/p&gt;
  3078.  
  3079. &lt;h2&gt;Performance Impact&lt;/h2&gt;
  3080.  
  3081. &lt;p&gt;In the previous section, two styles of using RSpec&amp;#39;s &lt;code&gt;it&lt;/code&gt; blocks were illustrated. The next example showcases both approaches to understand how the testing performance changes. To simulate some pre-amble (i.e., database interactions, setting up objects) sleeps are used when accessing the values from their &lt;code&gt;let&lt;/code&gt; blocks.&lt;/p&gt;
  3082. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;
  3083.  
  3084. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AttributeObject&lt;/span&gt;
  3085. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value4&lt;/span&gt;
  3086.  
  3087. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3088. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@value1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;
  3089. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@value2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;
  3090. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@value3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;
  3091. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@value4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;
  3092. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3093. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3094.  
  3095. &lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;performance using `it` blocks&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3096. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AttributeObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3097.  
  3098. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3099. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3100. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3101. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3102.  
  3103. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;slow tests due to many `it` blocks&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3104. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3105. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3106. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3107. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3108. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3109.  
  3110. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;faster tests due to a combined `it` block&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3111. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3112. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3113. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3114. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3115. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3116. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3117. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3118. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3119. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3120. &lt;p&gt;For this example it is possible to use both testing approaches. There are two criteria that must be satisfied to allow this transformation. For each &lt;code&gt;it&lt;/code&gt; block:&lt;/p&gt;
  3121.  
  3122. &lt;ul&gt;
  3123. &lt;li&gt;The &lt;strong&gt;pre-amble is the same&lt;/strong&gt;. In the example they are all instantiating the &lt;code&gt;AttributeObject&lt;/code&gt; with the four value arguments.&lt;/li&gt;
  3124. &lt;li&gt;The &lt;strong&gt;contents are not mutating the environment in a different way from each other&lt;/strong&gt;. If an &lt;code&gt;it&lt;/code&gt; block mutates the environment then the many &lt;code&gt;it&lt;/code&gt; block approach might have unexpected results. In the example each &lt;code&gt;it&lt;/code&gt; block contains only a single assertion.&lt;/li&gt;
  3125. &lt;/ul&gt;
  3126. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;# Many &amp;#39;it&amp;#39; blocks
  3127. Finished in 1.67 seconds (files took 0.09327 seconds to load)
  3128. 4 examples, 0 failures
  3129.  
  3130. # Combined &amp;#39;it&amp;#39; block
  3131. Finished in 0.41675 seconds (files took 0.09545 seconds to load)
  3132. 1 example, 0 failures
  3133. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3134. &lt;p&gt;It is clear to see that the combined &lt;code&gt;it&lt;/code&gt; block executes less examples and therefore requires less time. Both of these approaches are testing the &lt;em&gt;exact same thing&lt;/em&gt;, however one does so more quickly than the other. There is still the loss of the precise failure reporting per &lt;code&gt;it&lt;/code&gt; block. It can be argued that it is not a huge loss given the code under test has been developed and the tests are for regressions.&lt;/p&gt;
  3135.  
  3136. &lt;h3&gt;Larger Scale Test Suite&lt;/h3&gt;
  3137.  
  3138. &lt;p&gt;To better illustrate the benefits of using combined &lt;code&gt;it&lt;/code&gt; blocks when possible, I have applied this technique to a larger project. The conversion between the two approaches was straight-forward and the results are satisfying. It was possible to consolidated many &lt;code&gt;it&lt;/code&gt; blocks (300 less test examples), reducing the run time by 38%.&lt;/p&gt;
  3139. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;# Before
  3140. Finished in 3 minutes 6.3 seconds (files took 10.06 seconds to load)
  3141. 1506 examples, 0 failures
  3142.  
  3143. # After
  3144. Finished in 1 minute 55.27 seconds (files took 10.11 seconds to load)
  3145. 1206 examples, 0 failures
  3146. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3147. &lt;p&gt;The results are going to differ between projects and testing styles, although in most cases there will be some performance improvements.&lt;/p&gt;
  3148.  
  3149. &lt;h2&gt;Suggested Testing Workflow&lt;/h2&gt;
  3150.  
  3151. &lt;p&gt;The following workflow is the one I follow as it provides the best of both worlds:&lt;/p&gt;
  3152.  
  3153. &lt;ol&gt;
  3154. &lt;li&gt;Iteratively develop feature using many &lt;code&gt;it&lt;/code&gt; blocks for feedback locally&lt;/li&gt;
  3155. &lt;li&gt;Complete feature&lt;/li&gt;
  3156. &lt;li&gt;Convert many &lt;code&gt;it&lt;/code&gt; blocks into a combined &lt;code&gt;it&lt;/code&gt; block for regressions&lt;/li&gt;
  3157. &lt;li&gt;Merge feature into master&lt;/li&gt;
  3158. &lt;/ol&gt;
  3159.  
  3160. &lt;p&gt;Eventually some issue may come up and a regression might be found. If more work is involved to fix and understand the issue at hand I might split the combined &lt;code&gt;it&lt;/code&gt; block to provide more feedback on failures. At this point I would go back to following the workflow from the start.&lt;/p&gt;
  3161. </description>
  3162.    </item>
  3163.    
  3164.    
  3165.    
  3166.    <item>
  3167.      <title>One Repo to Rule Them All</title>
  3168.      <link>https://techblog.thescore.com/2015/09/18/one-repo-to-rule-them-all/</link>
  3169.      <pubDate>Fri, 18 Sep 2015 00:00:00 +0000</pubDate>
  3170.      <author></author>
  3171.      <guid>https://techblog.thescore.com/2015/09/18/one-repo-to-rule-them-all</guid>
  3172.      <description>&lt;p&gt;This week it&amp;#39;s been a hot topic of discussion about how Google has (almost) all of their code within a single, massive, 2-billion lines of code repository.&lt;/p&gt;
  3173.  
  3174. &lt;p&gt;There was a quote by &lt;a href=&quot;https://twitter.com/searls&quot;&gt;Justin Searls&lt;/a&gt;, which was most definitely him joking around, but it got me thinking about how we share code at theScore.&lt;/p&gt;
  3175.  
  3176. &lt;p&gt;There&amp;#39;s the common misconception that you either have a &amp;quot;Monolith&amp;quot; or series of &amp;quot;Microservices&amp;quot;. Or as somebody else put it on Twitter: &amp;quot;A swarm of loosely coupled nanoservices&amp;quot;.&lt;/p&gt;
  3177.  
  3178. &lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Microliths.&lt;/p&gt;&amp;mdash; Justin Searls (@searls) &lt;a href=&quot;https://twitter.com/searls/status/644646188801724416&quot;&gt;September 17, 2015&lt;/a&gt;&lt;/blockquote&gt;
  3179.  
  3180. &lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  3181.  
  3182. &lt;p&gt;We certainly have a number of repos (iOS app, Android app, website, data and API, user accounts and authentication API, etc...), but the &amp;quot;data and API&amp;quot; repo in particular has a number of related Rails apps along with some local gems.&lt;/p&gt;
  3183.  
  3184. &lt;h2&gt;Sharing code across Rails apps&lt;/h2&gt;
  3185.  
  3186. &lt;p&gt;The repository I mentioned has 4 different Rails apps inside of it. It has an app for the API, an app for an internal admin panel, an app for processing incoming data, and an app for handling push alerts.&lt;/p&gt;
  3187.  
  3188. &lt;p&gt;All of these apps share 2 gems which are also found inside the same repository: a gem for the &amp;quot;data&amp;quot; layer (basically the models and objects for interacting with the database), and a support gem which has a few utility classes that are shared, along with some things like lists of country codes and whatnot.&lt;/p&gt;
  3189.  
  3190. &lt;p&gt;The repository is structured like this:&lt;/p&gt;
  3191.  
  3192. &lt;ul&gt;
  3193. &lt;li&gt;data_processor/ (rails app)&lt;/li&gt;
  3194. &lt;li&gt;admin/ (rails app)&lt;/li&gt;
  3195. &lt;li&gt;api/ (rails app)&lt;/li&gt;
  3196. &lt;li&gt;pusher/ (rails app)&lt;/li&gt;
  3197. &lt;li&gt;thescore-data/ (gem)&lt;/li&gt;
  3198. &lt;li&gt;thescore-support/ (gem)&lt;/li&gt;
  3199. &lt;li&gt;test (script)&lt;/li&gt;
  3200. &lt;/ul&gt;
  3201.  
  3202. &lt;p&gt;Each of the 4 Rails apps can have their own gems and dependencies, two of which being the local data and  support gems.&lt;/p&gt;
  3203. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile&lt;/span&gt;
  3204. &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;the_score_support&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;../thescore-support&amp;#39;&lt;/span&gt;
  3205. &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;the_score_data&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;../thescore-data&amp;#39;&lt;/span&gt;
  3206. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3207. &lt;h2&gt;Decreasing coupling without increasing complexity&lt;/h2&gt;
  3208.  
  3209. &lt;p&gt;&lt;strong&gt;Monoliths&lt;/strong&gt; are great in certain respects: They allow you to have all of your code inside a single app. If you need to &amp;quot;communicate&amp;quot; to another part of the system, you can do so via a method call (fast, simple) rather than by communicating over HTTP with a JSON request (slow, complex). There is also no need to attempt to version your API... there is a single version and that is the app itself.&lt;/p&gt;
  3210.  
  3211. &lt;p&gt;But there are obvious drawbacks as well: You can have seemingly unrelated things all packed into the same place (the API along with the admin panel for example), which both may require a number of gems that the other doesn&amp;#39;t need.&lt;/p&gt;
  3212.  
  3213. &lt;p&gt;&lt;strong&gt;Microservices&lt;/strong&gt; aim to solve some of the problems brought on by a monolith, but end up causing an increase in complexity by having to coordinate APIs across the different services, versioning them and deploying them at the same time.&lt;/p&gt;
  3214.  
  3215. &lt;p&gt;By combining related &amp;quot;Microservices&amp;quot; into a single repository, you end up getting the benefits from both approaches, but without the drawbacks that each one provides.&lt;/p&gt;
  3216.  
  3217. &lt;h2&gt;Benefits from testing&lt;/h2&gt;
  3218.  
  3219. &lt;p&gt;By having all of your apps in a single service you are also able to easily test all of your applications together. You can either run them individually, because each one has their own set of tests, or you can write a simple script which will test each app along with each gem, so that you know that your changes to the data gem hasn&amp;#39;t broken anything in one of the Rails apps.&lt;/p&gt;
  3220. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/bin/bash&lt;/span&gt;
  3221.  
  3222. &lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
  3223. &lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RACK_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
  3224.  
  3225. &lt;span class=&quot;nb&quot;&gt;trap&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SIGINT&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SIGTERM
  3226.  
  3227. &lt;span class=&quot;c1&quot;&gt;# The BASE_DIR is the absolute path of the directory of this script file&lt;/span&gt;
  3228. &lt;span class=&quot;nv&quot;&gt;BASE_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;dirname&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BASH_SOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[0]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
  3229. &lt;span class=&quot;nv&quot;&gt;FAILED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
  3230.  
  3231. &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;project&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;admin&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;api&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;thescore-data&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pusher&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;thescore-support&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;data_processor&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3232. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BASE_DIR&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$project&lt;/span&gt;
  3233. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Running specs for &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$project&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; with seed &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SEED&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  3234.  
  3235. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;bin/rspec
  3236.  
  3237. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-ne&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
  3238. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FAILED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  3239. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
  3240. &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
  3241.  
  3242. &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$FAILED&lt;/span&gt;
  3243. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3244. &lt;p&gt;Better yet, you can set up your continuous integration tools to tets your entire project each time you push to the repository, helping to guarantee that all apps in this repo are functioning correctly.&lt;/p&gt;
  3245.  
  3246. &lt;h2&gt;A single pull request&lt;/h2&gt;
  3247.  
  3248. &lt;p&gt;Another benefit of having everything within a single repo is that only a single pull request is needed for a change which might span multiple apps and/or gems.&lt;/p&gt;
  3249.  
  3250. &lt;p&gt;If you&amp;#39;ve ever had to coordinate multiple PRs which must be approved, merged, and deployed in sync, you know how difficult and annoying it can be. It&amp;#39;s not just difficult for you though, it&amp;#39;s difficult for whoever is having to review the PR as well. The reviewers need to be made aware that there are multiple PRs in different repositories, and keep that in mind when reviewing each one individually.&lt;/p&gt;
  3251.  
  3252. &lt;p&gt;Combining apps and gems into a single repo helps to solve this, and PRs become a lot easier to manage.&lt;/p&gt;
  3253.  
  3254. &lt;h2&gt;Final thoughts&lt;/h2&gt;
  3255.  
  3256. &lt;p&gt;You don&amp;#39;t have to go as far as Google, where everything your organization done is in a single repository. But by combining separate apps in a single repository, you&amp;#39;re able to get some of the advantages of having Microservices while not throwing out all the advantages of a Monolith. Or, as Justin Searls said, &lt;strong&gt;Microliths.&lt;/strong&gt;&lt;/p&gt;
  3257. </description>
  3258.    </item>
  3259.    
  3260.    
  3261.    
  3262.    <item>
  3263.      <title>Database Transactions With pytest</title>
  3264.      <link>https://techblog.thescore.com/2015/06/25/database-transactions-with-pytest/</link>
  3265.      <pubDate>Thu, 25 Jun 2015 00:00:00 +0000</pubDate>
  3266.      <author></author>
  3267.      <guid>https://techblog.thescore.com/2015/06/25/database-transactions-with-pytest</guid>
  3268.      <description>&lt;p&gt;At theScore, most of our projects are built on Rails so we&amp;#39;re used to having testing tools built-in. We primarily use RSpec with Rails and that combo gives us a few things by default:&lt;/p&gt;
  3269.  
  3270. &lt;ol&gt;
  3271. &lt;li&gt;Test discovery under &lt;code&gt;spec/&lt;/code&gt; or &lt;code&gt;test/&lt;/code&gt;&lt;/li&gt;
  3272. &lt;li&gt;Automatic database transaction support per test&lt;/li&gt;
  3273. &lt;/ol&gt;
  3274.  
  3275. &lt;p&gt;We have an old, custom Python project that we&amp;#39;ve been working with and slowly modernizing. The need for unit tests came up and I evaluated a few options. I ended up choosing &lt;a href=&quot;https://pytest.org/&quot;&gt;pytest&lt;/a&gt; as it seemed to be most modern and popular framework.&lt;/p&gt;
  3276.  
  3277. &lt;p&gt;By default, pytest is configured to discover tests in &lt;em&gt;all&lt;/em&gt; directories and subdirectories. So, while it does work, it can be slow (because it crawls everything) and it can discover tests that it shouldn&amp;#39;t (such as a vendored code).&lt;/p&gt;
  3278.  
  3279. &lt;p&gt;I worked around this issue by using the &lt;code&gt;norecursedirs&lt;/code&gt; option. This option tells pytest which directories &lt;em&gt;not&lt;/em&gt; to go into (no option exists to tell it to only go into certain directories). Here&amp;#39;s a sample config:&lt;/p&gt;
  3280. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# setup.cfg&lt;/span&gt;
  3281. &lt;span class=&quot;k&quot;&gt;[pytest]&lt;/span&gt;
  3282. &lt;span class=&quot;na&quot;&gt;norecursedirs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.git vendor my-project/lib my-project/helpers docs config log tmp\*&lt;/span&gt;
  3283. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3284. &lt;p&gt;Note, if you put &lt;code&gt;vendor&lt;/code&gt;, then make sure you don&amp;#39;t have a directory like &lt;code&gt;tests/vendor/&lt;/code&gt; because it will be ignored:&lt;/p&gt;
  3285. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;cat&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;setup.cfg
  3286. &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;pytest&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  3287. &lt;span class=&quot;nv&quot;&gt;norecursedirs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;config
  3288.  
  3289. $&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;tree
  3290. .
  3291. ├──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;config
  3292. │&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;└──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;file.json
  3293. ├──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;file_name.py
  3294. ├──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;setup.cfg
  3295. └──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;tests
  3296. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;├──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;config
  3297. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;│&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;└──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;my_test.py
  3298. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;└──&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;my_test.py
  3299.  
  3300. &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;directories,&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;files
  3301.  
  3302. $&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;py.test&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--collect-only
  3303. &lt;span class=&quot;o&quot;&gt;=======&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;session&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;starts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=======&lt;/span&gt;
  3304. platform&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;darwin&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Python&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.9&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;py-1.4.27&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pytest-2.6.4
  3305. collected&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;items
  3306. &amp;lt;Module&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;tests/my_test.py&amp;#39;&lt;/span&gt;&amp;gt;
  3307. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&amp;lt;Class&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Test&amp;#39;&lt;/span&gt;&amp;gt;
  3308. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&amp;lt;Instance&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;()&amp;#39;&lt;/span&gt;&amp;gt;
  3309. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&amp;lt;Function&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;test_foo&amp;#39;&lt;/span&gt;&amp;gt;
  3310. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3311. &lt;p&gt;Anyway, buyer be warned!&lt;/p&gt;
  3312.  
  3313. &lt;p&gt;To solve #2 (automatic database transaction support per test), it got... a bit tricky. We utilized standard xUnit style tests, so our tests would look like this:&lt;/p&gt;
  3314. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3315.  
  3316.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3317.        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3318.  
  3319.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_can_get_meaning_of_life&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3320.        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_meaning_of_life&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;
  3321. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3322. &lt;p&gt;Say that instantiating &lt;code&gt;Widget&lt;/code&gt; actually wrote to the database. At this point, the widget is going to be saved in the database and subsequent tests could error out because they rely on having no widgets in the database.&lt;/p&gt;
  3323.  
  3324. &lt;p&gt;Our first solution used inheritance like this:&lt;/p&gt;
  3325. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# tests/helper.py&lt;/span&gt;
  3326. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3327.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3328.        &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3329.  
  3330.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;teardown_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3331.        &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3332.  
  3333. &lt;span class=&quot;c1&quot;&gt;# tests/unit/widget_test.py&lt;/span&gt;
  3334. &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tests.helper&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseTest&lt;/span&gt;
  3335.  
  3336. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3337.  
  3338.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3339.        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setup_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3340.        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3341.  
  3342.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_can_get_meaning_of_life&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3343.        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_meaning_of_life&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;
  3344. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3345. &lt;p&gt;This works for the most part, except that &lt;code&gt;teardown_method&lt;/code&gt; does not get called if something failed in &lt;code&gt;setup_method&lt;/code&gt; &lt;a href=&quot;https://pytest.org/latest/announce/release-2.4.0.html&quot;&gt;by design since pytest 2.4&lt;/a&gt; (see &amp;#39;issue322&amp;#39;). This means that &lt;code&gt;orm.session.rollback()&lt;/code&gt; might not be called.&lt;/p&gt;
  3346.  
  3347. &lt;h3&gt;Enter Pytest Fixtures&lt;/h3&gt;
  3348.  
  3349. &lt;p&gt;While the classic xUnit style doesn&amp;#39;t work so well, we do have an alternative: &lt;a href=&quot;https://pytest.org/latest/fixture.html&quot;&gt;pytest fixtures&lt;/a&gt;.&lt;/p&gt;
  3350.  
  3351. &lt;p&gt;What are these fixtures? Well, they&amp;#39;re basically dependencies that you can require for your tests. Here&amp;#39;s how our code can look now:&lt;/p&gt;
  3352. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# tests/helper.py&lt;/span&gt;
  3353. &lt;span class=&quot;nd&quot;&gt;@pytest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3354. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3355.    &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3356.  
  3357.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
  3358.        &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3359.  
  3360.    &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addfinalizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3361.  
  3362.    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
  3363.  
  3364. &lt;span class=&quot;c1&quot;&gt;# tests/unit/widget_test.py&lt;/span&gt;
  3365. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestMyWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3366.  
  3367.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_my_widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3368.        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;in passing test&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  3369.  
  3370.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_my_failing_widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3371.        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;in failing test&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  3372.        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3373. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3374. &lt;p&gt;The extended example and output &lt;a href=&quot;https://gist.github.com/Nitrodist/60ced9ca02d9e56cde42&quot;&gt;can be found here&lt;/a&gt;.&lt;/p&gt;
  3375.  
  3376. &lt;p&gt;The downside of this technique is that we have to remember to opt-in to every test by specifying &lt;code&gt;db_transaction&lt;/code&gt; as one of the arguments. To get around this issue, we tried combining &lt;code&gt;autouse&lt;/code&gt; and classes:&lt;/p&gt;
  3377. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# tests/helper.py&lt;/span&gt;
  3378. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3379.    &lt;span class=&quot;c1&quot;&gt;# prefixed with &amp;#39;_&amp;#39; since autouse fixtures are executed alphabetically&lt;/span&gt;
  3380.    &lt;span class=&quot;c1&quot;&gt;# see http://stackoverflow.com/a/28593102/269694&lt;/span&gt;
  3381.    &lt;span class=&quot;nd&quot;&gt;@pytest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;autouse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3382.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_wrap_test_in_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3383.        &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3384.  
  3385.        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
  3386.            &lt;span class=&quot;n&quot;&gt;orm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3387.  
  3388.        &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addfinalizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3389.  
  3390. &lt;span class=&quot;c1&quot;&gt;# tests/unit/widget_test.py&lt;/span&gt;
  3391. &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tests.helper&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseTest&lt;/span&gt;
  3392.  
  3393. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3394.  
  3395.    &lt;span class=&quot;nd&quot;&gt;@pytest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;autouse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3396.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3397.        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  3398.  
  3399.    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_can_get_meaning_of_life&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  3400.        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_meaning_of_life&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;
  3401. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3402. &lt;p&gt;That&amp;#39;s it! No other special tricks.&lt;/p&gt;
  3403.  
  3404. &lt;h3&gt;Conclusion&lt;/h3&gt;
  3405.  
  3406. &lt;p&gt;This took a bit of thinking to come up with, but maybe you or someone you know has a better way of writing tests with database transactions in pytest. Let us know what you think and leave a comment below!&lt;/p&gt;
  3407. </description>
  3408.    </item>
  3409.    
  3410.    
  3411.    
  3412.    <item>
  3413.      <title>Reducing WatchKit Traffic With View Models</title>
  3414.      <link>https://techblog.thescore.com/2015/05/20/reducing-watchkit-traffic-with-view-models/</link>
  3415.      <pubDate>Wed, 20 May 2015 00:00:00 +0000</pubDate>
  3416.      <author></author>
  3417.      <guid>https://techblog.thescore.com/2015/05/20/reducing-watchkit-traffic-with-view-models</guid>
  3418.      <description>&lt;p&gt;If you own an Apple Watch, you probably noticed something disappointing: 3rd party apps are SLOW.&lt;/p&gt;
  3419.  
  3420. &lt;p&gt;If you&amp;#39;ve written a watch app, you&amp;#39;ll know that a big part of the problem lies in the bandwidth limitations of phone to watch communication. Architecting a performant WatchKit extension can be difficult due to the write-only nature of WatchKit UI elements and the stringly-typed nature of controllers.&lt;/p&gt;
  3421.  
  3422. &lt;p&gt;How can we decrease traffic sent from the phone to the watch while simultaneously reducing the complexity of your app&amp;#39;s architecture? In this post I will describe how these can be achieved by using View Models.&lt;/p&gt;
  3423.  
  3424. &lt;h2&gt;Apple&amp;#39;s Recommendations&lt;/h2&gt;
  3425.  
  3426. &lt;p&gt;Apple specifically &lt;a href=&quot;https://developer.apple.com/watchkit/tips/&quot;&gt;recommends&lt;/a&gt; that you should:&lt;/p&gt;
  3427.  
  3428. &lt;ul&gt;
  3429. &lt;li&gt;Minimize traffic&lt;/li&gt;
  3430. &lt;li&gt;Update only what has changed&lt;/li&gt;
  3431. &lt;li&gt;Load content lazily&lt;/li&gt;
  3432. &lt;/ul&gt;
  3433.  
  3434. &lt;p&gt;This can be difficult with the WatchKit SDK since UI elements exist on the watch but are controlled from the WatchKit extension. We can&amp;#39;t query the state of the app running on the watch, forcing us to maintain UI state in our extension. If we don&amp;#39;t do this we risk sending unneccesary updates to the UI on the watch.&lt;/p&gt;
  3435.  
  3436. &lt;p&gt;View Models allow us to manage UI state without adding complexity to your WatchKit extension.&lt;/p&gt;
  3437.  
  3438. &lt;h2&gt;View Models&lt;/h2&gt;
  3439.  
  3440. &lt;p&gt;A View Model is an object for encapsulating the presentation logic of a model. In MVC we don’t want a view to contain this logic since it results in tight coupling to a specific model. Instead, the controller object is generally responsible for containing the presentation logic and uses it to configure a view. However, this just moves the coupling from the view to the controller, leaving you with a controller that is bloated and not reusable.&lt;/p&gt;
  3441.  
  3442. &lt;p&gt;View Models sit in-between the view and the controller, allowing you to easily encapsulate the manipulation of a model to provide just what is needed to populate the view. Business logic for representing models can be removed from both the view and the controller, increasing their reusability and testability.&lt;/p&gt;
  3443.  
  3444. &lt;p&gt;For example, here&amp;#39;s a simple View Model for an Event object that exposes only what is needed to populate a row of this table:&lt;/p&gt;
  3445.  
  3446. &lt;p&gt;&lt;img src=&quot;/assets/watchkit.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
  3447. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EventViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3448.  
  3449.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;firstTeamName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;
  3450.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;secondTeamName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;
  3451.  
  3452.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;firstTeamScore&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;
  3453.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;secondTeamScore&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;
  3454.  
  3455.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;firstTeamLogo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
  3456.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;secondTeamLogo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
  3457.  
  3458.    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3459.        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstTeamName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot; @ &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondTeamName&lt;/span&gt;
  3460.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3461.  
  3462.    &lt;span class=&quot;kd&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CGSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3463.        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstTeamName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awayTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;
  3464.        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondTeamName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;homeTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;
  3465.  
  3466.        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstTeamScore&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awayScore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;
  3467.        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondTeamScore&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;homeScore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;
  3468.  
  3469.        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstTeamLogo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awayTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awayTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doubleResolutionURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageQuality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
  3470.        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondTeamLogo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;homeTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;homeTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doubleResolutionURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageQuality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;
  3471.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3472. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3473. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3474. &lt;h2&gt;Minimizing Traffic &amp;amp; Updating Only What Has Changed&lt;/h2&gt;
  3475.  
  3476. &lt;p&gt;WatchKit interface elements are write-only, meaning that they can’t be queried to decide whether or not to send an update. Using View Models adds the benefit of holding the current state of the view, allowing the extension to determine if an interface element’s value needs to be updated.&lt;/p&gt;
  3477.  
  3478. &lt;p&gt;To easily add this ‘diffing’ functionality, we created a simple protocol that all WKInterface elements can implement in an extension (or category, in Objective-C).&lt;/p&gt;
  3479. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Updatable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3480.    &lt;span class=&quot;kd&quot;&gt;typealias&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
  3481.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt;
  3482. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3483. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3484. &lt;p&gt;The implementation of this on WKInterface label looks like this:&lt;/p&gt;
  3485. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WKInterfaceLabel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Updatable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3486.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3487.        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3488.            &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3489.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3490.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3491. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3492. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3493. &lt;p&gt;Now instead of calling &lt;code&gt;myLabel.setText(“newValue”)&lt;/code&gt; we can call &lt;code&gt;myLabel.updateFrom(“oldValue”, to: “newValue”)&lt;/code&gt; and &lt;code&gt;setText&lt;/code&gt; will only be called if the arguments differ.&lt;/p&gt;
  3494.  
  3495. &lt;p&gt;Since all of our images are loaded remotely and then sized to the dimensions required by the WKInterfaceImage, we have to compare two different properties of the image. We can do this while still conforming to the Updatable protocol by making a simple ImageViewModel:&lt;/p&gt;
  3496. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImageViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Equatable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3497.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;NSURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
  3498.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CGSize&lt;/span&gt;
  3499. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3500.  
  3501. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3502.    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rhs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;
  3503. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3504. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3505. &lt;p&gt;Now the extension on WKInterfaceImage looks like this:&lt;/p&gt;
  3506. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WKInterfaceImage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Updatable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3507.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3508.        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3509.            &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3510.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3511.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3512. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3513. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3514. &lt;p&gt;Here we call a function that we’ve written that asynchronously downloads an image, resizes it, caches it and sets it on the WKInterfaceImage, so by using the View Model we have potentially saved a lot of network and processing work.&lt;/p&gt;
  3515.  
  3516. &lt;h2&gt;Tables&lt;/h2&gt;
  3517.  
  3518. &lt;p&gt;Perhaps the most interesting application of View Models in WatchKit is for WKInterfaceTables. Whereas UITableViews have a datasource and delegate, WKInterfaceTables require the controller to maintain an internal mapping of data to both row controller type and row controller identifier, since row controllers are instantiated by a storyboard identifier and not by alloc’ing an instance of a class.&lt;/p&gt;
  3519.  
  3520. &lt;p&gt;This mapping can result in lots of logic being stuck inside a big loop where each row controller is populated. Additionally, it makes it much easier to discard every row in the table and recreate it, which Apple strongly discourages. We can combine a View Model for the table with individual View Models for each row type to move the presentation logic and the updating logic out of the controller.&lt;/p&gt;
  3521. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TableViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3522.    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rowTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3523.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rowViewModelAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
  3524.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WKInterfaceTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updateFrom&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldRowViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRowViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atIndex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3525. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3526.  
  3527. &lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WKInterfaceTable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Updatable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3528.    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TableViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TableViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3529.        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;oldTableModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3530.            &lt;span class=&quot;c1&quot;&gt;// only update if necessary&lt;/span&gt;
  3531.            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3532.                &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setRowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3533.            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3534.            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3535.                &lt;span class=&quot;c1&quot;&gt;// swap old row types for new row types&lt;/span&gt;
  3536.                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRowType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3537.                    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;swap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  3538.                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3539.                        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;oldRowType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  3540.                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldRowType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRowType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3541.                            &lt;span class=&quot;bp&quot;&gt;swap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  3542.                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3543.                        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3544.                            &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;removeRowsAtIndexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSIndexSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  3545.                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3546.                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3547.                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;swap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3548.                        &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insertRowsAtIndexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSIndexSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;withRowType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRowType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3549.                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3550.                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3551.  
  3552.                &lt;span class=&quot;c1&quot;&gt;// remove extraneous rows&lt;/span&gt;
  3553.                &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;numRowsToRemove&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;count&lt;/span&gt;
  3554.                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numRowsToRemove&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3555.                    &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;removeRowsAtIndexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSIndexSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexesInRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSMakeRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numRowsToRemove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  3556.                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3557.            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3558.  
  3559.            &lt;span class=&quot;c1&quot;&gt;// populate each row&lt;/span&gt;
  3560.            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numberOfRows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3561.                &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updateFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowViewModelAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowViewModelAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3562.            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3563.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3564.        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newTableModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3565.            &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setRowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3566.            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numberOfRows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3567.                &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updateFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowViewModelAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3568.            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3569.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3570.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3571. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3572. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3573. &lt;p&gt;Now if we load a data set, do a network fetch and get the same data set, zero updates are sent to the watch. No wasted bandwidth or rendering time. If one label in one row changes, only that change is sent. If we insert a different row type in the middle of the table, only the affected rows are modified.&lt;/p&gt;
  3574.  
  3575. &lt;p&gt;We swizzled the setters for WKInterfaceLabel and WKInterfaceTable to log their calls, demonstrating the improvements:&lt;/p&gt;
  3576.  
  3577. &lt;p&gt;&lt;strong&gt;Initial loading:&lt;/strong&gt;&lt;/p&gt;
  3578.  
  3579. &lt;ul&gt;
  3580. &lt;li&gt;1 setRowTypes: call&lt;/li&gt;
  3581. &lt;li&gt;17 setText: calls&lt;/li&gt;
  3582. &lt;/ul&gt;
  3583.  
  3584. &lt;p&gt;&lt;strong&gt;Reloading same data:&lt;/strong&gt;&lt;/p&gt;
  3585.  
  3586. &lt;ul&gt;
  3587. &lt;li&gt;no calls&lt;/li&gt;
  3588. &lt;/ul&gt;
  3589.  
  3590. &lt;p&gt;&lt;strong&gt;Inserting a new row type and deleting a row from the end:&lt;/strong&gt;&lt;/p&gt;
  3591.  
  3592. &lt;ul&gt;
  3593. &lt;li&gt;1 insertRowsAtIndexes:withRowType: call&lt;/li&gt;
  3594. &lt;li&gt;1 removeRowsAtIndexes: call&lt;/li&gt;
  3595. &lt;li&gt;3 setText: calls&lt;/li&gt;
  3596. &lt;/ul&gt;
  3597.  
  3598. &lt;p&gt;Even more useful is that we can easily adhere to Apple’s recommendation:&lt;/p&gt;
  3599.  
  3600. &lt;blockquote&gt;
  3601. &lt;p&gt;“To optimize the launch time of your WatchKit app and make your app feel more responsive, load the content below the initially visible area of a controller after the controller has displayed to the user.”&lt;/p&gt;
  3602. &lt;/blockquote&gt;
  3603.  
  3604. &lt;p&gt;If we do a network fetch for the first 3 rows of data, update the table, then do a subsequent fetch for 10 rows of data, we only have to call &lt;code&gt;myTable.updateFrom(_, to:)&lt;/code&gt; and don’t have to worry about computing row offsets. Only the rows that have been added to the bottom of the table will be sent to the watch.&lt;/p&gt;
  3605.  
  3606. &lt;h2&gt;Code Simplification&lt;/h2&gt;
  3607.  
  3608. &lt;p&gt;Prior to using View Models, our table updating code looked something like this:&lt;/p&gt;
  3609. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setRowTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateRow&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventRow&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventRow&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3610.  
  3611. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateTableWithData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3612.    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3613.        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rowData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  3614.  
  3615.        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3616.            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3617.                &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rowController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowControllerAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventController&lt;/span&gt;
  3618.                &lt;span class=&quot;c1&quot;&gt;// set all the labels and images&lt;/span&gt;
  3619.                &lt;span class=&quot;n&quot;&gt;rowController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;populate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3620.               &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
  3621.            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3622.                &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rowController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowControllerAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateController&lt;/span&gt;
  3623.                &lt;span class=&quot;c1&quot;&gt;// set all the labels and images&lt;/span&gt;
  3624.                &lt;span class=&quot;n&quot;&gt;rowController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;populate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3625.                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
  3626.            &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  3627.                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
  3628.        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3629.    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3630. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3631. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3632. &lt;p&gt;Now that we’ve moved all of our logic out of the interface controller, an update call to the table looks like this:&lt;/p&gt;
  3633. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;previousTableViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventsTableViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
  3634.  
  3635. &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateTableWithData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  3636.    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newTableViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventsTableViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3637.    &lt;span class=&quot;kc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updateFromOldValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;previousTableViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toNewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3638.    &lt;span class=&quot;n&quot;&gt;previousTableViewModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTableViewModel&lt;/span&gt;
  3639. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3640. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3641. &lt;h2&gt;Testing&lt;/h2&gt;
  3642.  
  3643. &lt;p&gt;View Models also have the benefit of adding basic testing functionality to your WatchKit extension. Because we can’t query WatchKit interface elements, we have no way of knowing if they correctly represent the data we are interested in. Now if we assume that the content of the view is accurately represented by the View Model, it allows us to perform business logic tests on the View Model.&lt;/p&gt;
  3644.  
  3645. &lt;h2&gt;Profiling&lt;/h2&gt;
  3646.  
  3647. &lt;p&gt;While we can demonstrate that the number of setter calls has been reduced and our code has been simplified, we are unable to profile and measure the performance improvements. The Apple Watch is essentially a black box, with several confounding variables (latency, watch state) that can’t be eliminated when testing. However, our methods follow Apple’s recommendations so we can hope that by reducing traffic and updating only what has changed we improve the responsiveness of our WatchKit Extension.&lt;/p&gt;
  3648.  
  3649. &lt;h2&gt;Conclusion&lt;/h2&gt;
  3650.  
  3651. &lt;p&gt;By using View Models the developer can create wrappers around their model objects, simplifying their extension architecture while at the same time reducing the network traffic sent from the phone to the watch. Give this approach a try when you re-architect your WatchKit extension and let us know if you see any performance improvements!&lt;/p&gt;
  3652. </description>
  3653.    </item>
  3654.    
  3655.    
  3656.    
  3657.    <item>
  3658.      <title>Share Your Bag of Tricks</title>
  3659.      <link>https://techblog.thescore.com/2015/05/01/share-your-bag-of-tricks/</link>
  3660.      <pubDate>Fri, 01 May 2015 00:00:00 +0000</pubDate>
  3661.      <author></author>
  3662.      <guid>https://techblog.thescore.com/2015/05/01/share-your-bag-of-tricks</guid>
  3663.      <description>&lt;p&gt;Everyone has specific tips and shortcuts that they have picked up from various sources, including the following:&lt;/p&gt;
  3664.  
  3665. &lt;ul&gt;
  3666. &lt;li&gt;Trial &amp;amp; error or self-discovery&lt;/li&gt;
  3667. &lt;li&gt;From a friend or colleague&lt;/li&gt;
  3668. &lt;li&gt;Reading books, blog posts, articles&lt;/li&gt;
  3669. &lt;li&gt;Watching videos, conferences&lt;/li&gt;
  3670. &lt;li&gt;Formal education&lt;/li&gt;
  3671. &lt;/ul&gt;
  3672.  
  3673. &lt;p&gt;Regardless of how one has gained these skills, it is obvious that they increase their effectiveness and efficiency at tasks. With enough exposure and time, one can amass an impressive assortment of skills, tips and shortcuts. We tend to call these people &lt;em&gt;veterans&lt;/em&gt;/&lt;em&gt;experts&lt;/em&gt;/&lt;em&gt;experienced&lt;/em&gt;/&lt;em&gt;seniors&lt;/em&gt;, amongst other words of praise and respect.&lt;/p&gt;
  3674.  
  3675. &lt;p&gt;This post is not directed towards a specific group of individuals, as the overarching message applies to everyone. It is common to find large visible gaps with respect to skills and/or experience. For example, within a workplace environment a more experienced colleague might have customized and automated parts of their daily workflow to increase their effectiveness and efficiency.&lt;/p&gt;
  3676.  
  3677. &lt;h2&gt;The Bag of Tricks&lt;/h2&gt;
  3678.  
  3679. &lt;p&gt;In general the following is true:&lt;/p&gt;
  3680.  
  3681. &lt;blockquote&gt;
  3682. &lt;p&gt;Experienced individuals have something which sets them apart from the less-experienced.&lt;/p&gt;
  3683. &lt;/blockquote&gt;
  3684.  
  3685. &lt;p&gt;Let us ignore the common qualities that account for uniqueness such as personality, and focus solely on ones skills and experience. Then, it&amp;#39;s possible to identify their &lt;em&gt;bag of tricks&lt;/em&gt;.&lt;/p&gt;
  3686.  
  3687. &lt;p&gt;A bag of tricks is really a simple concept. Often it is a set of small tips and shortcuts used to make certain tasks easier than they normally would be.&lt;/p&gt;
  3688.  
  3689. &lt;blockquote&gt;
  3690. &lt;p&gt;&lt;strong&gt;Bag of Tricks&lt;/strong&gt; &lt;em&gt;| bag əv triks |&lt;/em&gt;
  3691. noun&lt;/p&gt;
  3692.  
  3693. &lt;p&gt;1 &lt;em&gt;informal&lt;/em&gt; a set of ingenious plans, techniques, or resources: &lt;em&gt;I better pull something out of my &lt;b&gt;bag of tricks&lt;/b&gt; to finish this task quickly&lt;/em&gt;&lt;/p&gt;
  3694. &lt;/blockquote&gt;
  3695.  
  3696. &lt;p&gt;In some cases the holder of the bag of tricks clearly knows what resides in their bag. In other cases they might be forgetting some items, which occurs over time as the holder has completely integrated it into their normal repertoire.&lt;/p&gt;
  3697.  
  3698. &lt;h2&gt;Sharing the Bag&lt;/h2&gt;
  3699.  
  3700. &lt;p&gt;Often in workplaces we have various levels of experience such as: &lt;em&gt;Junior&lt;/em&gt;, &lt;em&gt;Intermediate&lt;/em&gt;, &lt;em&gt;Senior&lt;/em&gt;. In our societies we have accepted the following model:&lt;/p&gt;
  3701.  
  3702. &lt;blockquote&gt;
  3703. &lt;p&gt;Experienced individuals can &lt;em&gt;mentor&lt;/em&gt; the less experienced &lt;em&gt;pupils&lt;/em&gt;&lt;/p&gt;
  3704. &lt;/blockquote&gt;
  3705.  
  3706. &lt;p&gt;This is a very simple learning model, and it works. It can be seen in many industries and professions as they promote formal mentorship/apprenticeship programs. These formal programs often have a cost associated with them, although that typically correlates with the quality received. Informal approaches are often executed between colleagues with little structure.&lt;/p&gt;
  3707.  
  3708. &lt;p&gt;Everyone has something worth showing. Someone will be interested in learning it, or improving upon it. Levels of experience does not guarantee one will always be teaching or learning. In a symbiotic relationship everyone will be sharing their bag of tricks.&lt;/p&gt;
  3709.  
  3710. &lt;h3&gt;Pupil&amp;#39;s Perspective&lt;/h3&gt;
  3711.  
  3712. &lt;p&gt;Getting a peek at someone&amp;#39;s bag of tricks is of great benefit for the pupil. If someone offers advice, there should be no hesitation to accept it. A pupil, or anyone for that matter, should always accept the offer of advice since there is no downside to hearing it out. Even if the pupil has already known about the provided advice, the act of hearing it again reinforces it. It might also be possible for the pupil to give back immediately to the mentor.&lt;/p&gt;
  3713.  
  3714. &lt;p&gt;The best thing a pupil can do is to provide feedback and appreciation. By questioning the mentor and asking for additional details, it forces the mentor to inspect and reflect on their bag of tricks. In some situation the tides can change and the mentor will learn from the pupil.&lt;/p&gt;
  3715.  
  3716. &lt;h3&gt;Mentor&amp;#39;s Perspective&lt;/h3&gt;
  3717.  
  3718. &lt;p&gt;Mentors are encouraged to share their bag of tricks, and most are willing to do so. Sharing knowledge has many benefits:&lt;/p&gt;
  3719.  
  3720. &lt;ul&gt;
  3721. &lt;li&gt;Joy in helping someone&lt;/li&gt;
  3722. &lt;li&gt;Gained respect from pupil and others&lt;/li&gt;
  3723. &lt;li&gt;Self-improvement by going over specifics&lt;/li&gt;
  3724. &lt;li&gt;Constructive criticism&lt;/li&gt;
  3725. &lt;li&gt;Encouraging similar behaviour&lt;/li&gt;
  3726. &lt;/ul&gt;
  3727.  
  3728. &lt;p&gt;Sharing and helping can bring much joy to a mentor, which in turn leads to gaining respect from the pupil and others. As previously mentioned, a mentor may not necessarily know of the content of their bag of tricks. A pupil can probe and inquire on specifics to the point where new light is shone on the bag of tricks. By mentoring a pupil, it encourages others to do the same when they have amassed their own bag of tricks.&lt;/p&gt;
  3729.  
  3730. &lt;h2&gt;To Share, or Not to Share?&lt;/h2&gt;
  3731.  
  3732. &lt;p&gt;There might be situations where an individual does not want to share their bag of tricks. One strong reason for this is that they would lose their &lt;em&gt;edge&lt;/em&gt; over their colleagues. This is understandable, although if the environment is amenable to learning then there is no real reason to hold back.&lt;/p&gt;
  3733.  
  3734. &lt;p&gt;If everyone in an environment is holding back on knowledge, it does not sound like a place of growth. Furthermore, if everyone is scared of losing their edge, there are probably greater issues in that environment. If there is no sharing of knowledge between colleagues, everyone is at a disadvantage compared to the alternative.&lt;/p&gt;
  3735.  
  3736. &lt;h2&gt;Means of Sharing&lt;/h2&gt;
  3737.  
  3738. &lt;p&gt;It is clear that sharing is a win-win situation. In a workplace environment sharing knowledge is simple and commonplace. Either the pupil asks for help, or the mentor decides to impart knowledge. If the environment is pro-learning then this behaviour will be encouraged and valued. There might even be an explicit mentoring process in place or less informal opportunities such as &lt;em&gt;lunch and learns&lt;/em&gt;.&lt;/p&gt;
  3739.  
  3740. &lt;p&gt;As mentioned before, anyone can mentor regardless of their skill and experience levels. Everyone should take every opportunity to share something from their bag of tricks. Not only will this immediately benefit others, but what goes around comes around. Sharing can take many forms, including the following:&lt;/p&gt;
  3741.  
  3742. &lt;ul&gt;
  3743. &lt;li&gt;Blog Posts, Public Notes, Documentation&lt;/li&gt;
  3744. &lt;li&gt;Mentoring, Pairing Sessions&lt;/li&gt;
  3745. &lt;li&gt;Talks, Presentations, Demonstrations&lt;/li&gt;
  3746. &lt;li&gt;Video/Audio Recordings&lt;/li&gt;
  3747. &lt;/ul&gt;
  3748.  
  3749. &lt;p&gt;The best way is to reach a broad audience, so that everyone can benefit from it. Thankfully the Internet is an amazing medium to communicate to the masses.&lt;/p&gt;
  3750.  
  3751. &lt;p&gt;Take pride and share your bag of tricks!&lt;/p&gt;
  3752. </description>
  3753.    </item>
  3754.    
  3755.    
  3756.    
  3757.    <item>
  3758.      <title>No Excuses: Verifying RSpec Test Doubles</title>
  3759.      <link>https://techblog.thescore.com/2015/04/28/no-excuses-verifying-rspec-test-doubles/</link>
  3760.      <pubDate>Tue, 28 Apr 2015 00:00:00 +0000</pubDate>
  3761.      <author></author>
  3762.      <guid>https://techblog.thescore.com/2015/04/28/no-excuses-verifying-rspec-test-doubles</guid>
  3763.      <description>&lt;p&gt;Tests which utilize external services or interact with the database are typically the culprits of long-running tests. We want to keep our tests quick. It is possible to mock/stub out long running database and/or external services calls. This reduces the time a test suite takes to execute.&lt;/p&gt;
  3764.  
  3765. &lt;h2&gt;Unsheathe the Double&lt;/h2&gt;
  3766.  
  3767. &lt;p&gt;In Ruby, one approach to mocking is by completely replacing the object of interest with a lightweight &lt;a href=&quot;http://www.rubydoc.info/gems/rspec-mocks/frames#Test_Doubles&quot;&gt;double&lt;/a&gt; using &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt;. Proper usage of a &lt;em&gt;double&lt;/em&gt; can prevent tests from interacting with external services, such as a database (i.e., &lt;code&gt;ActiveRecord&lt;/code&gt;).&lt;/p&gt;
  3768.  
  3769. &lt;p&gt;With respect to RSpec, a &lt;em&gt;double&lt;/em&gt; is created by providing a classname or object, along with a hash of messages and their responses. A &lt;em&gt;double&lt;/em&gt; can only respond using the provided responses to their defined messages (technically there are other messages that a &lt;em&gt;double&lt;/em&gt; can respond to, but for our purpose we do not have to worry about them).&lt;/p&gt;
  3770. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3771. &lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; &amp;quot;Woof&amp;quot;&lt;/span&gt;
  3772. &lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; Double &amp;quot;Dog&amp;quot; received unexpected message :walk with (no args)&lt;/span&gt;
  3773. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3774. &lt;p&gt;In the above example, a &lt;code&gt;&amp;#39;Dog&amp;#39;&lt;/code&gt; &lt;em&gt;double&lt;/em&gt; is created that only knows &lt;code&gt;#talk&lt;/code&gt;. When it receives an unknown message like &lt;code&gt;#walk&lt;/code&gt; an appropriate exception is raised.&lt;/p&gt;
  3775.  
  3776. &lt;h2&gt;Double-Edge Double&lt;/h2&gt;
  3777.  
  3778. &lt;p&gt;A &lt;em&gt;double&lt;/em&gt; aims to abstract away from the concrete concepts that they are &lt;em&gt;standing in for&lt;/em&gt; (i.e., defined classes/methods/attributes/associations and their implementations). This can simplify tests by only dealing with the immediate concerns in a restricted scope. Using &lt;em&gt;doubles&lt;/em&gt; has its perks, but a problem can arise if changes occur to the underlying concrete concepts.&lt;/p&gt;
  3779.  
  3780. &lt;p&gt;Lets examine the following scenario:&lt;/p&gt;
  3781.  
  3782. &lt;blockquote&gt;
  3783. &lt;p&gt;We have an existing class with multiple defined methods. This class and its methods primarily interact with external services. This class and its methods are used within our test suite, in where we have mocked it out using a &lt;em&gt;double&lt;/em&gt;.&lt;/p&gt;
  3784.  
  3785. &lt;p&gt;We decided to modify a method definition on the described class. Our tests continue to pass.&lt;/p&gt;
  3786. &lt;/blockquote&gt;
  3787.  
  3788. &lt;p&gt;Given this scenario we would hope that by modifying the method definition that our existing tests which depend on said method definition to fail. Our tests are using a &lt;em&gt;double&lt;/em&gt; in which the method definition it has defined is still valid, even when the actual method definition has been altered.&lt;/p&gt;
  3789.  
  3790. &lt;p&gt;The consequences of not seeing any failing tests can be serious. Even with minor cosmetic changes, that do not alter functionality, it is still a misleading test. Identifying these tests after the fact can be challenging, as they initially appear to be in fine working order.&lt;/p&gt;
  3791.  
  3792. &lt;p&gt;It is important to always remember to check the usage of &lt;em&gt;doubles&lt;/em&gt; whose underlying concepts are changed. A gem called &lt;a href=&quot;https://github.com/xaviershay/rspec-fire&quot;&gt;rspec-fire&lt;/a&gt; was created to alleviate this task. This gem would verify that a &lt;em&gt;double&lt;/em&gt; is actually mocking an actual method defined on the concrete object. As of RSpec 3.0, &lt;em&gt;rspec-fire&lt;/em&gt; is now obsolete as RSpec has a set of new &lt;a href=&quot;https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/verifying-doubles&quot;&gt;verifying doubles&lt;/a&gt;.&lt;/p&gt;
  3793.  
  3794. &lt;h2&gt;Dance of the Double&lt;/h2&gt;
  3795.  
  3796. &lt;p&gt;A simple example best illustrates the downside of using the original RSpec &lt;em&gt;doubles&lt;/em&gt;. In this example we also show how to replace the &lt;em&gt;double&lt;/em&gt; with the new and improved verifying &lt;em&gt;doubles&lt;/em&gt;, along with their benefits.&lt;/p&gt;
  3797.  
  3798. &lt;p&gt;For this example we are not dealing with a database, although the idea is easily extendable. We can imagine that our test is creating actual entries in the database, thus incurring the performance hit.&lt;/p&gt;
  3799.  
  3800. &lt;hr&gt;
  3801.  
  3802. &lt;p&gt;First we define an &lt;code&gt;Owner&lt;/code&gt; that has a &lt;code&gt;Dog&lt;/code&gt;. The &lt;code&gt;Dog&lt;/code&gt; responds to &lt;code&gt;#talk&lt;/code&gt;. In addition we have a corresponding test that uses a &lt;em&gt;double&lt;/em&gt; to mock out the &lt;code&gt;Dog&lt;/code&gt; which responds to &lt;code&gt;#talk&lt;/code&gt;.&lt;/p&gt;
  3803. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Owner&lt;/span&gt;
  3804. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;
  3805.  
  3806. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3807. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@dog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;
  3808. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3809. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3810.  
  3811. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
  3812. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;talk&lt;/span&gt;
  3813. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Woof&amp;#39;&lt;/span&gt;
  3814. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3815. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3816.  
  3817. &lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3818. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3819. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3820. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3821. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3822.  
  3823. &lt;span class=&quot;c1&quot;&gt;# 1 example, 0 failures&lt;/span&gt;
  3824. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3825. &lt;hr&gt;
  3826.  
  3827. &lt;p&gt;We decide to change &lt;code&gt;Dog#talk&lt;/code&gt; to &lt;code&gt;Dog#bark&lt;/code&gt;, but forget to update the test.&lt;/p&gt;
  3828. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Owner&lt;/span&gt;
  3829. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;
  3830.  
  3831. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3832. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@dog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;
  3833. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3834. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3835.  
  3836. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
  3837. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bark&lt;/span&gt;
  3838. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Woof&amp;#39;&lt;/span&gt;
  3839. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3840. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3841.  
  3842. &lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3843. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3844. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3845. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3846. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3847.  
  3848. &lt;span class=&quot;c1&quot;&gt;# 1 example, 0 failures&lt;/span&gt;
  3849. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3850. &lt;p&gt;From our test&amp;#39;s perspective everything is still fine, since our &lt;em&gt;double&lt;/em&gt; still responds to &lt;code&gt;#talk&lt;/code&gt;. Ideally we would want our test here to fail since it is not accurately matching the interface defined by an actual &lt;code&gt;Dog&lt;/code&gt; instance.&lt;/p&gt;
  3851.  
  3852. &lt;p&gt;This is a problem. It is possible to completely &lt;em&gt;forget&lt;/em&gt; about fixing the test, since it technically passed.&lt;/p&gt;
  3853.  
  3854. &lt;hr&gt;
  3855.  
  3856. &lt;p&gt;This time we use a verifying &lt;em&gt;double&lt;/em&gt; that RSpec provides such as an &lt;code&gt;instance_double&lt;/code&gt;. This ensures that the messages the &lt;em&gt;double&lt;/em&gt; receives are verified against the interface defined by the concrete object.&lt;/p&gt;
  3857. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;
  3858.  
  3859. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Owner&lt;/span&gt;
  3860. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;
  3861.  
  3862. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3863. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@dog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;
  3864. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3865. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3866.  
  3867. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
  3868. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bark&lt;/span&gt;
  3869. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Woof&amp;#39;&lt;/span&gt;
  3870. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3871. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3872.  
  3873. &lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3874. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3875. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance_double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3876. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3877. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3878.  
  3879. &lt;span class=&quot;c1&quot;&gt;# Failure/Error: let(:dog) { instance_double(&amp;#39;Dog&amp;#39;, talk: &amp;#39;Fake Woof&amp;#39;) }&lt;/span&gt;
  3880. &lt;span class=&quot;c1&quot;&gt;#   Dog does not implement: talk&lt;/span&gt;
  3881. &lt;span class=&quot;c1&quot;&gt;# 1 example, 1 failure&lt;/span&gt;
  3882. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3883. &lt;p&gt;Now we have a failing test! This is what we expect to happen as our &lt;em&gt;double&lt;/em&gt; is attempting to using an unimplemented method. This feedback allows us to take the corrective action on our tests to ensure that they are not forgotten and invalid.&lt;/p&gt;
  3884.  
  3885. &lt;hr&gt;
  3886.  
  3887. &lt;p&gt;We now decide to change the number of arguments on &lt;code&gt;Dog#bark&lt;/code&gt;. Again with the old non-verifying &lt;em&gt;double&lt;/em&gt; our test would again simply pass. The verifying &lt;em&gt;doubles&lt;/em&gt; also check that the number of arguments match the defined interface&amp;#39;s number of arguments.&lt;/p&gt;
  3888. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;
  3889.  
  3890. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Owner&lt;/span&gt;
  3891. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;
  3892.  
  3893. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3894. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@dog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;
  3895. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3896. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3897.  
  3898. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
  3899. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3900. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loud&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Woof!!&amp;#39;&lt;/span&gt;
  3901. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3902. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3903.  
  3904. &lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  3905. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3906. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance_double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3907. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Fake Woof&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  3908. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3909.  
  3910. &lt;span class=&quot;c1&quot;&gt;# Failure/Error: let(:dog) { instance_double(&amp;#39;Dog&amp;#39;, talk: &amp;#39;Fake Woof&amp;#39;) }&lt;/span&gt;
  3911. &lt;span class=&quot;c1&quot;&gt;#   Wrong number of arguments. Expected 1, got 0.&lt;/span&gt;
  3912. &lt;span class=&quot;c1&quot;&gt;# 1 example, 1 failure&lt;/span&gt;
  3913. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3914. &lt;h2&gt;Double Development&lt;/h2&gt;
  3915.  
  3916. &lt;p&gt;If the underlying class is loaded &lt;code&gt;instance_double&lt;/code&gt; will do the verifying on the class. In the situation where the class is not loaded than it acts as a normal &lt;em&gt;double&lt;/em&gt;.&lt;/p&gt;
  3917.  
  3918. &lt;p&gt;During development an &lt;code&gt;instance_double&lt;/code&gt; allows one to develop in isolation if the class you are mocking out does not yet exist. Eventually when the test is more-or-less complete, it is possible to simply &lt;em&gt;load&lt;/em&gt; the class and the &lt;code&gt;instance_double&lt;/code&gt; will start to verify on the loaded class.&lt;/p&gt;
  3919.  
  3920. &lt;p&gt;In addition, during development you can use &lt;a href=&quot;https://github.com/nevir/rubocop-rspec/blob/master/lib/rubocop/cop/rspec/verified_doubles.rb&quot;&gt;&lt;code&gt;rubocop-rspec&lt;/code&gt;&lt;/a&gt; to ensure you always verify your &lt;em&gt;doubles&lt;/em&gt;.&lt;/p&gt;
  3921.  
  3922. &lt;h2&gt;Concluding Double&lt;/h2&gt;
  3923.  
  3924. &lt;p&gt;&lt;strong&gt;TL;DR -- There are no excuses, verify your RSpec test doubles.&lt;/strong&gt;&lt;/p&gt;
  3925.  
  3926. &lt;p&gt;Verifying your RSpec test &lt;em&gt;doubles&lt;/em&gt; can, and will, save you from many headaches down the road. In most cases the change required to use verifying &lt;em&gt;doubles&lt;/em&gt; is relatively easy. The benefits are clear, worthwhile and your test suite will thank you.&lt;/p&gt;
  3927. </description>
  3928.    </item>
  3929.    
  3930.    
  3931.    
  3932.    <item>
  3933.      <title>Mutate your Rack middleware's env!</title>
  3934.      <link>https://techblog.thescore.com/2014/12/04/modify-your-racks-env-hash/</link>
  3935.      <pubDate>Thu, 04 Dec 2014 00:00:00 +0000</pubDate>
  3936.      <author></author>
  3937.      <guid>https://techblog.thescore.com/2014/12/04/modify-your-racks-env-hash</guid>
  3938.      <description>&lt;p&gt;If you&amp;#39;ve been a Ruby developer for a while, chances are that you&amp;#39;ve written some kind of Rack middleware. Rack is a pretty big part of web development with Rails, and here at theScore we&amp;#39;ve developed a few, small pieces of middleware to add additional data to our requests so that our Rails applications can use it.&lt;/p&gt;
  3939.  
  3940. &lt;p&gt;If you&amp;#39;re not familiar with Rack or Rack middleware, I would recommend &lt;a href=&quot;http://southdesign.de/blog/rack.html&quot;&gt;this article on Rack&lt;/a&gt; and &lt;a href=&quot;http://stackoverflow.com/questions/2256569/what-is-rack-middleware&quot;&gt;this stackoverflow question on the middleware&lt;/a&gt;.&lt;/p&gt;
  3941.  
  3942. &lt;h2&gt;Background and Problem&lt;/h2&gt;
  3943.  
  3944. &lt;p&gt;While looking to integrate &lt;a href=&quot;http://yellerapp.com/&quot;&gt;Yeller&lt;/a&gt;, we ran into a bug in our middleware. The bug came up because Yeller was looking for Rails specific data that was being attached to the request&amp;#39;s &lt;code&gt;env&lt;/code&gt; hash when an error was raised. In this case, during a request, &lt;code&gt;ActionController::Metal&lt;/code&gt; sets &lt;code&gt;action_controller.instance&lt;/code&gt; on the &lt;code&gt;env&lt;/code&gt; hash:&lt;/p&gt;
  3945. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#:nodoc:&lt;/span&gt;
  3946. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@_request&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;
  3947. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@_env&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;
  3948. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@_env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;action_controller.instance&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
  3949. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3950. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;to_a&lt;/span&gt;
  3951. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3952. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3953. &lt;p&gt;(&lt;a href=&quot;https://github.com/rails/rails/blob/7c4bfe1c954ef90acf4f790e46fcbbd07d85af3e/actionpack/lib/action_controller/metal.rb#L195&quot;&gt;source pinned to rails 4.1.4&lt;/a&gt;)&lt;/p&gt;
  3954.  
  3955. &lt;p&gt;In Yeller, the error reporting code extracts that information to give us a better idea of what controller and action was involved when the error occurred:&lt;/p&gt;
  3956. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;render_exception_with_yeller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3957. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  3958. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;action_controller.instance&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  3959. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  3960. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;
  3961. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  3962. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  3963. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  3964. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# code without location information available&lt;/span&gt;
  3965. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3966. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3967. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3968. &lt;p&gt;(&lt;a href=&quot;https://github.com/tcrayford/yeller_ruby/blob/355cb6b874c6ddf0b3ee1d3d5012b9db16b7e0c0/lib/yeller/rails.rb#L58&quot;&gt;source pinned to yeller_ruby 0.2.2&lt;/a&gt;)&lt;/p&gt;
  3969.  
  3970. &lt;p&gt;When we ran Yeller&amp;#39;s verification Rake task, our errors were being sent to Yeller&amp;#39;s servers, but the Rake task also told us that we were &lt;em&gt;missing&lt;/em&gt; that Rails specific information (the location, for example). So, we scratched our heads a bit. Error reporting worked in new Rails applications, so it had to be something in our code.&lt;/p&gt;
  3971.  
  3972. &lt;p&gt;I started by removing our custom middleware -- we had three of them and I removed all of them. The rake task succeeded! So I started adding them in one-by-one until I added one and the rake task failed. At this point, I had narrowed the code down to about 3 lines and they looked something like this:&lt;/p&gt;
  3973. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3974. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;HTTP_X_API_VERSION&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;presence&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_api_version&lt;/span&gt;
  3975. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Handle bad API versions with commas in them&lt;/span&gt;
  3976. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;the_score.api_version&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApiVersionDecorator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  3977. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  3978. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  3979. &lt;h2&gt;The Solution&lt;/h2&gt;
  3980.  
  3981. &lt;p&gt;After showing Yeller&amp;#39;s team the middleware, they saw the problem! &lt;code&gt;env.merge&lt;/code&gt; returns a &lt;em&gt;new&lt;/em&gt; hash. The &amp;#39;gotcha&amp;#39; in this case is this:&lt;/p&gt;
  3982.  
  3983. &lt;blockquote&gt;
  3984. &lt;p&gt;The scope of the &lt;code&gt;env&lt;/code&gt; hash exists &lt;em&gt;after&lt;/em&gt; calls to &lt;code&gt;@app.call&lt;/code&gt; finish. Because of this, &lt;code&gt;@app.call&lt;/code&gt; should be invoked with the same &lt;code&gt;env&lt;/code&gt; hash passed into the method.&lt;/p&gt;
  3985. &lt;/blockquote&gt;
  3986.  
  3987. &lt;p&gt;Middleware that rely on subsequent middleware (or the app) to set information (e.g. &lt;code&gt;action_controller.instance&lt;/code&gt;) on the &lt;code&gt;env&lt;/code&gt; hash also rely on the fact that the subsequent middleware won&amp;#39;t pass a &lt;em&gt;different&lt;/em&gt; &lt;code&gt;env&lt;/code&gt; hash to the subsequent middleware. If the subsequent middleware &lt;em&gt;do&lt;/em&gt; pass a different hash, here&amp;#39;s what happens:&lt;/p&gt;
  3988.  
  3989. &lt;ol&gt;
  3990. &lt;li&gt;The new &lt;code&gt;env&lt;/code&gt; is only used from that point forward in the middleware stack&lt;/li&gt;
  3991. &lt;li&gt;Needless memory is allocated because you&amp;#39;ve just duplicated every request&amp;#39;s information in a separate &lt;code&gt;env&lt;/code&gt;&lt;/li&gt;
  3992. &lt;/ol&gt;
  3993.  
  3994. &lt;p&gt;The fix and proper way to do this is to instead &lt;em&gt;mutate&lt;/em&gt; your hash through &lt;code&gt;Hash#[]&lt;/code&gt;, &lt;code&gt;Hash#merge!&lt;/code&gt;, &lt;code&gt;Hash#store&lt;/code&gt;, and so forth. A new hash isn&amp;#39;t created when you use these methods. Here&amp;#39;s the fixed code:&lt;/p&gt;
  3995. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  3996. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;HTTP_X_API_VERSION&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;presence&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_api_version&lt;/span&gt;
  3997. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Handle bad API versions with commas in them&lt;/span&gt;
  3998. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merge!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;the_score.api_version&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApiVersionDecorator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  3999. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4000. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4001. &lt;p&gt;If you want to see the problem happening in your console with a &amp;#39;real&amp;#39; Rack request (OK, not really, it uses &lt;code&gt;Rack::MockRequest&lt;/code&gt; in the tests), you can clone &lt;a href=&quot;https://github.com/Nitrodist/rack-middleware-gotcha&quot;&gt;this project&lt;/a&gt; that I&amp;#39;ve created, &lt;code&gt;bundle install&lt;/code&gt;, and run &lt;code&gt;rspec&lt;/code&gt;.&lt;/p&gt;
  4002.  
  4003. &lt;h2&gt;Bonus: How to Test It&lt;/h2&gt;
  4004.  
  4005. &lt;p&gt;We had a suite of tests using rpsec around these simple pieces of middleware, so we added something like this to every test:&lt;/p&gt;
  4006. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyMiddleware&lt;/span&gt;
  4007. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4008. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;
  4009. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4010.  
  4011. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4012. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4013. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4014. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4015.  
  4016. &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MyMiddleware&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  4017. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4018. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4019.  
  4020. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MyMiddleware&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4021.  
  4022. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;calls app with the same env hash&amp;quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  4023. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4024. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4025. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4026. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4027. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4028. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4029.  
  4030. &lt;p&gt;Rack is fairly straightforward, but there definitely are some &amp;#39;gotchas&amp;#39; that you can run into. This is just one example and there are a few more that you need to consider when creating Rack middleware. In a future post, I&amp;#39;ll explain another &amp;#39;gotcha&amp;#39; with regards to multithreading and Rack middleware.&lt;/p&gt;
  4031.  
  4032. &lt;p&gt;Another interesting point is that this is a &amp;#39;bug&amp;#39; that has been in our code base for a little over two years without being noticed. It just goes to show that every once in a while, you should take a look at the pieces of middleware that your application depends on: they may be misbehaving like ours was!&lt;/p&gt;
  4033. </description>
  4034.    </item>
  4035.    
  4036.    
  4037.    
  4038.    <item>
  4039.      <title>Are Your Cache-Control Directives Doing What They Are Supposed to Do?</title>
  4040.      <link>https://techblog.thescore.com/2014/11/19/are-your-cache-control-directives-doing-what-they-are-supposed-to-do/</link>
  4041.      <pubDate>Wed, 19 Nov 2014 00:00:00 +0000</pubDate>
  4042.      <author></author>
  4043.      <guid>https://techblog.thescore.com/2014/11/19/are-your-cache-control-directives-doing-what-they-are-supposed-to-do</guid>
  4044.      <description>&lt;p&gt;HTTP offers very powerful support for &lt;a href=&quot;http://tools.ietf.org/html/rfc2616#section-13&quot;&gt;caching&lt;/a&gt;:&lt;/p&gt;
  4045.  
  4046. &lt;blockquote&gt;
  4047. &lt;p&gt;The goal of caching in HTTP/1.1 is to eliminate the need to send requests in many cases,
  4048. and to eliminate the need to send full responses in many other cases. The former reduces the
  4049. number of network round-trips required for many operations; we use an &amp;quot;expiration&amp;quot; mechanism
  4050. for this purpose (see section 13.2). The latter reduces network bandwidth requirements; we
  4051. use a &amp;quot;validation&amp;quot; mechanism for this purpose (see section 13.3).&lt;/p&gt;
  4052. &lt;/blockquote&gt;
  4053.  
  4054. &lt;p&gt;If you&amp;#39;re not familiar with how the &amp;quot;expiration&amp;quot; and &amp;quot;validation&amp;quot; mechanisms work in different
  4055. kinds of HTTP caches, you should start with &lt;a href=&quot;http://tomayko.com/writings/things-caches-do&quot;&gt;Things Caches Do&lt;/a&gt;
  4056. by Ryan Tomayko. For a more in-depth look at HTTP caching, check out
  4057. &lt;a href=&quot;https://www.mnot.net/cache_docs&quot;&gt;Caching Tutorial for Web Authors and Webmasters&lt;/a&gt; by Mark
  4058. Nottingham.&lt;/p&gt;
  4059.  
  4060. &lt;h2&gt;Private and Shared Caches&lt;/h2&gt;
  4061.  
  4062. &lt;p&gt;Private caches can only serve a single user, whereas shared caches can serve any number
  4063. of users. To take full advantage of HTTP caching, you need to have a private cache on your
  4064. clients, and a shared gateway cache (also known as reverse proxy cache or surrogate cache)
  4065. just in front of your backend.&lt;/p&gt;
  4066.  
  4067. &lt;p&gt;The classic example of a private client cache is the browser cache. &lt;a href=&quot;http://developer.android.com/reference/android/net/http/HttpResponseCache.html&quot;&gt;HttpResponseCache&lt;/a&gt;
  4068. and &lt;a href=&quot;http://nshipster.com/nsurlcache&quot;&gt;NSURLCache&lt;/a&gt; are examples of private client caches for
  4069. Android and iOS respectively.&lt;/p&gt;
  4070.  
  4071. &lt;p&gt;&lt;a href=&quot;https://www.varnish-cache.org&quot;&gt;Varnish&lt;/a&gt; and &lt;a href=&quot;http://www.squid-cache.org&quot;&gt;Squid&lt;/a&gt; are examples
  4072. of gateway caches. CDNs such as &lt;a href=&quot;http://www.akamai.com&quot;&gt;Akamai&lt;/a&gt; and &lt;a href=&quot;http://www.fastly.com&quot;&gt;Fastly&lt;/a&gt;
  4073. are essentially geo-distributed gateway caches.&lt;/p&gt;
  4074.  
  4075. &lt;h2&gt;&lt;code&gt;Cache-Control&lt;/code&gt; Header&lt;/h2&gt;
  4076.  
  4077. &lt;p&gt;&lt;code&gt;Cache-Control&lt;/code&gt; header is used to control how the caches between your user and application
  4078. behave. It can be used as a request or response header (similar to &lt;code&gt;Content-Type&lt;/code&gt;). But, for
  4079. the purposes of this blog post, we&amp;#39;re going to explore using &lt;code&gt;Cache-Control&lt;/code&gt; as a response
  4080. header from your backend application.&lt;/p&gt;
  4081.  
  4082. &lt;p&gt;Note that there can be any number of shared caches between the private cache on the client,
  4083. and the gateway cache right in front of your backend. If you use HTTPS, you don&amp;#39;t have to
  4084. worry about how these intermediate caches (i.e. proxy cache installed on a company network)
  4085. would behave. At theScore, we leverage HTTP caching in all of our APIs. Furthermore, all of
  4086. our APIs use HTTPS as such we don&amp;#39;t have to worry about intermediate caches, and this blog
  4087. post will do the same for the most part.&lt;/p&gt;
  4088.  
  4089. &lt;h2&gt;&lt;code&gt;Cache-Control&lt;/code&gt; Directives&lt;/h2&gt;
  4090.  
  4091. &lt;p&gt;If you use &lt;code&gt;Cache-Control&lt;/code&gt; as a response header, there are only 9
  4092. &lt;a href=&quot;https://tools.ietf.org/html/rfc7234#section-5.2.2&quot;&gt;directives&lt;/a&gt; to worry about. That&amp;#39;s it!
  4093. Here&amp;#39;s a summary of the directives from &lt;a href=&quot;https://www.mnot.net/cache_docs&quot;&gt;Caching Tutorial for Web Authors and Webmasters&lt;/a&gt;
  4094. by Mark Nottingham:&lt;/p&gt;
  4095.  
  4096. &lt;blockquote&gt;
  4097. &lt;p&gt;Useful &lt;code&gt;Cache-Control&lt;/code&gt; response headers include:&lt;/p&gt;
  4098.  
  4099. &lt;ul&gt;
  4100. &lt;li&gt;&lt;p&gt;&lt;code&gt;max-age=[seconds]&lt;/code&gt; - specifies the maximum amount of time that a representation will be
  4101. considered fresh. Similar to &lt;code&gt;Expires&lt;/code&gt;, this directive is relative to the time of the request,
  4102. rather than absolute. &lt;code&gt;[seconds]&lt;/code&gt; is the number of seconds from the time of the request you
  4103. wish the representation to be fresh for.&lt;/p&gt;&lt;/li&gt;
  4104. &lt;li&gt;&lt;p&gt;&lt;code&gt;s-maxage=[seconds]&lt;/code&gt; - similar to &lt;code&gt;max-age&lt;/code&gt;, except that it only applies to shared
  4105. (e.g., proxy) caches.&lt;/p&gt;&lt;/li&gt;
  4106. &lt;li&gt;&lt;p&gt;&lt;code&gt;public&lt;/code&gt; - marks authenticated responses as cacheable; normally, if HTTP authentication
  4107. is required, responses are automatically private.&lt;/p&gt;&lt;/li&gt;
  4108. &lt;li&gt;&lt;p&gt;&lt;code&gt;private&lt;/code&gt; - allows caches that are specific to one user (e.g., in a browser) to store the
  4109. response; shared caches (e.g., in a proxy) may not.&lt;/p&gt;&lt;/li&gt;
  4110. &lt;li&gt;&lt;p&gt;&lt;code&gt;no-cache&lt;/code&gt; - forces caches to submit the request to the origin server for validation
  4111. before releasing a cached copy, every time. This is useful to assure that authentication
  4112. is respected (in combination with public), or to maintain rigid freshness, without
  4113. sacrificing all of the benefits of caching.&lt;/p&gt;&lt;/li&gt;
  4114. &lt;li&gt;&lt;p&gt;&lt;code&gt;no-store&lt;/code&gt; - instructs caches not to keep a copy of the representation under any conditions.&lt;/p&gt;&lt;/li&gt;
  4115. &lt;li&gt;&lt;p&gt;&lt;code&gt;must-revalidate&lt;/code&gt; - tells caches that they must obey any freshness information you give
  4116. them about a representation. HTTP allows caches to serve stale representations under special
  4117. conditions; by specifying this header, you’re telling the cache that you want it to strictly
  4118. follow your rules.&lt;/p&gt;&lt;/li&gt;
  4119. &lt;li&gt;&lt;p&gt;&lt;code&gt;proxy-revalidate&lt;/code&gt; — similar to &lt;code&gt;must-revalidate&lt;/code&gt;, except that it only applies to proxy caches.&lt;/p&gt;&lt;/li&gt;
  4120. &lt;/ul&gt;
  4121. &lt;/blockquote&gt;
  4122.  
  4123. &lt;p&gt;You can mix and match these directives in a number of different combinations:&lt;/p&gt;
  4124.  
  4125. &lt;ul&gt;
  4126. &lt;li&gt;&lt;code&gt;Cache-Control: private, max-age=10&lt;/code&gt;&lt;/li&gt;
  4127. &lt;li&gt;&lt;code&gt;Cache-Control: max-age=10, s-maxage=300&lt;/code&gt;&lt;/li&gt;
  4128. &lt;li&gt;&lt;code&gt;Cache-Control: max-age=10, s-maxage=300, proxy-revalidate&lt;/code&gt;&lt;/li&gt;
  4129. &lt;/ul&gt;
  4130.  
  4131. &lt;p&gt;As you can see, the directives are pretty straightforward to understand. They&amp;#39;re easy to use as
  4132. well if you assume that all the caches between your end user and application correctly implement
  4133. the spec. Unfortunately, as with any spec, you can&amp;#39;t make that assumption. You need to be aware
  4134. of any spec misinterpretations in the implementation of the caches that you&amp;#39;re using, and
  4135. properly account for them.&lt;/p&gt;
  4136.  
  4137. &lt;h2&gt;Getting &lt;code&gt;Cache-Control&lt;/code&gt; Right&lt;/h2&gt;
  4138.  
  4139. &lt;p&gt;Let&amp;#39;s look at a scenario, in which we consider most of the &lt;code&gt;Cache-Control&lt;/code&gt; directives from
  4140. above in the process of arriving at the final combination.&lt;/p&gt;
  4141.  
  4142. &lt;p&gt;Suppose you have a mobile application that consumes a REST API, and one of the API end-points
  4143. has &lt;code&gt;Cache-Control: max-age=7200&lt;/code&gt; (the response is considered fresh for 2 hours). Further suppose
  4144. that due to a new bug in the API, the response for this end-point changes in a way that causes
  4145. the app to crash. You can fix the bug and purge the cache for this end-point on your gateway
  4146. cache (gateway caches generally give you this kind of control). But, this won&amp;#39;t stop the clients
  4147. that have the bad response cached from crashing. In the worst case, these clients will continue
  4148. to crash for up to 2 hours. You essentially get stuck with stale content, and just have to
  4149. wait it out.&lt;/p&gt;
  4150.  
  4151. &lt;p&gt;To avoid getting stuck with stale content on a client, we want to make sure that the client
  4152. always revalidates the content before serving it from the client cache. We would still save
  4153. bandwidth with this approach. Having said that, we still want the original behaviour (the response
  4154. is considered fresh for 2 hours) on the gateway cache.&lt;/p&gt;
  4155.  
  4156. &lt;p&gt;It looks like &lt;code&gt;no-cache&lt;/code&gt; directive fits the bill perfectly as it allows you &amp;quot;to maintain rigid
  4157. freshness, without sacrificing all of the benefits of caching.&amp;quot; Note that &lt;code&gt;no-cache&lt;/code&gt; does not
  4158. mean &amp;quot;not to cache&amp;quot; (you use &lt;code&gt;no-store&lt;/code&gt; for that). But, sooner than later, we would discover
  4159. that some popular client caches (certain versions of IE and Firefox) treat &lt;code&gt;no-cache&lt;/code&gt; as
  4160. &lt;code&gt;no-store&lt;/code&gt;. So, let&amp;#39;s abandon this option and start with the following instead:&lt;/p&gt;
  4161. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Cache-Control: max-age=0
  4162. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4163. &lt;p&gt;With this in place, the response expires right away, which will force revalidation. However,
  4164. this will force revalidation on the gateway cache as well. In other words, all the requests
  4165. will end up hitting the backend. This can be fixed by using &lt;code&gt;s-maxage&lt;/code&gt;:&lt;/p&gt;
  4166. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Cache-Control: max-age=0, s-maxage=7200
  4167. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4168. &lt;p&gt;Caches can be configured to serve stale content (i.e. in case of network error during
  4169. revalidation), but we can enforce that stale content is not served even with such
  4170. configuration or under any other circumstances:&lt;/p&gt;
  4171. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Cache-Control: max-age=0, s-maxage=7200, must-revalidate
  4172. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4173. &lt;p&gt;On a second look, we might decide it&amp;#39;s best to serve stale content from client cache since
  4174. revalidation can fail due to network errors that can happen more often than not in a
  4175. mobile application. However, we still don&amp;#39;t want our gateway cache to serve stale content under
  4176. any circumstances. We can achieve this by using &lt;code&gt;proxy-revalidate&lt;/code&gt; instead of &lt;code&gt;must-revalidate&lt;/code&gt;:&lt;/p&gt;
  4177. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Cache-Control: max-age=0, s-maxage=7200, proxy-revalidate
  4178. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4179. &lt;p&gt;This is the final combination of directives we need to handle the scenario we originally
  4180. described at the beginning of this section.&lt;/p&gt;
  4181.  
  4182. &lt;h2&gt;Beyond &lt;code&gt;Cache-Control&lt;/code&gt;&lt;/h2&gt;
  4183.  
  4184. &lt;p&gt;The solution we came up above will not work if we need to worry about any intermediate
  4185. shared (proxy) caches between the client cache and gateway cache (this is the case if we
  4186. don&amp;#39;t use HTTPS). This is because &lt;code&gt;s-maxage&lt;/code&gt; applies to those caches as well, and we have no
  4187. control over purging cached content on them. We need a way to specify directives that
  4188. only apply to our gateway cache. Enter &lt;a href=&quot;http://tools.ietf.org/html/draft-nottingham-surrogates-00#section-3.6.2&quot;&gt;Surrogate-Control&lt;/a&gt;.&lt;/p&gt;
  4189.  
  4190. &lt;p&gt;&lt;code&gt;Surrogate-Control&lt;/code&gt; is like &lt;code&gt;Cache-Control&lt;/code&gt; except it only applies to gateway caches (also
  4191. known as surrogate caches). It is part of a draft spec, but some popular gateway caches
  4192. (i.e. Squid and Fastly) already support it. The gateway caches that don&amp;#39;t support
  4193. &lt;code&gt;Surrogate-Control&lt;/code&gt; usually have their own custom header that works the same way (i.e.
  4194. Akamai has &lt;code&gt;Edge-Control&lt;/code&gt;).&lt;/p&gt;
  4195.  
  4196. &lt;p&gt;Now you know about &lt;code&gt;Surrogate-Control&lt;/code&gt;, the final solution to handle the scenario described
  4197. in the last section is to use both &lt;code&gt;Cache-Control&lt;/code&gt; and &lt;code&gt;Surrogate-Control&lt;/code&gt;:&lt;/p&gt;
  4198. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Cache-Control: max-age=0
  4199. Surrogate-Control: max-age=7200, must-revalidate
  4200. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4201. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4202.  
  4203. &lt;p&gt;HTTP caching is powerful, but severely underused. Even when it is used, people rarely
  4204. leverage it to its maximum potential. Take the time to read all of &lt;a href=&quot;http://tools.ietf.org/html/rfc2616#section-13&quot;&gt;Caching in HTTP&lt;/a&gt;
  4205. section of HTTP/1.1 RFC now (even if you&amp;#39;ve already read it before). And, don&amp;#39;t forget
  4206. to pay close attention to the subsection on &lt;code&gt;Cache-Control&lt;/code&gt;.&lt;/p&gt;
  4207. </description>
  4208.    </item>
  4209.    
  4210.    
  4211.    
  4212.    <item>
  4213.      <title>The Toiling Programmer: Succeeding as a Developer</title>
  4214.      <link>https://techblog.thescore.com/2014/10/22/succeeding-as-a-developer/</link>
  4215.      <pubDate>Wed, 22 Oct 2014 00:00:00 +0000</pubDate>
  4216.      <author></author>
  4217.      <guid>https://techblog.thescore.com/2014/10/22/succeeding-as-a-developer</guid>
  4218.      <description>&lt;p&gt;Not sure if you&amp;#39;re a toiling programmer? Ask yourself these questions:&lt;/p&gt;
  4219.  
  4220. &lt;ul&gt;
  4221. &lt;li&gt;Do you feel like you&amp;#39;re drowning, that there&amp;#39;s no end in sight?&lt;/li&gt;
  4222. &lt;li&gt;Do you get stuck on long running problems (1 or more days long)?&lt;/li&gt;
  4223. &lt;li&gt;Are you unsure what needs to be done on a &lt;a href=&quot;http://en.wikipedia.org/wiki/Issue_tracking_system#Issues&quot;&gt;ticket&lt;/a&gt;, and yet you continue to work on it?&lt;/li&gt;
  4224. &lt;li&gt;Do you often get feedback that functionality is missing after you&amp;#39;ve shipped the work?&lt;/li&gt;
  4225. &lt;/ul&gt;
  4226.  
  4227. &lt;p&gt;That feeling of dread is cancerous towards your work, your relationships at work and your well being. I&amp;#39;ve personally experienced it many times during my short career -- it&amp;#39;s not a fun experience and that&amp;#39;s why I am writing about it.&lt;/p&gt;
  4228.  
  4229. &lt;p&gt;&lt;strong&gt;Work doesn&amp;#39;t have be like that&lt;/strong&gt;. You can stop the cycle and feel way better about yourself!&lt;/p&gt;
  4230.  
  4231. &lt;h2&gt;Manage expectations of other people&lt;/h2&gt;
  4232.  
  4233. &lt;p&gt;For me, one of the feelings of dread comes from thinking of what other people are thinking of myself -- whether or not I&amp;#39;m doing something quickly enough, or thorough enough. Things of that nature.&lt;/p&gt;
  4234.  
  4235. &lt;p&gt;Part of that thinking comes from not talking to those people often enough. People are quite understanding of someone doing work because that&amp;#39;s what most people are doing at work!&lt;/p&gt;
  4236.  
  4237. &lt;p&gt;Talk to the people about the project that you&amp;#39;re working on. Talk about the hardships. Talk about the small victories, the frustrations; people who care about your work want to know that you&amp;#39;re interested in it and that you&amp;#39;re making progress! Even the &amp;#39;failures&amp;#39; can be celebrated!&lt;/p&gt;
  4238.  
  4239. &lt;p&gt;Being up-front and honest will save you tons of time and frustration. Talk to the person who knows that part of the system the best -- they will have great insight into how it works and the &amp;#39;gotchas&amp;#39; in the system. If you can spend 8 hours solving a problem without help and 30 minutes talking to a colleague about it to get the same result (and the added benefit of having trust between you and your colleague), surely it&amp;#39;s worth it to reach out and talk about your problems.&lt;/p&gt;
  4240.  
  4241. &lt;p&gt;Admit to your colleagues that you don&amp;#39;t know what you&amp;#39;re doing (one of my favourite phrases) and seek their advice. It&amp;#39;s very liberating! Here are the upsides:&lt;/p&gt;
  4242.  
  4243. &lt;ul&gt;
  4244. &lt;li&gt;You solve the problem quickly with the help of your colleagues&lt;/li&gt;
  4245. &lt;li&gt;You gain the trust of your colleagues (and boss?)&lt;/li&gt;
  4246. &lt;li&gt;The problem actually &lt;em&gt;is&lt;/em&gt; non-trivial and you &lt;em&gt;did&lt;/em&gt; need the help (think of the hours/days you just saved of banging your head against a wall)&lt;/li&gt;
  4247. &lt;/ul&gt;
  4248.  
  4249. &lt;p&gt;Here are the downsides:&lt;/p&gt;
  4250.  
  4251. &lt;ul&gt;
  4252. &lt;li&gt;You expose yourself as a fraud (hint: you&amp;#39;re not a fraud)&lt;/li&gt;
  4253. &lt;li&gt;Loss of pride&lt;/li&gt;
  4254. &lt;li&gt;Other people think less of you&lt;/li&gt;
  4255. &lt;/ul&gt;
  4256.  
  4257. &lt;p&gt;On that last note, I don&amp;#39;t think that&amp;#39;s a valid concern. If someone thinks less of you in this case, then that relationship is &lt;em&gt;toxic&lt;/em&gt;. Not being able to rely on the expertise, wisdom, and experience of your co-workers at work is a terrible burden to carry.  If someone comes out and exposes themselves as a person who would do that to you, then at least you now know what kind of person they are (net gain for you!).&lt;/p&gt;
  4258.  
  4259. &lt;p&gt;Learn to rely on your co-workers and to confide in them.&lt;/p&gt;
  4260.  
  4261. &lt;h3&gt;Who to talk to&lt;/h3&gt;
  4262.  
  4263. &lt;p&gt;Talk to your &lt;strong&gt;boss&lt;/strong&gt;. Your boss has the most control over your future at your company. Besides the obvious personal reasons, your boss needs to know how things are going because they are often involved with planning for the future with regards to you, your team, and the rest of the company.&lt;/p&gt;
  4264.  
  4265. &lt;p&gt;Talk to your &lt;strong&gt;project manager&lt;/strong&gt;. If you have a PM, then they are going to need to know how long something is going to take. Periodic feedback on tasks is just as important as estimates because things often take longer than expected! Keep him up to date on things to maintain that relationship!&lt;/p&gt;
  4266.  
  4267. &lt;p&gt;Talk to your &lt;strong&gt;colleagues&lt;/strong&gt;. There may be a lot of expertise within your team on the problem that you&amp;#39;re working on. Draw upon them and you&amp;#39;ll be a happier person. Regular stand-ups usually allow for this communication to happen.&lt;/p&gt;
  4268.  
  4269. &lt;p&gt;Talk to &lt;strong&gt;stakeholders&lt;/strong&gt;.  The people that you&amp;#39;re doing the work for are most likely interested in how the work is going! They also can give you early feedback on what you have so far and the casual conversations that you have with them can lead you to new information or even just motivation to do the job well.&lt;/p&gt;
  4270.  
  4271. &lt;h2&gt;Specific Techniques&lt;/h2&gt;
  4272.  
  4273. &lt;p&gt;While I think the most important aspect is communication, being able to apply specific techniques can be very useful. I&amp;#39;ve come across a few techniques that I think you can apply to combat the feeling of dread.&lt;/p&gt;
  4274.  
  4275. &lt;h3&gt;Perspectives as a tool&lt;/h3&gt;
  4276.  
  4277. &lt;p&gt;Does the problem have you stumped? Try thinking about it from a different perspective. Put yourself in their shoes and start asking yourself what&amp;#39;s important and what&amp;#39;s not important with regards to the problem at hand. You can use this technique to come up with missing functionality and to come up with unique solutions.&lt;/p&gt;
  4278.  
  4279. &lt;p&gt;A great example of this is a problem like refactoring code. From a product owner perspective, refactoring code has no direct benefit, and yet they know that there are future benefits to refactoring the code into something more easy to work with. Conversely, your team would love someone to give the code some TLC because eventually they&amp;#39;ll have to change some behaviour in the affected code.&lt;/p&gt;
  4280.  
  4281. &lt;p&gt;What if you thought about this from the perspective of someone who wasn&amp;#39;t a programmer? You may find simple solutions to the problems that are presented to you. For example, the person might have ended up going with a simple spreadsheet in Excel. That ends up being a lot more maintainable than a MySQL database + Rails + Web Server. Even if it needs to be online and concurrently accessed, Google Docs Spreadsheets fills this niche!&lt;/p&gt;
  4282.  
  4283. &lt;p&gt;Think about the perspectives of these people:&lt;/p&gt;
  4284.  
  4285. &lt;ul&gt;
  4286. &lt;li&gt;Users&lt;/li&gt;
  4287. &lt;li&gt;Product owner&lt;/li&gt;
  4288. &lt;li&gt;Your team&lt;/li&gt;
  4289. &lt;li&gt;Legal&lt;/li&gt;
  4290. &lt;li&gt;Competitors&lt;/li&gt;
  4291. &lt;/ul&gt;
  4292.  
  4293. &lt;h3&gt;Talk to the person who made the ticket&lt;/h3&gt;
  4294.  
  4295. &lt;p&gt;This seems obvious to some people, but it&amp;#39;s not obvious to others. Talk to the person who made the ticket! Maybe they didn&amp;#39;t have all the facts. Maybe they&amp;#39;ve changed their minds or perhaps the work isn&amp;#39;t needed anymore. If you do the work for the ticket without talking to the person responsible for creating it, then &lt;strong&gt;that&amp;#39;s a red flag&lt;/strong&gt;.&lt;/p&gt;
  4296.  
  4297. &lt;p&gt;Say a task will take you 3 days to do the way it&amp;#39;s written in the ticket. After talking with the ticket reporter, you find out that the part that would take 2 days to do is more of a nice-to-have, and now the ticket will only take 1 day.&lt;/p&gt;
  4298.  
  4299. &lt;p&gt;That doesn&amp;#39;t mean that the additional work shouldn&amp;#39;t be done, though. It can live on in another ticket where, if it ends up actually being that important, the work will be done. Simply put, delaying a feature until it&amp;#39;s absolutely necessary allows your team and business to focus on the pain points of what you&amp;#39;re trying to solve.&lt;/p&gt;
  4300.  
  4301. &lt;h3&gt;Do something different&lt;/h3&gt;
  4302.  
  4303. &lt;p&gt;Working on something else while you think about the problem can really help you come up with a solution to the problem. Take another small ticket on while you think about the larger ticket and a solution may just come to mind.&lt;/p&gt;
  4304.  
  4305. &lt;p&gt;Or, just take a walk! Or get a coffee, the options are pretty much endless and each person has their own way of taking their mind off the problem.&lt;/p&gt;
  4306.  
  4307. &lt;p&gt;Another small point here is that the people who you consulted earlier for a solution may come back to you with a solution as well.&lt;/p&gt;
  4308.  
  4309. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4310.  
  4311. &lt;p&gt;Always keep an open mind while you&amp;#39;re developing. You may not realize it, but you probably have a support network around you that you can rely on!&lt;/p&gt;
  4312. </description>
  4313.    </item>
  4314.    
  4315.    
  4316.    
  4317.    <item>
  4318.      <title>Background Processing: Use Einhorn to Spawn and Manage Worker Processes</title>
  4319.      <link>https://techblog.thescore.com/2014/10/20/background-processing-use-einhorn-to-spawn-and-manage-workers/</link>
  4320.      <pubDate>Mon, 20 Oct 2014 00:00:00 +0000</pubDate>
  4321.      <author></author>
  4322.      <guid>https://techblog.thescore.com/2014/10/20/background-processing-use-einhorn-to-spawn-and-manage-workers</guid>
  4323.      <description>&lt;p&gt;Most modern web applications have a background processing subsystem. In other words,
  4324. whenever something does not need to be done in the web request (i.e. sending a
  4325. welcome email when a new user signs up), it can be queued up and processed
  4326. later outside of the request-response cycle. This allows the web requests to
  4327. be fast and responsive.&lt;/p&gt;
  4328.  
  4329. &lt;h2&gt;Anatomy of a Background Processing System&lt;/h2&gt;
  4330.  
  4331. &lt;p&gt;A background processing system usually consists of the following:&lt;/p&gt;
  4332.  
  4333. &lt;p&gt;&lt;strong&gt;Queue&lt;/strong&gt; is where the &amp;quot;jobs&amp;quot; are stored till they&amp;#39;re processed. A job contains
  4334. the data necessary for the task at hand (i.e. we need the &lt;code&gt;user_id&lt;/code&gt; to look up the
  4335. user&amp;#39;s email in our database before sending a welcome email), as well as some
  4336. meta data (i.e. the time the job entered into the queue).&lt;/p&gt;
  4337.  
  4338. &lt;p&gt;&lt;strong&gt;Producers&lt;/strong&gt; put jobs into the queue. For example, the request that handles
  4339. user sign-ups can produce a new job for each successful sign-up so a welcome email
  4340. will be sent to the user who has just signed up.&lt;/p&gt;
  4341.  
  4342. &lt;p&gt;&lt;strong&gt;Consumers&lt;/strong&gt; know how to process one or more kinds of jobs. For example, the
  4343. consumer responsible for sending welcome emails would know how to look up the
  4344. user, build the welcome message, and deliver it to the user&amp;#39;s email address. In
  4345. other words, the consumers implement your business logic.&lt;/p&gt;
  4346.  
  4347. &lt;p&gt;&lt;strong&gt;Workers&lt;/strong&gt; poll the queue for new jobs. When a job becomes available, they
  4348. pop it off the queue, and hand it off to the consumer that knows how to process
  4349. that job.&lt;/p&gt;
  4350.  
  4351. &lt;h2&gt;Putting the Pieces Together&lt;/h2&gt;
  4352.  
  4353. &lt;p&gt;In most languages, you can find a library that addresses all of the pieces above, and
  4354. presents you with a complete solution. In Ruby, you can find several libraries such as
  4355. &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;resque&lt;/a&gt;, &lt;a href=&quot;https://github.com/nesquena/backburner&quot;&gt;backburner&lt;/a&gt;,
  4356. and &lt;a href=&quot;https://github.com/collectiveidea/delayed_job&quot;&gt;delayed_job&lt;/a&gt; that do this.&lt;/p&gt;
  4357.  
  4358. &lt;p&gt;Suppose you want to put together a custom background processing system. The most
  4359. important pieces you need to worry about are the queue and workers. What you choose
  4360. as a queue or how you implement your workers will determine the performance and
  4361. scalability of the system. How you should choose a queue is outside the scope of
  4362. this blog post so let&amp;#39;s move on to talking about the workers.&lt;/p&gt;
  4363.  
  4364. &lt;h2&gt;The Workers&lt;/h2&gt;
  4365.  
  4366. &lt;p&gt;As described earlier, workers sit in a loop, and wait for new jobs. When a job
  4367. becomes available, they pop it off the queue and process it by delegating to
  4368. the appropriate consumer. Here is a sample worker in Ruby that uses
  4369. &lt;a href=&quot;http://kr.github.io/beanstalkd&quot;&gt;beanstalk&lt;/a&gt; as the queue:&lt;/p&gt;
  4370. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;
  4371.  
  4372. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Worker&lt;/span&gt;
  4373. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4374. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# REF https://github.com/beanstalkd/beaneater&lt;/span&gt;
  4375. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Beaneater&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Pool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;localhost:11300&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4376.  
  4377. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Assume keys map to beanstalk tubes&lt;/span&gt;
  4378. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;watch!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4379.  
  4380. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  4381. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Wait for 10 seconds to get a job&lt;/span&gt;
  4382. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tubes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reserve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4383.  
  4384. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Assume that we originally enqueued the job with a JSON body&lt;/span&gt;
  4385. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job_data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4386.  
  4387. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tube&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;job_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4388. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4389. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4390. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4391.  
  4392. &lt;span class=&quot;c1&quot;&gt;# Assume that we have AlertDetector and DeviceRegistrar defined&lt;/span&gt;
  4393. &lt;span class=&quot;c1&quot;&gt;# in our app as below:&lt;/span&gt;
  4394. &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  4395. &lt;span class=&quot;c1&quot;&gt;#   class AlertDetector&lt;/span&gt;
  4396. &lt;span class=&quot;c1&quot;&gt;#     def self.perform(job_data)&lt;/span&gt;
  4397. &lt;span class=&quot;c1&quot;&gt;#       # Detect an alert (i.e. new goal is scored)&lt;/span&gt;
  4398. &lt;span class=&quot;c1&quot;&gt;#     end&lt;/span&gt;
  4399. &lt;span class=&quot;c1&quot;&gt;#   end&lt;/span&gt;
  4400. &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  4401. &lt;span class=&quot;c1&quot;&gt;#   class DeviceRegistrar&lt;/span&gt;
  4402. &lt;span class=&quot;c1&quot;&gt;#     def self.perform(job_data)&lt;/span&gt;
  4403. &lt;span class=&quot;c1&quot;&gt;#       # Register the device for push notifications&lt;/span&gt;
  4404. &lt;span class=&quot;c1&quot;&gt;#     end&lt;/span&gt;
  4405. &lt;span class=&quot;c1&quot;&gt;#   end&lt;/span&gt;
  4406. &lt;span class=&quot;no&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  4407. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;alert_detector&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AlertDetector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  4408. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;device_registrar&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DeviceRegistrar&lt;/span&gt;
  4409. &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4410. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4411. &lt;p&gt;Given that you put the code above in &lt;code&gt;bin/work&lt;/code&gt; inside your application, you
  4412. can run it as &lt;code&gt;./bin/work&lt;/code&gt; from the root of the application.&lt;/p&gt;
  4413.  
  4414. &lt;p&gt;The worker above can process only one job at a time. This won&amp;#39;t scale as the
  4415. number of jobs increase. You need to be able to process many jobs in parallel.
  4416. You can use threads to do this, but threads are difficult to use, and even more
  4417. difficult to debug. In addition, if your worker is written in Ruby or Python,
  4418. you don&amp;#39;t have true threads anyway.&lt;/p&gt;
  4419.  
  4420. &lt;p&gt;The alternative is to run many copies of the worker process, which is a
  4421. reliable and popular way to achieve concurrency in unix. Now, you need to
  4422. worry about how you spin up and manage multiple worker processes. This is
  4423. where most people reinvent the the wheel, and write custom code. Don&amp;#39;t
  4424. reinvent the wheel.&lt;/p&gt;
  4425.  
  4426. &lt;h2&gt;Enter Einhorn&lt;/h2&gt;
  4427.  
  4428. &lt;p&gt;&lt;a href=&quot;https://github.com/stripe/einhorn&quot;&gt;Einhorn&lt;/a&gt; bills itself as the
  4429. language-independent shared socket manager. But, shared socket management is
  4430. just one of its many features:&lt;/p&gt;
  4431.  
  4432. &lt;ul&gt;
  4433. &lt;li&gt;&lt;p&gt;Einhorn lets you spin up any number of worker processes (the number can be
  4434. adjusted on the fly)&lt;/p&gt;&lt;/li&gt;
  4435. &lt;li&gt;&lt;p&gt;Einhorn can spawn a new pool of workers and gracefully kill off the old ones,
  4436. allowing seamless upgrades to new versions of your code.&lt;/p&gt;&lt;/li&gt;
  4437. &lt;li&gt;&lt;p&gt;Einhorn gets out of your application&amp;#39;s way&lt;/p&gt;&lt;/li&gt;
  4438. &lt;/ul&gt;
  4439.  
  4440. &lt;p&gt;This means we can take the worker we&amp;#39;ve written earlier, and run 20 copies of
  4441. it without modifying the original code in any way: &lt;code&gt;einhorn -n 20 ./bin/work&lt;/code&gt;. That&amp;#39;s
  4442. it. Einhorn does indeed get out of your application&amp;#39;s way.&lt;/p&gt;
  4443.  
  4444. &lt;p&gt;If you use Ruby, Einhorn can also preload your application, which means that it
  4445. loads everything prior to forking so that your code is only stored in memory once.&lt;/p&gt;
  4446.  
  4447. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4448.  
  4449. &lt;p&gt;At theScore, we&amp;#39;ve started using Einhorn to spin up and manage worker processes
  4450. in our background processing system that is responsible for generating and delivering
  4451. push alerts. We process millions of messages through that system every day. Einhorn
  4452. turned out to be simpler and much more resilient than the custom code we used to have.&lt;/p&gt;
  4453. </description>
  4454.    </item>
  4455.    
  4456.    
  4457.    
  4458.    <item>
  4459.      <title>3 Ways to Create Classes in Ruby</title>
  4460.      <link>https://techblog.thescore.com/2014/07/19/3-ways-to-create-classes-in-ruby/</link>
  4461.      <pubDate>Sat, 19 Jul 2014 00:00:00 +0000</pubDate>
  4462.      <author></author>
  4463.      <guid>https://techblog.thescore.com/2014/07/19/3-ways-to-create-classes-in-ruby</guid>
  4464.      <description>&lt;p&gt;Classes are &lt;a href=&quot;http://en.wikipedia.org/wiki/First-class_citizen&quot;&gt;first-class objects&lt;/a&gt; in Ruby.
  4465. All classes happen to be instances of &lt;code&gt;Class&lt;/code&gt;. In other words, classes are &lt;code&gt;Class&lt;/code&gt; objects
  4466. just like &lt;code&gt;&amp;#39;hello&amp;#39;&lt;/code&gt; and &lt;code&gt;&amp;#39;world&amp;#39;&lt;/code&gt; are &lt;code&gt;String&lt;/code&gt; objects.&lt;/p&gt;
  4467.  
  4468. &lt;p&gt;Out of the box, there are 3 ways to create classes in Ruby.&lt;/p&gt;
  4469.  
  4470. &lt;h2&gt;Use the &lt;code&gt;class&lt;/code&gt; Keyword&lt;/h2&gt;
  4471.  
  4472. &lt;p&gt;In the vast majority of the cases, you would use the &lt;code&gt;class&lt;/code&gt; keyword to create a class.
  4473. This is usually called a class definition:&lt;/p&gt;
  4474. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Customer&lt;/span&gt;
  4475. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card_number&lt;/span&gt;
  4476.  
  4477. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4478. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@full_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;
  4479. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@card_type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;
  4480. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@card_number&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;
  4481. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4482. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4483.  
  4484. &lt;span class=&quot;c1&quot;&gt;# Creating and using a Customer object:&lt;/span&gt;
  4485. &lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;visa&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4486.  
  4487. &lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; Thuva Tharma&lt;/span&gt;
  4488. &lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; visa&lt;/span&gt;
  4489. &lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; 1234&lt;/span&gt;
  4490. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4491. &lt;p&gt;If you&amp;#39;re a Rubyist, you&amp;#39;ve seen and done this so many times. So, let&amp;#39;s move on.&lt;/p&gt;
  4492.  
  4493. &lt;h2&gt;Use &lt;code&gt;Class.new&lt;/code&gt;&lt;/h2&gt;
  4494.  
  4495. &lt;p&gt;Just like how we created a customer with &lt;code&gt;Customer.new&lt;/code&gt; in the example above, we can create
  4496. a class with &lt;code&gt;Class.new&lt;/code&gt;:&lt;/p&gt;
  4497. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  4498. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card_number&lt;/span&gt;
  4499.  
  4500. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4501. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@full_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;
  4502. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@card_type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;
  4503. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@card_number&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;
  4504. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4505. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4506. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4507. &lt;p&gt;This is functionally equivalent to regular class definition, but more explicit.&lt;/p&gt;
  4508.  
  4509. &lt;p&gt;By convention, classes are referenced by constants in Ruby. This is why we&amp;#39;re assigning
  4510. the object returned by &lt;code&gt;Class.new&lt;/code&gt; to &lt;code&gt;Customer&lt;/code&gt; constant, but we can assign it to
  4511. a regular variable as well. On the other hand, if you don&amp;#39;t use a constant in a
  4512. regular class definition, you will get an error:&lt;/p&gt;
  4513. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;SyntaxError: class/module name must be CONSTANT
  4514. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4515. &lt;p&gt;Now, why would you ever want to use &lt;code&gt;Class.new&lt;/code&gt; over regular class definition? Let&amp;#39;s
  4516. look at creating a custom error class (for good reason, custom error classes should
  4517. inherit from &lt;code&gt;StandardError&lt;/code&gt;):&lt;/p&gt;
  4518. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CustomerError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;
  4519. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4520.  
  4521. &lt;span class=&quot;c1&quot;&gt;# This usually gets shortened as:&lt;/span&gt;
  4522. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CustomerError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4523. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4524. &lt;p&gt;You can pass in an argument to &lt;code&gt;Class.new&lt;/code&gt; to specify the super class for the class
  4525. you&amp;#39;re creating. With this in mind, you can define &lt;code&gt;CustomerError&lt;/code&gt; more elegantly:&lt;/p&gt;
  4526. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CustomerError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4527. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4528. &lt;p&gt;This is such a minor improvement, but a good use-case nonetheless. Another use-case
  4529. for &lt;code&gt;Class.new&lt;/code&gt; might be in testing. If you need to define classes only to be used
  4530. by your tests, you don&amp;#39;t want to pollute the global namespace with the test class
  4531. constants. You can use &lt;code&gt;Class.new&lt;/code&gt; to store the test classes in local or instance
  4532. variables.&lt;/p&gt;
  4533.  
  4534. &lt;h2&gt;Use &lt;code&gt;Struct&lt;/code&gt;&lt;/h2&gt;
  4535.  
  4536. &lt;p&gt;&lt;code&gt;Struct&lt;/code&gt; is available as part of Ruby&amp;#39;s standard library, and you don&amp;#39;t need to
  4537. &lt;code&gt;require&lt;/code&gt; anything to use it. &lt;code&gt;Struct&lt;/code&gt; lets you create classes for data container
  4538. objects without any boilerplate:&lt;/p&gt;
  4539. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Struct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4540. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4541. &lt;p&gt;This has all the features of &lt;code&gt;Customer&lt;/code&gt; class we created with regular class
  4542. definition as well as using &lt;code&gt;Class.new&lt;/code&gt; above. In addition, the customer objects
  4543. will have equality as per &lt;a href=&quot;http://en.wikipedia.org/wiki/Value_object&quot;&gt;value object&lt;/a&gt;
  4544. semantics:&lt;/p&gt;
  4545. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;visa&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4546. &lt;span class=&quot;n&quot;&gt;customer2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;visa&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4547. &lt;span class=&quot;n&quot;&gt;customer3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;amex&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4548.  
  4549. &lt;span class=&quot;n&quot;&gt;customer1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; true&lt;/span&gt;
  4550. &lt;span class=&quot;n&quot;&gt;customer1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; false&lt;/span&gt;
  4551. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4552. &lt;p&gt;On the other hand, we lose arity checking for arguments passed in to &lt;code&gt;Customer.new&lt;/code&gt;.
  4553. In other words, you don&amp;#39;t get &lt;code&gt;ArgumentError&lt;/code&gt; if you pass in less arguments:&lt;/p&gt;
  4554. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# full_name, card_type, card_number are all nil&lt;/span&gt;
  4555. &lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;
  4556.  
  4557. &lt;span class=&quot;c1&quot;&gt;# card_type, card_number are nil&lt;/span&gt;
  4558. &lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4559.  
  4560. &lt;span class=&quot;c1&quot;&gt;# card_number is nil&lt;/span&gt;
  4561. &lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;visa&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4562.  
  4563. &lt;span class=&quot;c1&quot;&gt;# full_name, card_type, card_number are all present&lt;/span&gt;
  4564. &lt;span class=&quot;no&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thuva Tharma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;visa&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4565. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4566. &lt;p&gt;This is not good. But, if you limit using &lt;code&gt;Struct&lt;/code&gt; to create inner classes for
  4567. value objects, it&amp;#39;s still safe and useful:&lt;/p&gt;
  4568. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Customer&lt;/span&gt;
  4569. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Internal: Class to create credit card value objects&lt;/span&gt;
  4570. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Struct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:holder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4571.  
  4572. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:card&lt;/span&gt;
  4573.  
  4574. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4575. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@full_name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;
  4576. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4577. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4578. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4579. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4580. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4581.  
  4582. &lt;p&gt;In Ruby, you can create classes using the &lt;code&gt;class&lt;/code&gt; keyword (class definition),
  4583. &lt;code&gt;Class.new&lt;/code&gt;, and &lt;code&gt;Struct&lt;/code&gt;. Although you would use regular class definition
  4584. in the vast majority of cases, &lt;code&gt;Class.new&lt;/code&gt; and &lt;code&gt;Struct&lt;/code&gt; would sometimes come
  4585. in handy if you know how to use them.&lt;/p&gt;
  4586. </description>
  4587.    </item>
  4588.    
  4589.    
  4590.    
  4591.    <item>
  4592.      <title>Select and Map Are Good</title>
  4593.      <link>https://techblog.thescore.com/2014/06/25/select-and-map-are-good/</link>
  4594.      <pubDate>Wed, 25 Jun 2014 00:00:00 +0000</pubDate>
  4595.      <author></author>
  4596.      <guid>https://techblog.thescore.com/2014/06/25/select-and-map-are-good</guid>
  4597.      <description>&lt;p&gt;This article argues that &lt;em&gt;when able to&lt;/em&gt; one should break down
  4598. iteration operations over an array into &lt;code&gt;#map&lt;/code&gt; and &lt;code&gt;#select&lt;/code&gt; as opposed to
  4599. operating on the enumerable through an &lt;code&gt;#each&lt;/code&gt;.&lt;/p&gt;
  4600.  
  4601. &lt;h2&gt;The Examples&lt;/h2&gt;
  4602.  
  4603. &lt;p&gt;Throughout this article I will refer to the following, contrived, example:&lt;/p&gt;
  4604.  
  4605. &lt;p&gt;You have an array of numbers &lt;code&gt;[1, 2, 3, 4, 5]&lt;/code&gt; and you want to subtract 3
  4606. from each of the items and then remove all items that are 0.&lt;/p&gt;
  4607.  
  4608. &lt;p&gt;A) Using &lt;code&gt;#each&lt;/code&gt; you could express this as:&lt;/p&gt;
  4609. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  4610. &lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
  4611.  
  4612. &lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4613. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
  4614. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  4615. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_item&lt;/span&gt;
  4616. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4617. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4618. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4619. &lt;p&gt;B) Using &lt;code&gt;#select&lt;/code&gt; and &lt;code&gt;#map&lt;/code&gt; you could express this as:&lt;/p&gt;
  4620. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  4621.  
  4622. &lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4623.  
  4624. &lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4625. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4626. &lt;h2&gt;The Arguments&lt;/h2&gt;
  4627.  
  4628. &lt;h3&gt;Better Seperation of Logic&lt;/h3&gt;
  4629.  
  4630. &lt;p&gt;Example A is doing two things in one block whereas example B is
  4631. doing just one thing in each of the two blocks. In general, the less
  4632. there is in a block the easier that block is to understand. Breaking a
  4633. problem down into map and select means that you have broken the problem
  4634. up into two distinct parts.&lt;/p&gt;
  4635.  
  4636. &lt;h3&gt;Clarity&lt;/h3&gt;
  4637.  
  4638. &lt;p&gt;Which brings us to what those two parts do. They are actually named.
  4639. If I am reading example B and I am trying to find the part where items
  4640. are removed, then I look in the select block. If I am looking for the part where the
  4641. items are changed then I look in the map block. The method names tell how the
  4642. block is to be used.&lt;/p&gt;
  4643.  
  4644. &lt;p&gt;When reading example A, I have to read all of the &lt;code&gt;#each&lt;/code&gt; block if I am
  4645. looking for where the values are changed or when the items are
  4646. removed.&lt;/p&gt;
  4647.  
  4648. &lt;h2&gt;Potential Counter Arguments&lt;/h2&gt;
  4649.  
  4650. &lt;p&gt;I think some people may argue that speed is a big issue. The idea is that
  4651. you are iterating over the enumerable twice so it is using more time.&lt;/p&gt;
  4652.  
  4653. &lt;p&gt;Okay let us assume the time to to process &lt;code&gt;item - 3&lt;/code&gt; and assign it to
  4654. variable/add it to the array is &lt;code&gt;a&lt;/code&gt; and that time to check the
  4655. &lt;code&gt;new_item/item != 0&lt;/code&gt; and add it the array is &lt;code&gt;b&lt;/code&gt;, and the time to setup
  4656. each iteration of the array is &lt;code&gt;c&lt;/code&gt;. We will also assume &lt;code&gt;i&lt;/code&gt; is the number
  4657. of iterations to travese the array.&lt;/p&gt;
  4658.  
  4659. &lt;p&gt;So example A will take &lt;code&gt;i(a + b + c)&lt;/code&gt; time and example B will take
  4660. &lt;code&gt;i(a + c) + i(b + c)&lt;/code&gt; . The difference between these two ends up being
  4661. B - A so &lt;code&gt;i(a + b) + 2ic - (i(a + b) + ic)&lt;/code&gt; &lt;code&gt;= ic&lt;/code&gt;. The difference
  4662. is going to be the time to setup the iterations.&lt;/p&gt;
  4663.  
  4664. &lt;p&gt;Let us check the actual difference in time with a much bigger array:&lt;/p&gt;
  4665. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;benchmark&amp;#39;&lt;/span&gt;
  4666.  
  4667. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4668. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
  4669.  
  4670. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4671. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
  4672. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  4673. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_item&lt;/span&gt;
  4674. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4675. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4676.  
  4677. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;
  4678. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4679.  
  4680. &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_map_and_select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4681. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4682.  
  4683. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4684. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4685.  
  4686. &lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10000000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt;
  4687.  
  4688. &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bmbm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4689. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;With #each&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4690. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;With #map and #select&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_map_and_select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;the_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  4691. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4692. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4693. &lt;p&gt;The output on my computer is:&lt;/p&gt;
  4694. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;                            user     system      total        real
  4695. With #each              1.830000   0.040000   1.870000 (  1.924606)
  4696. With #map and #select   2.440000   0.030000   2.470000 (  2.468480)
  4697. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4698. &lt;p&gt;So the time difference is about +25% for example B and this is a
  4699. really simple example. The difference as a percentage will fall as the
  4700. complexity of the operations increases.&lt;/p&gt;
  4701.  
  4702. &lt;p&gt;So the time difference does exists but I don&amp;#39;t think this is going to be a
  4703. huge factor in most cases.&lt;/p&gt;
  4704. </description>
  4705.    </item>
  4706.    
  4707.    
  4708.    
  4709.    <item>
  4710.      <title>Simple Ways to Protect an API: HTTP Basic Authentication and HTTP Token Authentication</title>
  4711.      <link>https://techblog.thescore.com/2014/06/25/http-basic-authentication-and-http-token-authentication/</link>
  4712.      <pubDate>Wed, 25 Jun 2014 00:00:00 +0000</pubDate>
  4713.      <author></author>
  4714.      <guid>https://techblog.thescore.com/2014/06/25/http-basic-authentication-and-http-token-authentication</guid>
  4715.      <description>&lt;p&gt;The backend for theScore mobile apps consists of many different web services (Core
  4716. Sports, Personalization, Push Alerts, etc.). Each service provides its own RESTful API.
  4717. Some of these services are internal. As such, they only have a very limited number of
  4718. consumers. Having a limited number of consumers for an API lets you keep the authentication
  4719. really simple for that API.&lt;/p&gt;
  4720.  
  4721. &lt;p&gt;For the purposes of this blog post, suppose that we have two services. One that is responsible
  4722. for detecting alerts (i.e. new goal is scored), and another that is responsible for actually
  4723. sending those alerts to mobile devices. Let&amp;#39;s call these &lt;code&gt;Detector&lt;/code&gt; and &lt;code&gt;Sender&lt;/code&gt; respectively.
  4724. Also, suppose that &lt;code&gt;Sender&lt;/code&gt; is a web service, and &lt;code&gt;Detector&lt;/code&gt; is a background processing system.
  4725. Further, whenever &lt;code&gt;Detector&lt;/code&gt; detects an alert, it uses the API provided by &lt;code&gt;Sender&lt;/code&gt; to deliver
  4726. the alerts.&lt;/p&gt;
  4727.  
  4728. &lt;p&gt;The internals of the services described above would look like the following in Ruby/Rails:&lt;/p&gt;
  4729. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Detector Service&lt;/span&gt;
  4730. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;GoalDetector&lt;/span&gt;
  4731. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4732. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Send the alert upon detection of new alert&lt;/span&gt;
  4733. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# REF https://github.com/lostisland/faraday&lt;/span&gt;
  4734. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://sender.abc.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4735. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/alerts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Spain Goal&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4736. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4737. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4738.  
  4739. &lt;span class=&quot;c1&quot;&gt;# Sender Service (https://sender.abc.com)&lt;/span&gt;
  4740. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AlertsController&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  4741. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# POST /alerts&lt;/span&gt;
  4742. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  4743. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Send an alert with message in params[:message]&lt;/span&gt;
  4744. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4745. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4746. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4747. &lt;p&gt;All is good except that our &lt;code&gt;Sender&lt;/code&gt; API is public. Anyone who happens to stumble upon
  4748. that API can discover this great power to send push alerts to our users. So, we need
  4749. a way to protect the API from unauthorized access.&lt;/p&gt;
  4750.  
  4751. &lt;h2&gt;HTTP Basic Authentication&lt;/h2&gt;
  4752.  
  4753. &lt;p&gt;This is generally the go-to solution for this problem. Our example above would look like
  4754. the following with Basic Authentication support:&lt;/p&gt;
  4755. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Detector Service&lt;/span&gt;
  4756. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;GoalDetector&lt;/span&gt;
  4757. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4758. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Send the alert upon detection of new alert&lt;/span&gt;
  4759. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# REF https://github.com/lostisland/faraday&lt;/span&gt;
  4760. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://sender.abc.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4761. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basic_auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4762. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4763.  
  4764. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/alerts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Spain Goal&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4765. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4766. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4767.  
  4768. &lt;span class=&quot;c1&quot;&gt;# Sender Service (https://sender.abc.com)&lt;/span&gt;
  4769. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AlertsController&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  4770. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before_action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:authenticate&lt;/span&gt;
  4771.  
  4772. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# POST /alerts&lt;/span&gt;
  4773. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  4774. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Send an alert with message in params[:message]&lt;/span&gt;
  4775. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4776.  
  4777. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;
  4778.  
  4779. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;
  4780. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authenticate_or_request_with_http_basic&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4781. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;
  4782. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4783. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4784. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4785.  
  4786. &lt;span class=&quot;c1&quot;&gt;# NOTE: In real applications, we will not have the&lt;/span&gt;
  4787. &lt;span class=&quot;c1&quot;&gt;# authentication credentials lying around in code.&lt;/span&gt;
  4788. &lt;span class=&quot;c1&quot;&gt;# We will store them in external configuration.&lt;/span&gt;
  4789. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4790. &lt;p&gt;Now, the &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;https://sender.abc.com/alerts&lt;/code&gt; would require that an
  4791. &lt;code&gt;Authorization&lt;/code&gt; header is present with the credentials encoded correctly in the value.
  4792. The header would look like:&lt;/p&gt;
  4793. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;# dXNlcm5hbWU6cGFzc3dvcmQ=\n is Base64-encoded value
  4794. # of the string username:password
  4795. Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n
  4796. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4797. &lt;p&gt;If you don&amp;#39;t have the credentials, you won&amp;#39;t be able to generate the &lt;code&gt;Authorization&lt;/code&gt;
  4798. header correctly. If you send the wrong value for the header in your request, you will
  4799. get &lt;code&gt;401 Unauthorized&lt;/code&gt; response back. So, we&amp;#39;ve now effectively protected the &lt;code&gt;Sender&lt;/code&gt;
  4800. service from unauthorized access.&lt;/p&gt;
  4801.  
  4802. &lt;h2&gt;HTTP Token Authentication&lt;/h2&gt;
  4803.  
  4804. &lt;p&gt;With Basic Authentication, you need to configure both username and password
  4805. in &lt;code&gt;Sender&lt;/code&gt; and &lt;code&gt;Detector&lt;/code&gt; services. But, if the password is really strong, then it
  4806. is a secure credential by itself. In other words, you can think of the password as a
  4807. secure token. In this case, the username becomes redundant. This is where Token
  4808. Authentication comes in.&lt;/p&gt;
  4809.  
  4810. &lt;p&gt;Our code example would look like the following with Token Authentication support:&lt;/p&gt;
  4811. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Detector Service&lt;/span&gt;
  4812. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;GoalDetector&lt;/span&gt;
  4813. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4814. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Send the alert upon detection of new alert&lt;/span&gt;
  4815. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# REF https://github.com/lostisland/faraday&lt;/span&gt;
  4816. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;https://sender.abc.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4817. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;WCZZYjnOQFUYfJIN2ShH1iD24UHo58A6TI&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4818. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4819.  
  4820. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/alerts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Spain Goal&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4821. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4822. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4823.  
  4824. &lt;span class=&quot;c1&quot;&gt;# Sender Service (https://sender.abc.com)&lt;/span&gt;
  4825. &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AlertsController&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  4826. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before_action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:authenticate&lt;/span&gt;
  4827.  
  4828. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# POST /alerts&lt;/span&gt;
  4829. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  4830. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Send an alert with message in params[:message]&lt;/span&gt;
  4831. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4832.  
  4833. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;
  4834.  
  4835. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;
  4836. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authenticate_or_request_with_http_token&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  4837. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;WCZZYjnOQFUYfJIN2ShH1iD24UHo58A6TI&amp;#39;&lt;/span&gt;
  4838. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4839. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4840. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4841.  
  4842. &lt;span class=&quot;c1&quot;&gt;# NOTE: In real applications, we will not have the&lt;/span&gt;
  4843. &lt;span class=&quot;c1&quot;&gt;# authentication credentials lying around in code.&lt;/span&gt;
  4844. &lt;span class=&quot;c1&quot;&gt;# We will store them in external configuration.&lt;/span&gt;
  4845. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4846. &lt;p&gt;Now, the &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;https://sender.abc.com/alerts&lt;/code&gt; would require that that an
  4847. &lt;code&gt;Authorization&lt;/code&gt; header is present with the correct token in the value. The header
  4848. would look like:&lt;/p&gt;
  4849. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;Authorization: Token token=&amp;quot;WCZZYjnOQFUYfJIN2ShH1iD24UHo58A6TI&amp;quot;
  4850. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4851. &lt;p&gt;If you send the wrong token in the &lt;code&gt;Authorization&lt;/code&gt; header, you will get
  4852. &lt;code&gt;401 Unauthorized&lt;/code&gt; response back. Again, we&amp;#39;ve protected the API from unauthorized
  4853. access.&lt;/p&gt;
  4854.  
  4855. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4856.  
  4857. &lt;p&gt;Both HTTP Basic Authentication and HTTP Token Authentication offer really simple
  4858. solutions to protect an API from unauthorized access. But, with Token Authentication,
  4859. you will have one less thing to configure in your services and consumers. Because of
  4860. that, I prefer using Token Authentication.&lt;/p&gt;
  4861.  
  4862. &lt;p&gt;Having said that, unlike Basic Authentication which is an approved spec, Token
  4863. Authentication is a draft spec:&lt;/p&gt;
  4864.  
  4865. &lt;ul&gt;
  4866. &lt;li&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc2617&quot;&gt;HTTP Authentication: Basic and Digest Access Authentication&lt;/a&gt;&lt;/li&gt;
  4867. &lt;li&gt;&lt;a href=&quot;http://tools.ietf.org/html/draft-hammer-http-token-auth-01&quot;&gt;HTTP Authentication: Token Access Authentication&lt;/a&gt;&lt;/li&gt;
  4868. &lt;/ul&gt;
  4869.  
  4870. &lt;p&gt;Regardless of being a draft spec, Token Authentication is well-supported and really
  4871. easy-to-use. I don&amp;#39;t see any practical reasons not to use it.&lt;/p&gt;
  4872.  
  4873. &lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Both Basic Authentication and Token Authentication are insecure unless used
  4874. over &lt;code&gt;HTTPS&lt;/code&gt;. All the code examples above use &lt;code&gt;HTTPS&lt;/code&gt; to make this explicit. We use
  4875. &lt;code&gt;HTTPS&lt;/code&gt; on all of our APIs at theScore.&lt;/p&gt;
  4876. </description>
  4877.    </item>
  4878.    
  4879.    
  4880.    
  4881.    <item>
  4882.      <title>How You Nest Modules Matters in Ruby</title>
  4883.      <link>https://techblog.thescore.com/2014/05/28/how-you-nest-modules-matters-in-ruby/</link>
  4884.      <pubDate>Wed, 28 May 2014 00:00:00 +0000</pubDate>
  4885.      <author></author>
  4886.      <guid>https://techblog.thescore.com/2014/05/28/how-you-nest-modules-matters-in-ruby</guid>
  4887.      <description>&lt;p&gt;Ruby provides two different syntaxes to nest modules (and classes):&lt;/p&gt;
  4888. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Syntax #1&lt;/span&gt;
  4889. &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;API&lt;/span&gt;
  4890. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;V1&lt;/span&gt;
  4891. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4892. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4893.  
  4894. &lt;span class=&quot;c1&quot;&gt;# Syntax #2&lt;/span&gt;
  4895. &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;API::V1&lt;/span&gt;
  4896. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4897. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4898. &lt;p&gt;Syntax #2 requires that &lt;code&gt;API&lt;/code&gt; module already exists. Most Rubyists know that.
  4899. Besides this difference, they think that these two syntaxes are interchangeable.
  4900. With that line of thinking, what syntax to use for nesting modules turns out to
  4901. be a matter of preference.&lt;/p&gt;
  4902.  
  4903. &lt;p&gt;But, the syntax you choose matters, which I will demonstrate shortly with
  4904. examples that relate to how you would organize a versioned REST API.&lt;/p&gt;
  4905.  
  4906. &lt;p&gt;Modules are just constants in Ruby, as such, regular constant look-up rules apply.
  4907. The very first rule relates to how modules are nested. For the purpose of this
  4908. blog post, we will ignore regular constants and just focus on modules.&lt;/p&gt;
  4909.  
  4910. &lt;p&gt;When you reference a constant in a nested context, Ruby looks it up inside
  4911. out:&lt;/p&gt;
  4912. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;API&lt;/span&gt;
  4913. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Responder&lt;/span&gt;
  4914. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4915.  
  4916. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;V1&lt;/span&gt;
  4917. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Controller&lt;/span&gt;
  4918. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;
  4919. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Responder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respond_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4920. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4921. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4922. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4923. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4924. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4925. &lt;p&gt;When &lt;code&gt;#action&lt;/code&gt; gets called, Ruby will see if &lt;code&gt;Responder&lt;/code&gt; is defined inside
  4926. &lt;code&gt;API::V1::Controller&lt;/code&gt; first, &lt;code&gt;API::V1&lt;/code&gt; second, &lt;code&gt;API&lt;/code&gt; third, and finally in the
  4927. top level. If it wasn&amp;#39;t for this inside-out look-up, referencing &lt;code&gt;Responder&lt;/code&gt; as
  4928. above would not have worked. Instead, it would have to be referenced as
  4929. &lt;code&gt;API::Responder&lt;/code&gt;.&lt;/p&gt;
  4930.  
  4931. &lt;p&gt;You can actually access the nesting information Ruby uses for this
  4932. look-up with &lt;code&gt;Module.nesting&lt;/code&gt;:&lt;/p&gt;
  4933. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;API&lt;/span&gt;
  4934. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Responder&lt;/span&gt;
  4935. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4936.  
  4937. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;V1&lt;/span&gt;
  4938. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Controller&lt;/span&gt;
  4939. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nesting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; [API::V1::Controller, API::V1, API]&lt;/span&gt;
  4940.  
  4941. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;
  4942. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Responder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respond_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4943. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4944. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4945. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4946. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4947. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4948. &lt;p&gt;Now, let&amp;#39;s see how the look-up behavior changes when we mix in syntax #2:&lt;/p&gt;
  4949. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;API&lt;/span&gt;
  4950. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Responder&lt;/span&gt;
  4951. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4952. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4953.  
  4954. &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;API::V1&lt;/span&gt;
  4955. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Controller&lt;/span&gt;
  4956. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nesting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; [API::V1::Controller, API::V1]&lt;/span&gt;
  4957.  
  4958. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;
  4959. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Responder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respond_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  4960. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4961. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4962. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  4963. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4964. &lt;p&gt;When &lt;code&gt;#action&lt;/code&gt; gets called, you will now get a &lt;code&gt;NameError&lt;/code&gt;:&lt;/p&gt;
  4965. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;NameError: uninitialized constant API::V1::Controller::Responder
  4966. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  4967. &lt;p&gt;This is because some nesting information gets lost when you use syntax #2.
  4968. You can see this from what &lt;code&gt;Module.nesting&lt;/code&gt; returns in the example above.
  4969. As per that example, Ruby now looks for &lt;code&gt;Responder&lt;/code&gt; inside &lt;code&gt;API::V1::Controller&lt;/code&gt;
  4970. and &lt;code&gt;API::V1&lt;/code&gt;. It no longer looks inside &lt;code&gt;API&lt;/code&gt;, where &lt;code&gt;Responder&lt;/code&gt; is actually
  4971. defined hence the error.&lt;/p&gt;
  4972.  
  4973. &lt;h2&gt;Conclusion&lt;/h2&gt;
  4974.  
  4975. &lt;p&gt;The syntax you use for nesting modules in Ruby should not be a matter of
  4976. preference. The syntax you use could be the difference between a working
  4977. program, and the one that throws an error. So choose wisely. Having said
  4978. that, I prefer syntax #1.&lt;/p&gt;
  4979.  
  4980. &lt;p&gt;By the way, we&amp;#39;ve explored just one aspect of how constant look-up works
  4981. in Ruby in general. If you use Rails, the matters gets even more complicated
  4982. with the autoloading magic. The following references will help you learn more:&lt;/p&gt;
  4983.  
  4984. &lt;ul&gt;
  4985. &lt;li&gt;&lt;a href=&quot;http://cirw.in/blog/constant-lookup&quot;&gt;Everything you ever wanted to know about constant lookup in Ruby&lt;/a&gt;&lt;/li&gt;
  4986. &lt;li&gt;&lt;a href=&quot;http://urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell&quot;&gt;Rails autoloading — how it works, and when it doesn&amp;#39;t&lt;/a&gt;&lt;/li&gt;
  4987. &lt;/ul&gt;
  4988. </description>
  4989.    </item>
  4990.    
  4991.    
  4992.    
  4993.    <item>
  4994.      <title>Benchmarking JSON Generation in Ruby</title>
  4995.      <link>https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/</link>
  4996.      <pubDate>Fri, 23 May 2014 00:00:00 +0000</pubDate>
  4997.      <author></author>
  4998.      <guid>https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby</guid>
  4999.      <description>&lt;p&gt;At theScore, we have a big JSON API with hundreds of end-points that expose
  5000. sports data. Since sports data is very rich, the JSON representation of most
  5001. of the resources on our API tend to be complex. As a result, a lot of time is
  5002. spent on generating JSON in our Rails application.&lt;/p&gt;
  5003.  
  5004. &lt;p&gt;At the time we wrote the API, &lt;a href=&quot;https://github.com/nesquena/rabl&quot;&gt;RABL&lt;/a&gt; was a
  5005. great choice. When &lt;a href=&quot;https://github.com/rails-api/active_model_serializers&quot;&gt;ActiveModel Serializers&lt;/a&gt;
  5006. came out with an object-oriented approach to generating JSON, we were excited.
  5007. We tried it out, and ended up with something that is more maintainable than
  5008. the RABL counterparts.&lt;/p&gt;
  5009.  
  5010. &lt;p&gt;But, migrating the entire JSON generation code from RABL to ActiveModel
  5011. Serializers (AMS) would require massive effort. Since RABL is still
  5012. relatively easy to use and maintain, more ease of use and maintenance
  5013. aspects of AMS is not a good enough reason to do the migration. On the
  5014. other hand, if we can significantly increase the performance of JSON
  5015. generation, then the effort to do this migration is justified.&lt;/p&gt;
  5016.  
  5017. &lt;p&gt;Having said that, we had been using plain Ruby presenters to generate JSON
  5018. with great success in some of our smaller API projects. If AMS turn out to
  5019. be much slower than using presenters, then we might want to explore presenters
  5020. a little more as a possible approach for our main API.&lt;/p&gt;
  5021.  
  5022. &lt;p&gt;So that&amp;#39;s enough background as to why we&amp;#39;re doing this benchmark.&lt;/p&gt;
  5023.  
  5024. &lt;h2&gt;The Setup&lt;/h2&gt;
  5025.  
  5026. &lt;p&gt;For the benchmark, we extracted real RABL code from our API that generates
  5027. JSON for basketball teams and events, and simplified a bit. Then, we implemented
  5028. AMS and plain Ruby presenters to generate the same JSON. We have 3 different
  5029. cases that we want to benchmark.&lt;/p&gt;
  5030.  
  5031. &lt;h3&gt;Case 1: Ultra Simple&lt;/h3&gt;
  5032.  
  5033. &lt;p&gt;The simplest case in which we generate JSON for a single team object:&lt;/p&gt;
  5034. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5035. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;abbreviation&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;TOR&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5036. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;full_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto Raptors&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5037. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto&amp;quot;&lt;/span&gt;
  5038. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5039. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5040. &lt;h3&gt;Case 2: Simple&lt;/h3&gt;
  5041.  
  5042. &lt;p&gt;Slightly more complex than above in which we embed team objects into
  5043. the main event object:&lt;/p&gt;
  5044. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5045. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;game_date&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2014-05-21&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5046. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;game_type&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Regular Season&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5047. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Final&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5048. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;away_team&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5049. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;abbreviation&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;MIA&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5050. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;full_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Miami Heat&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5051. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Miami&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5052. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;medium_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5053. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;short_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Heat&amp;quot;&lt;/span&gt;
  5054. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  5055. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;home_team&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5056. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;abbreviation&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;TOR&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5057. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;full_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto Raptors&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5058. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5059. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;medium_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5060. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;short_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Raptors&amp;quot;&lt;/span&gt;
  5061. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5062. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5063. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5064. &lt;h3&gt;Case 3: Complex&lt;/h3&gt;
  5065.  
  5066. &lt;p&gt;The most complex case in which we not only embed team objects and
  5067. a box score object into the main event object, but the box score
  5068. in turn also embed a last play object. Also, there are more
  5069. attributes on the event object.&lt;/p&gt;
  5070. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5071. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;game_date&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2014-05-21&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5072. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;game_type&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Regular Season&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5073. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Final&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5074. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;share_url&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;http://thesco.re/123&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5075. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;sport_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;basketball&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5076. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;away_ranking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5077. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;away_region&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Pac-12&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5078. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;home_ranking&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5079. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;home_region&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Top 25&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5080. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;important&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5081. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Washington, DC&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5082. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;away_team&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5083. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;abbreviation&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;MIA&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5084. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;full_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Miami Heat&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5085. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Miami&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5086. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;medium_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5087. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;short_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Heat&amp;quot;&lt;/span&gt;
  5088. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  5089. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;home_team&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5090. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;abbreviation&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;TOR&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5091. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;full_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto Raptors&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5092. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5093. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;medium_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Toronto&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5094. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;short_name&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Raptors&amp;quot;&lt;/span&gt;
  5095. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  5096. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;box_score&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5097. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;has_statistics&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5098. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;progress&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;11:23 2nd&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5099. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;attendance&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;21,307&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5100. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;referees&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thuva, Nate, Roel&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5101. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;last_play&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5102. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;points_type&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Field Goal&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5103. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;player_fouls&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5104. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;player_score&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5105. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;record_type&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Postseason&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5106. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;seconds&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
  5107. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5108. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5109. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5110. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5111. &lt;p&gt;We will also benchmark generating JSON for a collection of 100 objects
  5112. for each case above.&lt;/p&gt;
  5113.  
  5114. &lt;p&gt;Further, the benchmark is run under Ruby 2.1.1, and with the latest versions
  5115. of RABL and AMS (0.9.3 and 0.8.1 respectively) at the time of writing this.
  5116. The machine in which the benchmark is run is irrelevant because we&amp;#39;re only
  5117. interested in relative performance of RABL, AMS, and presenters. You can
  5118. checkout the setup &lt;a href=&quot;https://github.com/thuva/json_serialization_benchmark&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
  5119.  
  5120. &lt;p&gt;We will be running the benchmark twice. You will see why shortly.&lt;/p&gt;
  5121.  
  5122. &lt;h2&gt;First Run&lt;/h2&gt;
  5123.  
  5124. &lt;p&gt;As you can see below, AMS is about 2-3X slower than presenters. Since we&amp;#39;re not going for
  5125. pure speed, it&amp;#39;s still worth using AMS as it provides many features that we will likely to
  5126. implement ourselves for presenters. So we&amp;#39;re going to stop comparing them now.&lt;/p&gt;
  5127.  
  5128. &lt;p&gt;On the other hand, RABL is significantly slower than AMS. It is about 20-25X slower. This
  5129. difference remains the same when we deal with a collection of objects.&lt;/p&gt;
  5130. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;The times you see is the total for 10,000 iterations. Refer to &amp;quot;The Setup&amp;quot; section
  5131. above to understand the different cases: Ultra Simple, Simple, and Complex.
  5132.  
  5133.                                               user     system      total        real
  5134. RABL Ultra Simple                          1.610000   0.420000   2.030000 (  2.029277)
  5135. AMS Ultra Simple                           0.080000   0.000000   0.080000 (  0.083180)
  5136. Presenters Ultra Simple                    0.030000   0.000000   0.030000 (  0.029341)
  5137. --------------------------------------------------------------------------------------
  5138. RABL Simple                                9.080000   2.250000  11.330000 ( 11.342106)
  5139. AMS Simple                                 0.510000   0.000000   0.510000 (  0.505133)
  5140. Presenters Simple                          0.140000   0.000000   0.140000 (  0.140356)
  5141. --------------------------------------------------------------------------------------
  5142. RABL Complex                              19.030000   4.490000  23.520000 ( 23.529937)
  5143. AMS Complex                                1.010000   0.000000   1.010000 (  1.007729)
  5144. Presenters Complex                         0.320000   0.010000   0.330000 (  0.320893)
  5145.  
  5146.  
  5147.                                               user     system      total        real
  5148. RABL Ultra Simple: Collection              1.400000   0.420000   1.820000 (  1.829971)
  5149. AMS Ultra Simple: Collection               0.060000   0.000000   0.060000 (  0.062879)
  5150. Presenters Ultra Simple: Collection        0.020000   0.000000   0.020000 (  0.017285)
  5151. --------------------------------------------------------------------------------------
  5152. RABL Simple: Collection                    8.420000   2.180000  10.600000 ( 10.590213)
  5153. AMS Simple: Collection                     0.440000   0.000000   0.440000 (  0.443547)
  5154. Presenters Simple: Collection              0.110000   0.000000   0.110000 (  0.105298)
  5155. --------------------------------------------------------------------------------------
  5156. RABL Complex: Collection                  18.370000   4.370000  22.740000 ( 22.744374)
  5157. AMS Complex: Collection                    0.940000   0.000000   0.940000 (  0.944308)
  5158. Presenters Complex: Collection             0.270000   0.000000   0.270000 (  0.272239)
  5159. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5160. &lt;h2&gt;Second Run&lt;/h2&gt;
  5161.  
  5162. &lt;p&gt;As per the results of the first benchmark run, RABL is much slower compared to AMS.
  5163. There is no good reason why it has to be. So we did a bit of investigation, and discovered
  5164. that RABL spends a lot of time on template lookup. We can configure RABL to cache this lookup:&lt;/p&gt;
  5165. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rabl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  5166. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_sources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  5167. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  5168. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5169. &lt;p&gt;Below, you can see the results of the benchmark with template lookup caching enabled for RABL.&lt;/p&gt;
  5170.  
  5171. &lt;p&gt;Although performance doubles for RABL, it is still much slower. It is now about 12X
  5172. slower compared to AMS.&lt;/p&gt;
  5173. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;                                               user     system      total        real
  5174. RABL Ultra Simple                          1.050000   0.000000   1.050000 (  1.051644)
  5175. AMS Ultra Simple                           0.080000   0.000000   0.080000 (  0.084204)
  5176. Presenters Ultra Simple                    0.030000   0.000000   0.030000 (  0.028589)
  5177. --------------------------------------------------------------------------------------
  5178. RABL Simple                                5.820000   0.020000   5.840000 (  5.834718)
  5179. AMS Simple                                 0.490000   0.000000   0.490000 (  0.492227)
  5180. Presenters Simple                          0.130000   0.000000   0.130000 (  0.128998)
  5181. --------------------------------------------------------------------------------------
  5182. RABL Complex                              12.790000   0.020000  12.810000 ( 12.810967)
  5183. AMS Complex                                0.990000   0.010000   1.000000 (  0.998783)
  5184. Presenters Complex                         0.330000   0.000000   0.330000 (  0.322893)
  5185.  
  5186.  
  5187.                                               user     system      total        real
  5188. RABL Ultra Simple: Collection              0.830000   0.000000   0.830000 (  0.832709)
  5189. AMS Ultra Simple: Collection               0.060000   0.000000   0.060000 (  0.062520)
  5190. Presenters Ultra Simple: Collection        0.020000   0.000000   0.020000 (  0.018377)
  5191. --------------------------------------------------------------------------------------
  5192. RABL Simple: Collection                    5.420000   0.010000   5.430000 (  5.428974)
  5193. AMS Simple: Collection                     0.440000   0.000000   0.440000 (  0.444187)
  5194. Presenters Simple: Collection              0.100000   0.000000   0.100000 (  0.103999)
  5195. --------------------------------------------------------------------------------------
  5196. RABL Complex: Collection                  12.350000   0.010000  12.360000 ( 12.364715)
  5197. AMS Complex: Collection                    0.940000   0.010000   0.950000 (  0.942812)
  5198. Presenters Complex: Collection             0.270000   0.000000   0.270000 (  0.263692)
  5199. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5200. &lt;h2&gt;Conclusion&lt;/h2&gt;
  5201.  
  5202. &lt;p&gt;To reiterate, AMS is significantly faster compared RABL. Migrating to AMS from RABL
  5203. not only gives us greater ease of use and maintenance, but it also gives us great
  5204. performance gains. So the effort to do the migration is rightly justified.&lt;/p&gt;
  5205.  
  5206. &lt;p&gt;Benchmarking is a tricky business. There might be some big holes in the benchmarking
  5207. above. Checkout out the setup &lt;a href=&quot;https://github.com/thuva/json_serialization_benchmark&quot;&gt;here&lt;/a&gt;,
  5208. and feel free to comment if you find any issues with it.&lt;/p&gt;
  5209.  
  5210. &lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: When the post was originally written, the assumption was that
  5211. &lt;a href=&quot;https://github.com/ohler55/oj&quot;&gt;Oj&lt;/a&gt; was being used as the JSON encoding engine by all
  5212. of RABL, AMS, and presenters However, it turned out not to be the case.&lt;/p&gt;
  5213.  
  5214. &lt;p&gt;In fact, it was only being used by RABL as it is the engine RABL uses by default. In
  5215. othewords, the benchmark was unfair to AMS and presenters. With the Oj boost to AMS and
  5216. presenters, the relative performance of RABL gets even worse. This post has been updated
  5217. to include the latest benchmark results. You can find the original benchmark results
  5218. &lt;a href=&quot;https://gist.github.com/thuva/69f2cccee857e4fdc4f0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
  5219. </description>
  5220.    </item>
  5221.    
  5222.    
  5223.    
  5224.    <item>
  5225.      <title>Analyzing S3 and CloudFront Access Logs with AWS RedShift</title>
  5226.      <link>https://techblog.thescore.com/2014/05/14/analyzing-s3-and-cloudfront-access-logs-with-redshift/</link>
  5227.      <pubDate>Wed, 14 May 2014 00:00:00 +0000</pubDate>
  5228.      <author></author>
  5229.      <guid>https://techblog.thescore.com/2014/05/14/analyzing-s3-and-cloudfront-access-logs-with-redshift</guid>
  5230.      <description>&lt;p&gt;It&amp;#39;s not really a surprise that we deal with fairly high volumes of user
  5231. traffic at theScore. We use AWS for pretty much all of our hosting needs and
  5232. their two object distribution services - CloudFront and S3 - are two of the
  5233. most heavily used offerings in our environment. S3 provides file storage for
  5234. essentially unlimited data, split across as many &amp;quot;buckets&amp;quot; as you would like
  5235. to create. Separate from that is CloudFront, which is a content distribution
  5236. network similar to Akamai or Fastly. CloudFront points at one or more origins,
  5237. and adheres to standard HTTP caching headers. Typically we will deploy S3
  5238. buckets or application servers behind CloudFront &amp;quot;distributions&amp;quot; - this allows
  5239. the majority of HTTP requests to our properties be soaked up by CloudFront
  5240. edge servers.&lt;/p&gt;
  5241.  
  5242. &lt;p&gt;On a daily basis this works very well for us. The challenge comes though when
  5243. we need to perform any analysis on these logs. The S3 and CloudFront logs for
  5244. us are easily terabytes of data per year, and traditional log parsers tend to
  5245. not handle that size of data. There are some providers out there that specialize
  5246. in parsing these logs, and none of them have been able to provide reports on a
  5247. dataset of this size unfortunately.&lt;/p&gt;
  5248.  
  5249. &lt;h2&gt;Columnar Databases&lt;/h2&gt;
  5250.  
  5251. &lt;p&gt;So enter RedShift. RedShift is a fairly new offering from AWS. It&amp;#39;s a columnar
  5252. database built off of PostgreSQL. The scope of this post doesn&amp;#39;t go into the
  5253. details of columnar data stores, but the TL;DR is that instead of arranging data
  5254. on disk by records (or rows) the data is stored by each column. So say you have
  5255. a classic &amp;quot;users&amp;quot; table with username, e-mail, and password columns. In MySQL on
  5256. disk the data will be stored in exactly that order first for user ID 1, then 2,
  5257. then 3 and so on. With a columnar store all the usernames are first stored, and
  5258. then afterwards all of the e-mail addresses, and then all of the passwords.&lt;/p&gt;
  5259.  
  5260. &lt;p&gt;This drives a couple properties that make RedShift and others very fast when used
  5261. for data analysis. Due to the columns being grouped together physically, running
  5262. reports across specific columns of your data is very, very fast. For the above
  5263. contrived &amp;quot;users&amp;quot; example, this isn&amp;#39;t very relevant. Instead however think about
  5264. querying invoices in this type of story - very often you&amp;#39;d like to run reports
  5265. that are grouped by the company responsible for the invoice, the payment method,
  5266. and perhaps summary information on the dollar amount of the invoice.&lt;/p&gt;
  5267.  
  5268. &lt;p&gt;There are of course several downsides that would stop you from using RedShift or
  5269. its brethren for online transaction processing. Namely pretty much none of them are
  5270. ACID compliant, which is a big deal if you actually value your data (or at least,
  5271. it&amp;#39;s a big deal when you need ACID compliance). The other primary disadvantage is
  5272. that since the data is essentially being stored in a non-natural form, inserts and
  5273. updates are much more costly than a standard database and updates to the data
  5274. definition are usually impossible without recreating tables.&lt;/p&gt;
  5275.  
  5276. &lt;p&gt;With these constrains though these databases are still a fantastic tool when paired
  5277. with a more traditional SQL database. Usually you will feed the data from your OLTP
  5278. system into your data warehouse on a scheduled basis.&lt;/p&gt;
  5279.  
  5280. &lt;h2&gt;RedShift for log data&lt;/h2&gt;
  5281.  
  5282. &lt;p&gt;Log data is an interesting case for RedShift. In our environment as mentioned
  5283. previously we have so much log data from our CloudFront and S3 usage that nobody
  5284. could conceivably work with those datasets using standard text tools such as grep
  5285. or tail. Many people load their access logs into databases, but we have not found
  5286. this to be feasible using MySQL or PostgreSQL due to the fact that ad-hoc queries
  5287. run against sets with billions of rows can take hours. Once imported into RedShift
  5288. the same queries take minutes at the most.&lt;/p&gt;
  5289.  
  5290. &lt;h2&gt;Creating a RedShift instance&lt;/h2&gt;
  5291.  
  5292. &lt;p&gt;Make sure you have the latest version of the &lt;a href=&quot;https://aws.amazon.com/cli&quot;&gt;AWS CLI tools&lt;/a&gt;.
  5293. You can install this if you already have Python and pip by simply running
  5294. &lt;code&gt;pip install awscli&lt;/code&gt;&lt;/p&gt;
  5295. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;&lt;/span&gt;~$&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;aws&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;redshift&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;create-cluster&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  5296. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--cluster-identifier&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;logdata&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  5297. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--cluster-type&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;single-node&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  5298. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--node-type&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;dw1.xlarge&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  5299. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--master-username&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;luke&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  5300. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--master-user-password&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;password&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  5301. &lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--cluster-security-groups&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;default
  5302. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5303. &lt;p&gt;Wait for the cluster to boot - you can check the status at any time using:&lt;/p&gt;
  5304. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;&lt;/span&gt;~$&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;aws&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;redshift&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;describe-clusters&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;--cluster-identifier&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;logdata
  5305. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5306. &lt;p&gt;This should output quite a bit of useful data about your cluster:&lt;/p&gt;
  5307. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5308. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;Clusters&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  5309. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5310. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;PubliclyAccessible&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5311. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;MasterUsername&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;luke&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5312. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;VpcSecurityGroups&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
  5313. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;NumberOfNodes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5314. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;PendingModifiedValues&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
  5315. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterVersion&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5316. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;AutomatedSnapshotRetentionPeriod&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5317. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterParameterGroups&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  5318. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5319. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ParameterGroupName&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;default.redshift-1.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5320. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ParameterApplyStatus&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;in-sync&amp;quot;&lt;/span&gt;
  5321. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5322. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  5323. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;DBName&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;dev&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5324. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;PreferredMaintenanceWindow&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;wed:03:00-wed:03:30&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5325. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;Endpoint&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5326. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;Port&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5439&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5327. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;Address&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;your-endpoint.us-east-1.redshift.amazonaws.com&amp;quot;&lt;/span&gt;
  5328. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  5329. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;AllowVersionUpgrade&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5330. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterCreateTime&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2014-05-12T17:13:26.280Z&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5331. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterSecurityGroups&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  5332. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5333. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;Status&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;active&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5334. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterSecurityGroupName&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;default&amp;quot;&lt;/span&gt;
  5335. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5336. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  5337. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterIdentifier&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;logdata&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5338. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterNodes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  5339. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  5340. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;NodeRole&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;SHARED&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5341. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;PrivateIPAddress&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;snip&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5342. &lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;PublicIPAddress&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;snip&amp;quot;&lt;/span&gt;
  5343. &lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5344. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  5345. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;AvailabilityZone&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;us-east-1d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5346. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;NodeType&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;dw1.xlarge&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5347. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;Encrypted&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5348. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterRevisionNumber&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;786&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5349. &lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;quot;ClusterStatus&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;available&amp;quot;&lt;/span&gt;
  5350. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5351. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  5352. &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  5353. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5354. &lt;p&gt;You can see that the cluster status is set to &amp;quot;active&amp;quot;. Also shown is the
  5355. endpoint that you should use to connect to the RedShift cluster; the standard
  5356. Postgres command line tools should work just fine:&lt;/p&gt;
  5357. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;&lt;/span&gt;~$&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;psql&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-h&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;your-endpoint.us-east-1.redshift.amazonaws.com&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-p&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5439&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-U&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;luke&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-W
  5358. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5359. &lt;h2&gt;Creating the initial schema&lt;/h2&gt;
  5360.  
  5361. &lt;p&gt;Note that we will be creating separate tables for S3 and CloudFront logs, simply
  5362. because the format differs in some small ways. Common columns share their names so
  5363. that any queries you end up writing against the data will work in either.&lt;/p&gt;
  5364.  
  5365. &lt;p&gt;Run the following creation statements through the PostgreSQL client:&lt;/p&gt;
  5366. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s3_logentries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  5367. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creator&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RUNLENGTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5368. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5369. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logdate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SORTKEY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5370. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logtime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5371. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5372. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5373. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5374. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5375. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestkey&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5376. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DISTKEY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5377. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5378. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errorcode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5379. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytessent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5380. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objectsize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5381. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;totaltime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5382. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;turnaroundtime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5383. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;referer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5384. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;useragent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5385. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;versionid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;
  5386. &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  5387.  
  5388. &lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cf_logentries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  5389. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logdate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SORTKEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5390. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logtime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5391. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5392. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytessent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5393. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5394. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5395. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5396. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DISTKEY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5397. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5398. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creferrer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5399. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;useragent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5400. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5401. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5402. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_edge_result_type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5403. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_edge_request_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5404. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_host_header&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5405. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5406. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_bytes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5407. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time_taken&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ENCODE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LZO&lt;/span&gt;
  5408. &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  5409. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5410. &lt;p&gt;There are a few things to call out here. You&amp;#39;ll note that in most cases there&amp;#39;s not
  5411. really much thought given to the data type - instead of limiting the &lt;code&gt;VARCHAR&lt;/code&gt;s and what
  5412. not, it&amp;#39;s just relaxed with &lt;code&gt;MAX&lt;/code&gt; size constraints for each column. Typically you&amp;#39;d want
  5413. to size things appropriately; however for our simple examples this really has no effect
  5414. on the performance of our queries.&lt;/p&gt;
  5415.  
  5416. &lt;p&gt;Absent are any specifications for index creation and foreign key constraints. RedShift
  5417. (like most databases of it&amp;#39;s type) don&amp;#39;t require or even support index specifications as
  5418. they are not used by the data storage backend or the query planner. Foreign key
  5419. constraints, since they depend on traditional indexes, are also unsupported although
  5420. RedShift will accept their creation for informational purposes only.&lt;/p&gt;
  5421.  
  5422. &lt;p&gt;Additionally there&amp;#39;s some new keywords here. The first is the &lt;code&gt;ENCODE&lt;/code&gt; keyword. RedShift
  5423. will compress all data that you import, and each column can have a separate compression
  5424. definition. In practice if you leave off the compression stanza then RedShift will wait
  5425. for a couple hundred thousand rows to be inserted and then begin analyzing the columns
  5426. to select an appropriate compression algorithm. Once you&amp;#39;ve worked with similar datasets
  5427. though, you can capture these compression specifications and re-use them in your table
  5428. creation.&lt;/p&gt;
  5429.  
  5430. &lt;p&gt;Last are the &lt;code&gt;SORTKEY&lt;/code&gt; and &lt;code&gt;DISTKEY&lt;/code&gt; options on columns. &lt;code&gt;SORTKEY&lt;/code&gt; specifies how the data in
  5431. a table is stored on disk, and will dictate the natural order. If most of your reports
  5432. are constrained or ordered by a single column then that would be the best candidate for
  5433. this key (in our example the date is an obvious one).&lt;/p&gt;
  5434.  
  5435. &lt;p&gt;&lt;code&gt;DISTKEY&lt;/code&gt; needs a little more explaining. RedShift is a multiple node system, and can have
  5436. anywhere from 1 to 100 database nodes participating in your instance. This test instance,
  5437. and the one we actually use right now at theScore, are both single instance. However, when
  5438. you branch out into a multi-instance topology then RedShift will begin sharding your data
  5439. across the nodes using the chosen &lt;code&gt;DISTKEY&lt;/code&gt;. The &lt;a href=&quot;http://docs.aws.amazon.com/redshift/latest/dg/c_best-practices-best-dist-key.html&quot;&gt;documentation&lt;/a&gt;
  5440. recommends that you select a distribution key that will be used frequently in &lt;code&gt;JOIN&lt;/code&gt;s, which
  5441. is why we pick the URI. We don&amp;#39;t actually use multiple nodes here though, so it&amp;#39;s just
  5442. for future reference.&lt;/p&gt;
  5443.  
  5444. &lt;h2&gt;Load some data!&lt;/h2&gt;
  5445.  
  5446. &lt;p&gt;Now that you actually have the schema up and running, let&amp;#39;s actually load some data.
  5447. CloudFront distributions and S3 buckets both can be configured to log to a specific bucket
  5448. and key; obviously that has to be working for these examples. Let&amp;#39;s assume that we have
  5449. a distribution in CloudFront with the ID of &lt;code&gt;E1DHT7QI9H0ZOB&lt;/code&gt;, and that is&amp;#39; logging to a
  5450. bucket named &lt;code&gt;cloudfront-logs&lt;/code&gt;.&lt;/p&gt;
  5451.  
  5452. &lt;p&gt;RedShift adds a &lt;code&gt;COPY&lt;/code&gt; command to the Postgres language that allows you to import logs from
  5453. that bucket directly. It&amp;#39;s quite flexible and allows you to specify delimiters, error
  5454. thresholds, compression schemas and so on. For our simple example though, we&amp;#39;ll just load
  5455. one month of logs from just one of our CloudFront distributions:&lt;/p&gt;
  5456. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cf_logentries&lt;/span&gt;
  5457. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;s3://cloudfront-logs/E1DHT7QI9H0ZOB.2014-04-&amp;#39;&lt;/span&gt;
  5458. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CREDENTIALS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;aws_access_key_id=;aws_secret_access_key=&amp;#39;&lt;/span&gt;
  5459. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DELIMITER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MAXERROR&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FILLRECORD&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IGNOREHEADER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  5460. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5461. &lt;p&gt;We&amp;#39;re loading standard, tab-delimited gzip-compressed log files with the headers ignored.
  5462. Additionally we&amp;#39;re using the &lt;code&gt;FILLRECORD&lt;/code&gt; parameter. This causes the import process to
  5463. pad out missing records at the end of a line with &lt;code&gt;NULL&lt;/code&gt; values. This is pretty useful as
  5464. periodically the CloudFront and S3 log formats change, and they add new columns. Finally,
  5465. we&amp;#39;re telling the command to abort the copy if 200 errors are found.&lt;/p&gt;
  5466.  
  5467. &lt;p&gt;This could take some - in my example the full load took just over 3 hours:&lt;/p&gt;
  5468. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;INFO:  Load into table &amp;#39;cf_logentries&amp;#39; completed, 137647904 record(s) loaded successfully.
  5469. COPY
  5470. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5471. &lt;p&gt;You can use the same &lt;code&gt;COPY&lt;/code&gt; command to load your S3 logs as long as you specify the correct
  5472. table as the first parameter to the command.&lt;/p&gt;
  5473.  
  5474. &lt;h2&gt;Running Queries&lt;/h2&gt;
  5475.  
  5476. &lt;p&gt;That&amp;#39;s pretty much it for the setup. Now you can just run arbitrary queries against the log
  5477. tables. Here some fairly trivial examples that have come in handy:&lt;/p&gt;
  5478. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hits&lt;/span&gt;
  5479. &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cf_logentries&lt;/span&gt;
  5480. &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hits&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  5481. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5482. &lt;p&gt;Dead simple; this takes all the records and just spits out the unique URIs by popularity:&lt;/p&gt;
  5483. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;                           uri                            |   hits
  5484. ----------------------------------------------------------+----------
  5485. /api/v1/news                                             | 17017442
  5486. /api/v1/news.json                                        |  9923266
  5487. /api/v1/rivers/nhl/news/pinned                           |  6901556
  5488. /api/v1/rivers/nhl/news                                  |  6280842
  5489. /api/v1/rivers/nba/news/pinned                           |  5227507
  5490. /api/v1/rivers/top_news/news                             |  5051595
  5491. /api/v1/rivers/nba/news                                  |  4705110
  5492. /api/v1/rivers/nfl/news/pinned                           |  4565983
  5493. /api/v1//news.json                                       |  4342518
  5494. /api/v1/rivers/nfl/news.json                             |  4206018
  5495. /api/v1/rivers/nfl/news                                  |  3379180
  5496. /api/v1/nhl                                              |  2994995
  5497. /api/v1/rivers/mlb/news/pinned                           |  2992940
  5498. /api/v1/rivers/mlb/news                                  |  2649503
  5499. /api/v1/rivers/top_news/news/pinned                      |  2516112
  5500. /api/v1/rivers/nhl/news.json                             |  2493204
  5501. /api/v1/rivers/nba/news.json                             |  2481048
  5502. /api/v1/rivers/1/news.json                               |  2278832
  5503. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5504. &lt;p&gt;As simple as it is, the query is looking at 135 million rows and executes in
  5505. approximately 10 seconds using the base RedShift instance type. You can modify
  5506. the query a bit and see requests per day like so:&lt;/p&gt;
  5507. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  5508. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;day&lt;/span&gt;
  5509. &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cf_logentries&lt;/span&gt;
  5510. &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;day&lt;/span&gt;
  5511. &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  5512. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;  count  |    day
  5513. ---------+------------
  5514. 4180128 | 2014-04-01
  5515. 4874722 | 2014-04-02
  5516. 4402991 | 2014-04-03
  5517. 4781033 | 2014-04-04
  5518. 4676276 | 2014-04-05
  5519. 4781785 | 2014-04-06
  5520. 3863464 | 2014-04-07
  5521. 4368877 | 2014-04-08
  5522. 5306479 | 2014-04-09
  5523. 4870124 | 2014-04-10
  5524. 4141415 | 2014-04-11
  5525. 4896455 | 2014-04-12
  5526. 5314497 | 2014-04-13
  5527. 4695349 | 2014-04-14
  5528. 4315279 | 2014-04-15
  5529. 4569367 | 2014-04-16
  5530. 4509744 | 2014-04-17
  5531. 3657470 | 2014-04-18
  5532. 4069335 | 2014-04-19
  5533. 4127096 | 2014-04-20
  5534. 4445855 | 2014-04-21
  5535. 5043469 | 2014-04-22
  5536. 4803794 | 2014-04-23
  5537. 4538804 | 2014-04-24
  5538. 3929871 | 2014-04-25
  5539. 5056818 | 2014-04-26
  5540. 4710418 | 2014-04-27
  5541. 4640340 | 2014-04-28
  5542. 5389785 | 2014-04-29
  5543. 4686864 | 2014-04-30
  5544. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5545. &lt;p&gt;With CloudFront you really should care about your cache hit ratio - maybe it&amp;#39;s
  5546. obvious, but the load on your origin systems decrease as your content becomes
  5547. easier to cache. This query will look at the most used URLs and give you a cache
  5548. hit ratio:&lt;/p&gt;
  5549. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_hits&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  5550. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5551. &lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hits&lt;/span&gt;
  5552. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cf_logentries&lt;/span&gt;
  5553. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_edge_result_type&lt;/span&gt;
  5554. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;RefreshHit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  5555. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;
  5556. &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  5557. &lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  5558. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  5559. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;
  5560. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cf_logentries&lt;/span&gt;
  5561. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;
  5562. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;
  5563. &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  5564. &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5565. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5566. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5567. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_hits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hits&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cachehits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  5568. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cachehits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hitrate&lt;/span&gt;
  5569. &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;
  5570. &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_hits&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_hits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;
  5571. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_hits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cs_uri_query&lt;/span&gt;
  5572. &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_uris&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  5573. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5574. &lt;p&gt;One minute later, I get these results.&lt;/p&gt;
  5575. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;&lt;/span&gt;                 uri                 |          qs          | requests | cachehits | hitrate
  5576. -------------------------------------+----------------------+----------+-----------+---------
  5577. /api/v1/rivers/nhl/news/pinned      | limit=20             |  5301492 |   5136042 | 96.87
  5578. /api/v1/rivers/nba/news/pinned      | limit=20             |  3750216 |   3553899 | 94.76
  5579. /api/v1/rivers/nfl/news/pinned      | limit=20             |  2540830 |   2385556 | 93.88
  5580. /api/v1/rivers/top_news/news/pinned | limit=10             |  2516105 |   2353308 | 93.52
  5581. /api/v1/rivers/mlb/news/pinned      | limit=20             |  2102154 |   1952020 | 92.85
  5582. /api/v1/rivers/nfl/news/pinned      | -                    |  2025153 |   1845123 | 91.11
  5583. /api/v1/rivers/nhl/news             | limit=14             |  2010704 |   1933622 | 96.16
  5584. /api/v1/rivers/nfl/news.json        | limit=10             |  1869261 |   1696612 | 90.76
  5585. /api/v1/rivers/nhl/news             | limit=15             |  1845157 |   1776506 | 96.27
  5586. /api/v1/rivers/nhl/news/pinned      | -                    |  1600064 |   1454696 | 90.91
  5587. /api/v1/rivers/nhl/news.json        | limit=10             |  1515039 |   1379522 | 91.05
  5588. /api/v1/rivers/nba/news             | limit=14             |  1480116 |   1396547 | 94.35
  5589. /api/v1/rivers/nba/news/pinned      | -                    |  1477291 |   1291877 | 87.44
  5590. /api/v1/rivers/nba/news.json        | limit=10             |  1371061 |   1194216 | 87.10
  5591. /api/v1/rivers/top_news/news        | limit=20&amp;amp;embed_ids=t |  1289425 |   1139939 | 88.40
  5592. /api/v1/rivers/1/news/pinned        | -                    |  1167849 |   1000635 | 85.68
  5593. /api/v1/rivers/top_news/news        | limit=4              |  1122927 |   1040506 | 92.66
  5594. /api/v1/rivers/nba/news             | limit=15             |  1114807 |   1051494 | 94.32
  5595. /api/v1/rivers/1/news.json          | limit=10             |  1072153 |    909073 | 84.78
  5596. /api/v1/rivers/mlb/news             | limit=14             |   933096 |    862023 | 92.38
  5597. /api/v1/rivers/mlb/news/pinned      | -                    |   890785 |    755674 | 84.83
  5598. /api/v1/rivers/nfl/news             | limit=16             |   868015 |    807376 | 93.01
  5599. /api/v1/rivers/mlb/news.json        | limit=10             |   820678 |    696375 | 84.85
  5600. /api/v1/rivers/mlb/news             | limit=15             |   805442 |    743153 | 92.26
  5601. /api/v1/rivers/top_news/news        | limit=5              |   739409 |    690218 | 93.34
  5602. /api/v1/rivers/nfl/news             | limit=17             |   623315 |    581964 | 93.36
  5603. /api/v1/rivers/nba/news             | limit=16             |   582077 |    551094 | 94.67
  5604. /api/v1/rivers/nhl/news             | limit=16             |   572628 |    552693 | 96.51
  5605. /api/v1/rivers/nfl/news             | limit=15             |   454028 |    425197 | 93.64
  5606. /api/v1/rivers/nhl/news             | limit=13             |   438636 |    424338 | 96.74
  5607. /api/v1/rivers/ncaab/news/pinned    | limit=20             |   418286 |    333987 | 79.84
  5608. /api/v1/rivers/ncaab/news/pinned    | -                    |   376280 |    287165 | 76.31
  5609. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5610. &lt;p&gt;These hit rates are decent for most things, but you can definitely see right away
  5611. how the query parameters can be tuned a bit to provide an ever faster experience for
  5612. clients consuming these endpoints.&lt;/p&gt;
  5613.  
  5614. &lt;h2&gt;Conclusion&lt;/h2&gt;
  5615.  
  5616. &lt;p&gt;That&amp;#39;s pretty much it for now. There&amp;#39;s a lot more you can do with this, as the sky is the
  5617. limit when it comes to the queries you can write against your data. Hopefully this was a
  5618. beneficial introduction to RedShift with a novel use case. If I got anything wrong let
  5619. me know!&lt;/p&gt;
  5620.  
  5621. &lt;p&gt;There are other obvious related things you&amp;#39;d want to do if you use a setup like this
  5622. (such as automating the data load) that we may cover in a future post.&lt;/p&gt;
  5623. </description>
  5624.    </item>
  5625.    
  5626.    
  5627.    
  5628.    <item>
  5629.      <title>Using Airbrake with Rake and not Rails</title>
  5630.      <link>https://techblog.thescore.com/2014/05/12/using-airbrake-with-rake-and-not-rails/</link>
  5631.      <pubDate>Mon, 12 May 2014 00:00:00 +0000</pubDate>
  5632.      <author></author>
  5633.      <guid>https://techblog.thescore.com/2014/05/12/using-airbrake-with-rake-and-not-rails</guid>
  5634.      <description>&lt;p&gt;We use &lt;a href=&quot;http://airbrake.io&quot;&gt;Airbrake&lt;/a&gt; for our error reporting. An error happens,
  5635. the error gets sent to Airbrake and then we&amp;#39;re notified via email. We can then
  5636. investigate all the goodness from their website. Fantastic!&lt;/p&gt;
  5637.  
  5638. &lt;p&gt;Most Ruby apps are centered around some kind of web service (mainly Ruby on Rails).
  5639. As such, Airbrake has &lt;em&gt;really&lt;/em&gt; good Rails integration – even on their rake tasks!
  5640. Airbrake is in a lot of our projects, some of which that do not use Rails, however.&lt;/p&gt;
  5641.  
  5642. &lt;p&gt;A problem that I noticed while debugging why a rake task hadn&amp;#39;t reported errors to Airbrake was two fold:&lt;/p&gt;
  5643.  
  5644. &lt;ol&gt;
  5645. &lt;li&gt;Airbrake is &lt;strong&gt;not&lt;/strong&gt; configured to rescue from rake exceptions by default&lt;/li&gt;
  5646. &lt;li&gt;Even if you have it enabled, it only works if you have &lt;em&gt;Rails loaded&lt;/em&gt;&lt;/li&gt;
  5647. &lt;/ol&gt;
  5648.  
  5649. &lt;p&gt;Isn&amp;#39;t that crazy? &lt;a href=&quot;https://github.com/airbrake/airbrake/blob/51bf71ba517e9dbb1adb26f52eb6fa73a2f21c86/lib/airbrake/railtie.rb#L9&quot;&gt;Here&amp;#39;s the (insane) code that necessitates it&lt;/a&gt;.
  5650. It definitely wasn&amp;#39;t at the forefront of my mind to realize that enabling
  5651. &lt;code&gt;rescue_rake_exceptions&lt;/code&gt; wouldn&amp;#39;t rescue from...rake exceptions.&lt;/p&gt;
  5652.  
  5653. &lt;p&gt;The solution is to follow their &lt;a href=&quot;https://github.com/airbrake/airbrake/wiki/Using-Airbrake-with-Rake&quot;&gt;wiki entry&lt;/a&gt;
  5654. (which I had to edit to make it correct):&lt;/p&gt;
  5655. &lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;airbrake/rake_handler&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# require this if you don&amp;#39;t have rails loaded&lt;/span&gt;
  5656.  
  5657. &lt;span class=&quot;no&quot;&gt;Airbrake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  5658. &lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rescue_rake_exceptions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  5659. &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  5660. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  5661. &lt;p&gt;I&amp;#39;ve &lt;a href=&quot;https://github.com/airbrake/airbrake/issues/292&quot;&gt;raised the issue&lt;/a&gt; on
  5662. Airbrake&amp;#39;s github. Feel free to visit the link and weigh in!&lt;/p&gt;
  5663.  
  5664. &lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; we &lt;a href=&quot;https://github.com/airbrake/airbrake/pull/294&quot;&gt;have a solution&lt;/a&gt;
  5665. that will be included in the next release of the airbrake gem!&lt;/p&gt;
  5666. </description>
  5667.    </item>
  5668.    
  5669.    
  5670.  
  5671.  </channel>
  5672. </rss>
  5673.  
Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda