<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Bootstrap Me]]></title><description><![CDATA[Bootstrap Me]]></description><link>https://bootstrap.me.uk</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 04:38:05 GMT</lastBuildDate><atom:link href="https://bootstrap.me.uk/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Career advice for junior developers]]></title><description><![CDATA[title: Career advice for junior developers
published: true
description: 
tags: codenewbie, beginners, career, programming
I had a nice chat this morning with our new intern, who's due to start working with us this summer. One of the things he asked m...]]></description><link>https://bootstrap.me.uk/career-advice-for-junior-developers-1</link><guid isPermaLink="true">https://bootstrap.me.uk/career-advice-for-junior-developers-1</guid><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[career advice]]></category><category><![CDATA[Junior developer ]]></category><dc:creator><![CDATA[Fred Heath]]></dc:creator><pubDate>Tue, 14 Apr 2020 10:37:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726436728959/19811511-699b-4b9c-91d8-ae04ef6e80b7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<p>title: Career advice for junior developers
published: true
description: </p>
<h2 id="heading-tags-codenewbie-beginners-career-programming">tags: codenewbie, beginners, career, programming</h2>
<p>I had a nice chat this morning with our new intern, who's due to start working with us this summer. One of the things he asked me was if I could give him any tips about things he should be looking to learn in order to advance his career in programming. In my mind, I started formulating a list of programming languages, web frameworks, libraries and technologies. I was then struck by the thought that most things on my list would be obsolete in 5-10 years. I considered whether knowing the intricacies of a specific language, or how a framework is put together or how the Model-View-Controller paradigm works, was a solid foundation for a successful career in Software Development. The clear answer to my own question was that no, all these things are transient. They are here today, gone tomorrow. 
An old proverb sprang to mind: "You give someone a fish, they'll eat for a day. Teach them to fish and they'll be eating for a lifetime".</p>
<p>So this is what I told him: "Learn whatever catches your eye, excites you and you feel comfortable with. But whatever you do, keep in mind the following":  </p>
<h3 id="heading-1-enjoy-what-you-do">1. Enjoy what you do</h3>
<p>You'll be spending one of the biggest parts of your life working (the other big part is sleeping but we can't help that). If the tools or people you're working with frustrate, impede or depress you, then change them. Sometimes, this may require changing jobs. That's fine, you have no obligation to suffer in exchange for money or out of some mis-placed sense of loyalty. By enjoying what you do at work, you're going to be better at it and more productive. Enjoying being round your colleagues will make you more collaborative and help you learn new things. Most importantly, by working in an enjoyable environment you will also be safeguarding your mental health, this most precious of goods.</p>
<h3 id="heading-2-coding-isnt-enough">2. Coding isn't enough</h3>
<p>To be a good developer you need to be able to code. To be a great developer you need to be able to:</p>
<ul>
<li>Communicate your assumptions and expectations to others.</li>
<li>Empathise with others and understand their point of view.</li>
<li>Plan ahead with forethought and discretion.</li>
<li>Manage other people's expectations of you.</li>
</ul>
<p>Unfortunately, there aren't any boot camps for these skills. These are skills you need to hone and cultivate yourself. They take time and effort and not everyone manages to accomplish all of them. But mastering these skills can make all the difference between yet another coder and an effective software developer.</p>
<h3 id="heading-3-you-read-more-code-than-you-write">3. You read more code than you write</h3>
<p>You may not think so, but, trust me, you do. So what does this mean? It means that when you write code you must keep in mind that someone else (or even worse, you 12 months later) will have to read it. So make sure your code is readable, well-documented and trimmed down to what is needed to deliver the intended piece of functionality. Ensure your codebase is <em>not</em> bloated with clever code hacks or complex design patterns and architectures which only exist to inflate people's egos or adhere to some dogmatic pattern or methodology. Keep your code simple, modular and readable. Your colleagues and future self will thank you for it.</p>
<h3 id="heading-4-separate-the-things-that-change-from-the-things-that-stay-the-same">4. Separate the things that change from the things that stay the same</h3>
<p>Everything changes over time, but there are things that change at predictable occasions or at regular frequency. Make sure these things are separated from stuff that doesn't often change. So keep your UI code well away from your business-logic code. Keep your data-reading code away from your data-formatting code. If you are dealing with external APIs, hide them behind an adaptor or facade. The APIs will be changing a lot more frequently than your internal usage of them, i.e. your facade. Apply this principle throughout your code and designs. </p>
<h3 id="heading-5-develop-iteratively-and-incrementally">5. Develop iteratively and incrementally</h3>
<p>Never try to build a complete and perfect system in one go. Focus on the most needed and clear features first and provide the simplest working solution to them. Then build-up your project over many iterations, each iteration refining existing features and delivering new ones. </p>
<p>If you're not sure what an iterative and incremental approach is, then <a target="_blank" href="https://itsadeliverything.com/revisiting-the-iterative-incremental-mona-lisa">this</a> should help clarify it.</p>
<h3 id="heading-6-premature-optimisation-kills-projects">6. Premature optimisation kills projects</h3>
<p>Do not try to anticipate future needs without solid evidence that these needs are likely to arise. If your web-site ever needs to handle 10 zillion requests per minute then don't worry, at that stage you'll be making enough money to comfortably retire at your own private island, so focus on that instead ;) .The point is, do not optimise pre-maturely, 'just in case'. Do not work on the basis of 'what-ifs'. If you do, you'll only add unnecessary complexity in your code, increase the likelihood of bugs and reduce its readability. On that note, prefer languages and frameworks that promote flexibility and ease-of-change.</p>
<h3 id="heading-7-no-silver-bullet">7. No Silver Bullet</h3>
<p>To quote <a target="_blank" href="&quot;https://is.muni.cz/www/jirqa/The.Mythical.Man.Month.F.Brooks.pdf">Mr Brooks</a></p>
<blockquote>
<p>There is no single development, in either technology or management technique, which by itself promises even one order of magnitude [tenfold] improvement within a decade in productivity, in reliability, in simplicity.</p>
</blockquote>
<p>Over your career you'll see many 'hotnesses' come and go. You'll hear people raving on about the fastest web framework, the coolest design paradigm, the best programming language, and so on. All these things may be true within the narrow slice of reality of the people clamouring for them, but they are not necessarily true in your reality. Complex problems, like the ones we face in software development, are never solved by simple solutions, like using a single programming language or web framework. Never be fooled by thinking you found that one thing which will solve all your programming problems. </p>
<h3 id="heading-8-generalise-dont-specialise">8. Generalise, don't specialise</h3>
<p>In nature, the species of animals more likely to survive are the generalist species, that is the species which can exploit different environments and food sources. In contrast, the species more likely to go extinct are the specialists, the ones that rely on a single food source or can only thrive in one environment. The same applies in software development. Do not put all your eggs in one basket, as the saying goes. The ability to do many things well, will see you farther than the ability to one thing brilliantly. </p>
<h2 id="heading-epilogue">Epilogue</h2>
<p>Here you go, I'm sure there are a couple of more items I forgot to add, but I think these capture the fundamental principles I've learned over many years as a software developer. I wish someone told me these things when I was first starting out. Hopefully they may help someone else, so thanks for reading and feel free to add your own advice in the comments.  </p>
]]></content:encoded></item><item><title><![CDATA[Parallelising ETL workflows with the Jongleur gem]]></title><description><![CDATA[Our assignment
Our company has just gone on a huge recruitment spree and has just hired 100,000 new employees. The HR department has sent us a spreadsheet with the employee details, asking us to save them in the company staff database. After having a...]]></description><link>https://bootstrap.me.uk/parallelising-etl-workflows-with-the-jongleur-gem</link><guid isPermaLink="true">https://bootstrap.me.uk/parallelising-etl-workflows-with-the-jongleur-gem</guid><category><![CDATA[Ruby]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Ruby Gem]]></category><category><![CDATA[multiprocessing]]></category><dc:creator><![CDATA[Fred Heath]]></dc:creator><pubDate>Mon, 14 Jan 2019 09:39:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571215501/qdACuv9aX.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<h2 id="heading-our-assignment">Our assignment</h2>
<p>Our company has just gone on a huge recruitment spree and has just hired 100,000 new employees. The HR department has sent us a spreadsheet with the employee details, asking us to save them in the company staff database. After having a look at the data, we notice a few actions we need to take before inserting the details into our database and we come up with the following algorithm:</p>
<ul>
<li><p>Read data from the spreadsheet</p>
</li>
<li><p>Break employees' full name into first name and last name fields</p>
</li>
<li><p>Assign each employee a unique company number</p>
</li>
<li><p>Insert employee into the database as long as their first and last name is less than 255 characters long</p>
</li>
</ul>
<h2 id="heading-a-single-process-etl-pipeline">A single-process ETL pipeline</h2>
<p>We then proceed to write a crude but effective ETL pipeline</p>
<pre><code class="lang-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'csv'</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'pg'</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'ostruct'</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">'securerandom'</span>

@users = []
@conn = PG.connect( <span class="hljs-symbol">dbname:</span> <span class="hljs-string">'testdb'</span> )
DATA_FILE = <span class="hljs-string">"data/users.csv"</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">extract</span><span class="hljs-params">(data_file)</span></span>
  CSV.foreach(data_file) <span class="hljs-keyword">do</span> <span class="hljs-params">|row|</span>
    user = OpenStruct.new
    user.name = row[<span class="hljs-number">0</span>]
    user.email = row[<span class="hljs-number">1</span>]
    @users &lt;&lt; user
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">transform</span></span>
  @users.map! <span class="hljs-keyword">do</span> <span class="hljs-params">|usr|</span>
    usr.first_name = usr.name.split[<span class="hljs-number">0</span>]
    usr.last_name = usr.name.split[<span class="hljs-number">1</span>]
    usr.staff_no = SecureRandom.uuid
    usr
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load</span><span class="hljs-params">(usr)</span></span>
  <span class="hljs-keyword">if</span>( usr.first_name.length &lt; <span class="hljs-number">255</span> &amp;&amp;  usr.last_name.length &lt; <span class="hljs-number">255</span>)
    @conn.exec_params(
      <span class="hljs-string">"INSERT INTO STAFF (FIRST_NAME, LAST_NAME, EMAIL, STAFF_NO) VALUES ($1, $2, $3, $4)"</span>,
      [usr.first_name, usr.last_name, usr.email, usr.staff_no]
    )
  <span class="hljs-keyword">else</span> puts <span class="hljs-string">"Validation failed for <span class="hljs-subst">#{usr.first_name}</span> <span class="hljs-subst">#{usr.last_name}</span>"</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

extract(DATA_FILE)
transform
@users.each {<span class="hljs-params">|usr|</span> load(usr)}
</code></pre>
<p>Our <code>extract</code> method reads the data from file and stores it in memory. The <code>transform</code> method then splits the full name field into first and last name and assigns a UUID to the employee. Finally, our <code>load</code> method ensures the name fields don't exceed the expected length, before inserting the data into the Staff table.</p>
<p>We test our script and it works fine, however all this data crunching takes a long time and, us being the perfectionist that we are, would like to make it faster.</p>
<h2 id="heading-the-need-for-speed">The need for speed</h2>
<p>Enter <a target="_blank" href="https://gitlab.com/RedFred7/Jongleur">Jongleur</a> a Ruby gem that allows us to create tasks that are executed as separate OS processes and that can be ran with a certain precedence.</p>
<p>We'll use Jongleur to split our ETL pipeline into two separate and parallel pipelines, each dealing with one half of the input data. We want our task graph to look like this:</p>
<p><img src="https://filedn.com/lBg7bSLnQRakb21qRGSa3pS/jongleur_etl.png" alt="2x ETL" /></p>
<p>Each Extract, Transform and Load task will do exactly what we described in our initial algorithm above, but with only half of the spreadsheet records. The Cleanup task will kick-in last, to sweep away any undue remnants after our pipeline has been ran.</p>
<p>Representing the above graph in Jongleur is a simple matter of creating a Ruby Hash:</p>
<pre><code class="lang-ruby">etl_graph = {
  <span class="hljs-symbol">Extract1:</span> [<span class="hljs-symbol">:Transform1</span>],
  <span class="hljs-symbol">Transform1:</span> [<span class="hljs-symbol">:Load1</span>],
  <span class="hljs-symbol">Extract2:</span> [<span class="hljs-symbol">:Transform2</span>],
  <span class="hljs-symbol">Transform2:</span> [<span class="hljs-symbol">:Load2</span>],
  <span class="hljs-symbol">Load1:</span> [<span class="hljs-symbol">:Cleanup</span>],
  <span class="hljs-symbol">Load2:</span> [<span class="hljs-symbol">:Cleanup</span>]
}
</code></pre>
<p>We can then just tell Jongleur to use our task graph</p>
<pre><code class="lang-ruby">Jongleur::API.add_task_graph etl_graph
</code></pre>
<h2 id="heading-design-considerations">Design considerations</h2>
<p>One thing we need to consider before creating our parallel tasks is how to share data between tasks which are ran as separate processes. Shared Memory could be used but has some <a target="_blank" href="https://brandur.org/ruby-memory">pitfalls</a> which is why we'll be using the wonderful in-memory data store which is <a target="_blank" href="https://redis.io/">Redis</a>. Redis is (mostly) single-threaded, which means concurrent requests are executed sequentially and safely (and blazingly quickly). Furthermore, we'll take advantage of one of Jongleur's great features whereby each task can access the process ids (pids) of its predecessors. This means that we can use a task's pid as a key for its data. So our Extract1 task can save data to Redis with its own pid and when Transform1 task starts it will know exactly which data was created by Extract1, its predecessor!</p>
<h2 id="heading-common-attributes">Common attributes</h2>
<p>Every Jongleur task class must inherit from the base class <code>Jongleur::WorkerTask</code>. This means that we can use this class to define data that we want accessible from any and all WorkerTask instances. In our case we want any Extract, Transform and Cleanup tasks to be able to access our Redis data store. To achieve that we simply define the Redis connection as a class variable in the <code>Jongleur::WorkerTask</code> hierarchy:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Jongleur::WorkerTask</span></span>
  @@redis = Redis.new(<span class="hljs-symbol">host:</span> <span class="hljs-string">"localhost"</span>, <span class="hljs-symbol">port:</span> <span class="hljs-number">6379</span>, <span class="hljs-symbol">db:</span> <span class="hljs-number">15</span>)
<span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-extracting">Extracting</h2>
<p>Both our Extract1 and Extract2 tasks will need to have some common functionality, namely to parse records from the csv file and save them to Redis:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Extract</span> &lt; Jongleur::WorkerTask</span>

  DATA_FILE = <span class="hljs-string">"data/users.csv"</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_row</span><span class="hljs-params">(row, rowno)</span></span>
    user = {}
    user[<span class="hljs-symbol">:num</span>] = rowno
    user[<span class="hljs-symbol">:name</span>] = row[<span class="hljs-number">0</span>]
    user[<span class="hljs-symbol">:email</span>] = row[<span class="hljs-number">1</span>]
    user

    @@redis.hset(Process.pid.to_s, <span class="hljs-string">"user:<span class="hljs-subst">#{rowno}</span>"</span>, user.to_json)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Now the only divergence between Extract1 and Extract2 comes when reading the input data from the csv file. Extract1 will only read and store the first 50,000 records, while Extract2 will read and store the remaining 50,000 records.</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Extract1</span> &lt; Extract</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span></span>
    CSV.foreach(DATA_FILE).with_index(<span class="hljs-number">1</span>) <span class="hljs-keyword">do</span> <span class="hljs-params">|row, rowno|</span>
      <span class="hljs-keyword">break</span> <span class="hljs-keyword">if</span> rowno &gt;= <span class="hljs-number">50000</span>
      process_row(row, rowno)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Extract2</span> &lt; Extract</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span></span>
    CSV.foreach(DATA_FILE).with_index(<span class="hljs-number">1</span>) <span class="hljs-keyword">do</span> <span class="hljs-params">|row, rowno|</span>
      <span class="hljs-keyword">next</span> <span class="hljs-keyword">if</span> rowno &lt; <span class="hljs-number">50000</span>
      process_row(row, rowno)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-transforming">Transforming</h2>
<p>Each Tranform task will need to use its Extracting predecessor's pid in order to load the extracted records it needs to transform. It will then proceed to split each record's name, assign it a UUID and save it back to Redis:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Transform</span> &lt; Jongleur::WorkerTask</span>
  @desc = <span class="hljs-string">'Transforming'</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span></span>
    loader_id = Jongleur::Implementation.get_process_id(predecessors.first)
    users = @@redis.hgetall(loader_id)
    users.keys.each <span class="hljs-keyword">do</span> <span class="hljs-params">|usr_key|</span>
      transformed_user = {}
      data = JSON.parse users[usr_key]
      transformed_user[<span class="hljs-symbol">:first_name</span>] = data[<span class="hljs-string">'name'</span>].split[<span class="hljs-number">0</span>]
      transformed_user[<span class="hljs-symbol">:last_name</span>] = data[<span class="hljs-string">'name'</span>].split[<span class="hljs-number">1</span>]
      transformed_user[<span class="hljs-symbol">:staff_no</span>] = SecureRandom.uuid
      transformed_user[<span class="hljs-symbol">:num</span>] = data[<span class="hljs-string">'num'</span>]
      transformed_user[<span class="hljs-symbol">:email</span>] = data[<span class="hljs-string">'email'</span>]
      @@redis.hset(Process.pid.to_s, <span class="hljs-string">"user:<span class="hljs-subst">#{data[<span class="hljs-string">'num'</span>]}</span>"</span>, transformed_user.to_json)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Transform1</span> &lt; Transform;</span> <span class="hljs-keyword">end</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Transform2</span> &lt; Transform;</span> <span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-loading">Loading</h2>
<p>Load1 and Load2 will pick up the records stored by their respective predecessor Transform tasks, will validate the name length and will insert the records in the Staff table of our HR's Postgres database:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Load</span> &lt; Jongleur::WorkerTask</span>
  @desc = <span class="hljs-string">'Loading'</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span></span>
    loader_id = Jongleur::Implementation.get_process_id(predecessors.first)
    users = @@redis.hgetall(loader_id)
    conn = PG.connect( <span class="hljs-symbol">dbname:</span> <span class="hljs-string">'testdb'</span> )
    users.keys.each <span class="hljs-keyword">do</span> <span class="hljs-params">|usr_key|</span>
      data = JSON.parse users[usr_key]
      <span class="hljs-keyword">if</span>( data[<span class="hljs-string">'first_name'</span>].length &lt; <span class="hljs-number">255</span> &amp;&amp;  data[<span class="hljs-string">'last_name'</span>].length &lt; <span class="hljs-number">255</span> )
        conn.exec_params(
          <span class="hljs-string">"INSERT INTO STAFF (FIRST_NAME, LAST_NAME, EMAIL, STAFF_NO) VALUES ($1, $2, $3, $4)"</span>,
          [ data[<span class="hljs-string">'first_name'</span>], data[<span class="hljs-string">'last_name'</span>], data[<span class="hljs-string">'email'</span>], data[<span class="hljs-string">'staff_no'</span>] ]
        )
      <span class="hljs-keyword">else</span> puts <span class="hljs-string">"Validation failed for <span class="hljs-subst">#{usr.first_name}</span> <span class="hljs-subst">#{usr.last_name}</span>"</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Load1</span> &lt; Load;</span> <span class="hljs-keyword">end</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Load2</span> &lt; Load;</span> <span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-cleanup">Cleanup</h2>
<p>All our Cleanup task has to do is to erase the Redis data created by the previously-ran tasks</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cleanup</span> &lt; Jongleur::WorkerTask</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span></span>
    @@redis.flushdb
    puts <span class="hljs-string">"...Cleanup"</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-running-our-pipelines">Running our pipelines</h2>
<p>We now have</p>
<ol>
<li><p>Defined our Task Graph and configured Jongleur with it</p>
</li>
<li><p>Implemented an <code>#execute</code> method for each of the Tasks in our Task Graph</p>
</li>
</ol>
<p>We are now ready to invoke Jongleur and run our parallel pipelines:</p>
<pre><code class="lang-ruby">API.run <span class="hljs-keyword">do</span> <span class="hljs-params">|on|</span>
  on.completed <span class="hljs-keyword">do</span> <span class="hljs-params">|task_matrix|</span>
    puts <span class="hljs-string">"Jongleur run is complete \n"</span>
    puts task_matrix
    puts <span class="hljs-string">"oh-oh"</span> <span class="hljs-keyword">if</span> API.failed_tasks(task_matrix).length &gt; <span class="hljs-number">0</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>The output is:</p>
<pre><code class="lang-shell">Starting workflow...
starting task Extract1
starting task Extract2
finished task: Extract1, process: 22722, exit_status: 0, success: true
starting task Transform1
finished task: Extract2, process: 22723, exit_status: 0, success: true
starting task Transform2
finished task: Transform1, process: 22726, exit_status: 0, success: true
starting task Load1
finished task: Transform2, process: 22727, exit_status: 0, success: true
starting task Load2
finished task: Load1, process: 22729, exit_status: 0, success: true
finished task: Load2, process: 22732, exit_status: 0, success: true
starting task Cleanup
...Cleanup
finished task: Cleanup, process: 22745, exit_status: 0, success: true
Workflow finished
Jongleur run is complete
#&lt;struct Jongleur::Task name=:Extract1, pid=22722, running=false, exit_status=0, finish_time=1546800512.6342049, success_status=true&gt;
#&lt;struct Jongleur::Task name=:Transform1, pid=22726, running=false, exit_status=0, finish_time=1546800518.355283, success_status=true&gt;
#&lt;struct Jongleur::Task name=:Extract2, pid=22723, running=false, exit_status=0, finish_time=1546800512.967079, success_status=true&gt;
#&lt;struct Jongleur::Task name=:Transform2, pid=22727, running=false, exit_status=0, finish_time=1546800519.273847, success_status=true&gt;
#&lt;struct Jongleur::Task name=:Load1, pid=22729, running=false, exit_status=0, finish_time=1546800533.147315, success_status=true&gt;
#&lt;struct Jongleur::Task name=:Load2, pid=22732, running=false, exit_status=0, finish_time=1546800534.1630201, success_status=true&gt;
#&lt;struct Jongleur::Task name=:Cleanup, pid=22745, running=false, exit_status=0, finish_time=1546800534.899818, success_status=true&gt;
</code></pre>
<p>Jongleur gives us a thorough account of each Task's details and success status. Notice how both initial Tasks (Extract1, Extract2) start at the same time and each new Task starts only once its predecessor on the Task Graph has finished. If a Task failed, for whatever reason, Jongleur wouldn't execute its dependent tasks but it would continue running any other tasks it could. The status of all Tasks would still be visible in the Task Matrix structure yielded by the <code>completed</code> callback.</p>
<h2 id="heading-performance">Performance</h2>
<p>Running the simple single-process ETL pipeline at the beginning of this article produced the following timings:</p>
<p><code>9.55s user 1.40s system 32% cpu 33.494 total</code></p>
<p>Running the parallel Jongleur pipelines produced:</p>
<p><code>19.99s user 5.23s system 89% cpu 27.210 total</code></p>
<p>User time is increased with Jongleur (I'd guess due to using Redis) and so is kernel time, obviously due to forking and managing many processes, but overall time is approx. 18% faster using Jongleur with two pipelines. Also consider that in this example we only used two parallel lines, i.e. we split the data load into 2 parts. It is a simple matter to split it into 4, 8 or even 16 parallel lines (dependent on our CPU cores) and thus gain a massive performance gain, using the same logic and principles demonstrated here and simply adding more Tasks.</p>
<h2 id="heading-jongleur-advantages">Jongleur advantages</h2>
<ol>
<li><p>Performance, through parallelisation.</p>
</li>
<li><p>Implicit precedence handling. Jongleur will start running a task only once its predecessor tasks have successfully finished running. If a task fails, its dependent tasks wil not be ran.</p>
</li>
<li><p>Code modularisation. If a task fails, for whatever reason, it will be marked as such by Jongleur and its dependent tasks will not be run. However, that won't stop any other tasks from being run, which means that partial failure does not necessitate complete failure, in use cases where this is desirable.</p>
</li>
</ol>
<h2 id="heading-the-complete-code">The Complete Code</h2>
<p>including data file for this demo is <a target="_blank" href="https://gitlab.com/RedFred7/jongleur_etl">freely available</a></p>
<h2 id="heading-the-future">The Future</h2>
<p>Feel free to suggest improvements and new features for Jongleur or to talk to me about trying it out for your own Use Cases :)</p>
]]></content:encoded></item><item><title><![CDATA[The Three Ghosts of FizzBuzz: A Christmas Story]]></title><description><![CDATA[FizzBuzz is a simple kids game, often used as a test at programming interviews.  It goes like this:

"Write a program that prints the numbers from 1 to 100. But for multiples of 3 print “Fizz” instead of the number and for multiples of 5 print “Buzz”...]]></description><link>https://bootstrap.me.uk/the-three-ghosts-of-fizzbuzz-a-christmas-story</link><guid isPermaLink="true">https://bootstrap.me.uk/the-three-ghosts-of-fizzbuzz-a-christmas-story</guid><category><![CDATA[Ruby]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[programming languages]]></category><category><![CDATA[FIZZBUZZ CHALLANGE]]></category><dc:creator><![CDATA[Fred Heath]]></dc:creator><pubDate>Fri, 21 Dec 2018 15:07:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1632567521251/VWI7xc37e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<p>FizzBuzz is a simple kids game, often used as a test at programming interviews.  It goes like this:</p>
<blockquote>
<p>"Write a program that prints the numbers from 1 to 100. But for multiples of 3 print “Fizz” instead of the number and for multiples of 5 print “Buzz”. For numbers which are multiples of both 3 and 5 print “FizzBuzz”.</p>
</blockquote>
<p>I fell asleep yesterday while thinking about all the different ways we can solve FizzBuzz in Ruby. Then three ghosts appeared in my dream. The first one was the Ghost of Imperative Programming (GImP, for short). It showed me how to do FizzBuzz in an imperative way:</p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">imperative</span><span class="hljs-params">(n)</span></span>
  <span class="hljs-keyword">if</span> (n % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> != <span class="hljs-number">0</span>)
    <span class="hljs-string">'Fizz'</span>
  <span class="hljs-keyword">elsif</span> (n % <span class="hljs-number">3</span> != <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>)
    <span class="hljs-string">'Buzz'</span>
  <span class="hljs-keyword">elsif</span> (n % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>)
    <span class="hljs-string">'FizzBuzz'</span>
  <span class="hljs-keyword">else</span> n.to_s
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>"Simple conditionals are easy to read", it said. "They're also relatively fast. They're a good, basic way to implement your solution"</p>
<p>Next, the Ghost of Antecedent Declarative Inference appeared (GhAnDI). "Don't listen to GImP" it said. "Be more clever. Look here, the conditions that are evaluated to produce each FizzBuzz value are mutually exclusive, if you represent them as Hash values then only a maximum of <strong>one</strong> of our Hash keys will have the value of 'true' for any given number. Use this to your advantage."</p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">declarative</span><span class="hljs-params">(n)</span></span>
  h <span class="hljs-params">||</span>= {
    <span class="hljs-string">'Fizz'</span> =&gt; (n % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> != <span class="hljs-number">0</span>),
    <span class="hljs-string">'Buzz'</span> =&gt; (n % <span class="hljs-number">3</span> != <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>),
    <span class="hljs-string">'FizzBuzz'</span> =&gt; (n % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>),
    n.to_s =&gt; (n % <span class="hljs-number">3</span> != <span class="hljs-number">0</span>) &amp;&amp; (n % <span class="hljs-number">5</span> != <span class="hljs-number">0</span>)
  }
  h.key(<span class="hljs-literal">true</span>) <span class="hljs-params">||</span> n
<span class="hljs-keyword">end</span>
</code></pre>
<p>"You then make sure you return this 'true' key (<code>h.key(true)</code>) from the method. Of course if all the keys have the 'false' value (i.e. the passed number is neither divisible by 3, nor by 5) then <code>h.key(true)</code> will evaluate to nil, so use the logical OR operator to ensure that -in this case- you return the actual passed number."</p>
<p>"That's pretty smart" I said. "We are not specifying a sequence of steps leading to the solution, but rather the conditions required and we leverage the language features to work out the correct one. Cool!"</p>
<p>As GhAnDI departed, I sensed a new presence arriving. It was wearing trendy clothes , had a well-oiled beard and there was a large and vocal group of developers following it around. It was... the Ghost of Functional Erudition (GoFEr).</p>
<p>"The other two are the past", GoFEr said. "I will show you the future"</p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">functional</span><span class="hljs-params">(n)</span></span>
  [[<span class="hljs-number">15</span>, <span class="hljs-string">'FizzBuzz'</span>], [<span class="hljs-number">5</span>, <span class="hljs-string">'Buzz'</span>], [<span class="hljs-number">3</span>, <span class="hljs-string">'Fizz'</span>]].select { <span class="hljs-params">|x|</span> (n % x[<span class="hljs-number">0</span>] == <span class="hljs-number">0</span>)}.map{<span class="hljs-params">|a|</span> a[<span class="hljs-number">1</span>] }.first <span class="hljs-params">||</span> n.to_s
<span class="hljs-keyword">end</span>
</code></pre>
<p>"We specify both conditions and outcome together" it said. "Ruby doesn't have tuples, but we can get a similar effect with an Array of Arrays. We then select the 'tuple' which satisfies the condition of zero-mod division with the tuple's divisor (<code>x[0]</code>). If more than one tuples match, we only select the first one, since we cleverly ordered our Array that way. And if there are no matches then we return the original number input. Simples."</p>
<p>And with that GoFEr shimmied away to the sounds of 'California dreaming' and I woke up. I learned some useful techniques from this encounter, but mostly I came to appreciate just how powerful and flexible the Ruby language really is.</p>
<p>Merry Christmas everyone and happy Ruby-ing!</p>
<p><em>PS</em>: As a Christmas present, the Ghosts put all their code along with some bench-marking on GitLab (https://gitlab.com/snippets/35596)</p>
]]></content:encoded></item><item><title><![CDATA[Python Slices vs Ruby blocks]]></title><description><![CDATA[A couple of my Python colleagues tried to impress me today with Python's named slices feature. The way it works is like that:
 s = list('helloworld!')
  => ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!']
WORLD = slice(5, 10)
s[WORLD]
 => ['w'...]]></description><link>https://bootstrap.me.uk/python-slices-vs-ruby-blocks</link><guid isPermaLink="true">https://bootstrap.me.uk/python-slices-vs-ruby-blocks</guid><category><![CDATA[Python]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[programming languages]]></category><category><![CDATA[Lambda Expression]]></category><dc:creator><![CDATA[Fred Heath]]></dc:creator><pubDate>Tue, 11 Sep 2018 08:40:18 GMT</pubDate><content:encoded><![CDATA[<hr />
<p>A couple of my Python colleagues tried to impress me today with Python's named slices feature. The way it works is like that:</p>
<pre><code class="lang-python"> s = list(<span class="hljs-string">'helloworld!'</span>)
  =&gt; [<span class="hljs-string">'h'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'w'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'r'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'d'</span>, <span class="hljs-string">'!'</span>]
WORLD = slice(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>)
s[WORLD]
 =&gt; [<span class="hljs-string">'w'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'r'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'d'</span>]
</code></pre>
<p>So you can have your own customised slicing mechanism that you can apply to any list. Which is kind of cool. That prompted me to demonstrate how we can do the same thing with Ruby. Luckily, in Ruby world we have blocks, procs and lambdas, the ultimate play-dough that allows us to stretch and flex in every direction.</p>
<pre><code class="lang-ruby">​s = [<span class="hljs-string">'h'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'w'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'r'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'d'</span>, <span class="hljs-string">'!'</span>]
  =&gt; [<span class="hljs-string">'h'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'w'</span>, <span class="hljs-string">'o'</span>, <span class="hljs-string">'r'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'d'</span>, <span class="hljs-string">'!'</span>]
world = proc {<span class="hljs-params">|x |</span> x.slice(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>)}
world[s]
 =&gt; [<span class="hljs-string">"w"</span>, <span class="hljs-string">"o"</span>, <span class="hljs-string">"r"</span>, <span class="hljs-string">"l"</span>, <span class="hljs-string">"d"</span>, <span class="hljs-string">"!"</span>]
</code></pre>
<p>​so we can do things like:</p>
<pre><code class="lang-ruby">first = proc { <span class="hljs-params">|x|</span> x.slice(<span class="hljs-number">0</span>) }
first[s]
=&gt; <span class="hljs-string">"h"</span>
</code></pre>
<p>or even things that we can't do with Python's named slicing, since it doesn't allow us to pass the receiver as an argument to the block (x in the example below)</p>
<pre><code class="lang-ruby">last = proc {<span class="hljs-params">|x|</span> x.slice x.length-<span class="hljs-number">1</span>, x.length}
last[<span class="hljs-string">%w(dog rabbit fox cat)</span>]
=&gt; [<span class="hljs-string">"cat"</span>]
</code></pre>
<p>or</p>
<pre><code class="lang-ruby">median = proc {<span class="hljs-params">|x|</span> x.slice(x.length / <span class="hljs-number">2</span>) }
median[<span class="hljs-string">%w(dog rabbit cat)</span>]
=&gt; <span class="hljs-string">"rabbit"</span>
</code></pre>
<p>and of course we're not just restricted to slicing arrays,</p>
<pre><code class="lang-ruby">domain_extractor = proc { <span class="hljs-params">|x|</span> x.gsub(<span class="hljs-regexp">/.+@([^.]+).+/</span>, <span class="hljs-string">'\1'</span>) }
domain_extractor[<span class="hljs-string">"fred@mydomain.co.uk"</span>]
=&gt; <span class="hljs-string">"mydomain"</span>
</code></pre>
<p>and since a block is just an anonymous Proc object, we can use it with any method that accepts Proc parameters ​</p>
<pre><code class="lang-ruby">email_list = [<span class="hljs-string">"fred@mydomain.com"</span>, <span class="hljs-string">"john@gmail.com"</span>, <span class="hljs-string">"mary@yahoo.co.uk"</span>]
=&gt; [<span class="hljs-string">"fred@mydomain.com"</span>, <span class="hljs-string">"john@gmail.com"</span>, <span class="hljs-string">"mary@yahoo.co.uk"</span>]
email_list.map(&amp;domain_extractor)
=&gt; [<span class="hljs-string">"mydomain"</span>, <span class="hljs-string">"gmail"</span>, <span class="hljs-string">"yahoo"</span>]
</code></pre>
<p>Blocks and Procs (a.k.a lambdas) are ideal for some quick, reusable functionality or for when defining a full-blown method would be too much, but also for more serious uses such as callbacks and deferred execution. IMHO, they are a fundamental part of what makes Ruby such a flexible and powerful language. Learn them, use them, enjoy them :)</p>
]]></content:encoded></item><item><title><![CDATA[The top 5 programming books of all time]]></title><description><![CDATA[title: The top 5 programming books of all time
published: true
description: The best programming books of all time
tags: book, programming, software 
What makes a programming book truly great? IMHO, two things:

Content quality
Content delivery

Ther...]]></description><link>https://bootstrap.me.uk/the-top-5-programming-books-of-all-time</link><guid isPermaLink="true">https://bootstrap.me.uk/the-top-5-programming-books-of-all-time</guid><category><![CDATA[programming languages]]></category><category><![CDATA[books]]></category><dc:creator><![CDATA[Fred Heath]]></dc:creator><pubDate>Thu, 16 Aug 2018 07:13:26 GMT</pubDate><content:encoded><![CDATA[<hr />
<p>title: The top 5 programming books of all time
published: true
description: The best programming books of all time</p>
<h2 id="heading-tags-book-programming-software">tags: book, programming, software </h2>
<p>What makes a programming book truly great? IMHO, two things:</p>
<ol>
<li>Content quality</li>
<li>Content delivery</li>
</ol>
<p>There are many clever people out there who have a lot of knowledge about some very innovative, complex and useful subjects. But very few of those people can impart this knowledge in writing, in a way that it can be easily understood and assimilated. Writing style and expressiveness make all the difference between knowing something and sharing it in writing. With that in mind, these are my top 5 programming books (in no particular order):</p>
<hr />
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571227491/pQDb0cnDm.jpeg" alt="the C programming language" /></p>
<p> The book that taught C to generations of programmers. This is a no-nonsense book, terse and concise but tells you everything you need to know about C. Some people will argue that in this day and age you don't need to know C to be a programmer. True, but you don't learn C to be a programmer, you learn it to be a <em>better</em> programmer. Knowing about memory allocation, how pointers work, what an L-value is...all this stuff gives you much better insight into your own programming language of choice. In the words of a colleague "you can't really program until you know how strcmp and strcpy work".
<br /></p>
</li>
</ul>
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571228948/tV9NZahEY.jpeg" alt="Thinking in C++" /></p>
<p>  Until this came out, most C++ (and other programming) books were simple knowledge-dumps of their authors' undoubted expertise. Bruce Eckel took a very complex subject (C++), broke it down into fundamental principles and explained each one in an informal, conversational way that made it comprehensible even to readers like me who didn't have the vast intellect of Stroustrup or Scott Meyer. C++ for your ordinary Joe Programmer!
<br /></p>
</li>
</ul>
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571230882/bc4AjM0z2.jpeg" alt="Design Patterns in Ruby" /></p>
<p>  This book manages to give the reader a great, working understanding of design patterns while introducing and showcasing the Ruby programming language. Unlike the GoF design pattern book this one is written in a conversational and incremental fashion, offering relevant, real-world examples of how and when to apply design patterns and also provides a great perspective on how patterns are but a product of the constraints of current frameworks and languages. A must-read on design patterns, regardless of your programming language.</p>
</li>
</ul>
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571232371/QfCxdgdCJ.jpeg" alt="JavaScript: the Good Parts" /></p>
<p>  This book takes a language notorious for its inconsistencies, ugly syntax and design weaknesses, strips away all that and presents you with an elegant, expressive and effective language. What more can I say?</p>
</li>
</ul>
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571233984/A00qKkWJj.jpeg" alt="Refactoring" /></p>
<p>  When this book was first published, terms like 'refactoring', 'code-smells' and 'TDD' lived on the edge of the software community, practiced and understood by a few eccentrics. This book explained why we should take notice and how to apply these concepts in our everyday programming. A thought-process-changing book indeed. </p>
</li>
</ul>
<hr />
<p>There are other books I could have added but I have to draw the line somewhere and this is it. Let me know your top 5 programming books.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering the Proxy Pattern: A Detailed Overview]]></title><description><![CDATA[Intro
The Proxy is one of the most popular design patterns in most programming languages. A Proxy is simply an object that sits between some client code and a service object. The client code deals directly with the Proxy, instead of the service objec...]]></description><link>https://bootstrap.me.uk/mastering-the-proxy-pattern-a-detailed-overview</link><guid isPermaLink="true">https://bootstrap.me.uk/mastering-the-proxy-pattern-a-detailed-overview</guid><dc:creator><![CDATA[Fred Heath]]></dc:creator><pubDate>Sun, 25 Feb 2018 23:49:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726437826241/cf4dfdc2-3e20-45f7-b0b8-059626ec5018.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<h2 id="heading-intro">Intro</h2>
<p>The Proxy is one of the most popular design patterns in most programming languages. A Proxy is simply an object that sits between some client code and a service object. The client code deals directly with the Proxy, instead of the service object. <a class="post-section-overview" href="#serviceobject"><sup>1</sup></a> This means that the Proxy can be used to mask the service object's physical location (a <em>Remote</em> proxy), manage access to the service object (a <em>Security</em> proxy), or just lazily initialize the service object (a <em>Virtual</em> proxy).</p>
<h2 id="heading-the-old-way">The old way</h2>
<p>Whatever the use-case, the idiomatic Ruby way to implement a proxy has been by utilizing the <code>method_missing</code> method, as described in Russ Olsen's seminal book <a target="_blank" href="http://designpatternsinruby.com/">Design Patterns in Ruby</a>.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># the service object</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Account</span></span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(balance)</span></span>
    @balance = balance
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deposit</span><span class="hljs-params">(amount)</span></span>
    @balance += amount
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(amount)</span></span>
    @balance -= amount
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">self</span>.<span class="hljs-title">interest_rate_for</span><span class="hljs-params">(a_balance)</span></span>
    a_balance &gt; <span class="hljs-number">10_000</span> ? <span class="hljs-string">'3.2%'</span> : <span class="hljs-string">'5.5%'</span>
  <span class="hljs-keyword">end</span>

<span class="hljs-keyword">end</span>

<span class="hljs-comment"># the proxy object</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AccountProxy</span></span>
  <span class="hljs-keyword">require</span> <span class="hljs-string">'etc'</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(real_account)</span></span>
    @real_account = real_account
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">method_missing</span><span class="hljs-params">(name, *args)</span></span>
    raise <span class="hljs-string">"Unauthorised access!"</span> <span class="hljs-keyword">unless</span> Etc.getlogin == <span class="hljs-string">'fred'</span>
    @real_account.send(name, *args)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-comment">## client code</span>
acc = Account.new(<span class="hljs-number">100</span>)
proxy = AccountProxy.new(acc)
proxy.deposit(<span class="hljs-number">10</span>)
proxy.withdraw(<span class="hljs-number">50</span>)
</code></pre>
<p>In this simple proxy example, we intercept all method calls to <code>AccountProxy</code>, do a basic security check and then delegate the method to the real <code>Account</code> object to do the actual work.</p>
<p>This is a concise and effective way to create a proxy in Ruby, but it has some weaknesses:</p>
<ol>
<li><p>The client code needs to call a separate Object, e.g. AccountProxy instead of Account. It's pretty obvious we're not dealing with the 'real' service object, which is not only a nuisance if you have to re-write existing client code, but may also lead malicious actors to try to by pass the proxy.</p>
</li>
<li><p><code>method_missing</code> is a catch-all trap. It will catch every method call coming our way and we need to think long and hard about which methods we want to delegate to the 'real' object and which ones to handle in our proxy (<code>#to_s</code>, for instance)</p>
</li>
<li><p>Using <code>method_missing</code> has a performance hit. When calling a method on an object, the Ruby interpreter will first look all the way up the object hierarchy trying to find the method and -when it can't- will go back down the hierarchy tree and start looking for a <code>method_missing</code> implementation. This will happen for every single method call.</p>
</li>
<li><p>Our proxy doesn't work with class methods. To do that we'd need a separate proxy object for <code>Account</code>'s singleton class.</p>
</li>
</ol>
<h2 id="heading-the-new-way">The new way</h2>
<p>A few years after Russ's book came out, Ruby 2.0.0 was released and introduced <a target="_blank" href="http://ruby-doc.org/core-2.2.0/Module.html#method-i-prepend_features">Module#prepend</a>. The way <code>Module#prepend</code> works is by inserting the prepended Module between the calling code (a.k.a the 'receiver' object) and the module or class that does the prepending.</p>
<p>Can you see the connection already? The prepended Module <em>sits between the receiver and a service object</em>. Sounds familiar? Oh yes, this is exactly what a Proxy is meant to be doing!</p>
<p>Knowing this we can use the <code>#prepended</code> hook method to dynamically implement all methods of the prepending module in our proxy, making sure to add our extra security check, before calling the original method implementation.</p>
<pre><code class="lang-ruby">
<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Proxy</span></span>
  <span class="hljs-keyword">require</span> <span class="hljs-string">'etc'</span>

  <span class="hljs-comment"># Dynamically re-creates receiver's class methods, intercepts calls </span>
  <span class="hljs-comment"># to them and checks user before invoking parent code</span>
  <span class="hljs-comment">#</span>
  <span class="hljs-comment"># <span class="hljs-doctag">@param</span> receiver the class or module which has prepended this module</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">self</span>.<span class="hljs-title">prepended</span><span class="hljs-params">(receiver)</span></span>
    obj_mthds = receiver.instance_methods - receiver.superclass.instance_methods
    obj_mthds.each <span class="hljs-keyword">do</span> <span class="hljs-params">|m|</span>
      args = receiver.instance_method(m).parameters <span class="hljs-comment"># =&gt; [[:req, :x], [:req, :y]]</span>
      args.map! {<span class="hljs-params">|x|</span> x[<span class="hljs-number">1</span>].to_s}
      Proxy.class_eval <span class="hljs-keyword">do</span>
        define_method(m) <span class="hljs-keyword">do</span> <span class="hljs-params">|*args|</span>
          puts <span class="hljs-string">"*** intercepting method: <span class="hljs-subst">#{m}</span>, args: <span class="hljs-subst">#{args}</span>"</span>
          raise <span class="hljs-string">"Unauthorised access!"</span> <span class="hljs-keyword">unless</span> Etc.getlogin == <span class="hljs-string">'fred'</span>
          <span class="hljs-keyword">super</span>(*args)
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>

<span class="hljs-keyword">end</span> <span class="hljs-comment">#module</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Account</span></span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(balance)</span></span>
    @balance = balance
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deposit</span><span class="hljs-params">(amount)</span></span>
    @balance += amount
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(amount)</span></span>
    @balance -= amount
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">self</span>.<span class="hljs-title">interest_rate_for</span><span class="hljs-params">(a_balance)</span></span>
    a_balance &gt; <span class="hljs-number">10_000</span> ? <span class="hljs-string">'3.2%'</span> : <span class="hljs-string">'5.5%'</span>
  <span class="hljs-keyword">end</span>

  prepend Proxy

<span class="hljs-keyword">end</span>
</code></pre>
<p>All we're doing is taking advantage of the way <code>#prepend</code> affects the Ruby Object Model in order to find out which methods the intercepted object is defining and then implementing them in our proxy while adding our own code. To call the original implementation, we once again leverage the fact that the intercepted object is the parent of our proxy module, so all we need to do is call <code>#super</code> (no more ugly <code>#send</code> calls)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571239564/vz9dTo8wr.png" alt="prepending_proxy_to_class" /></p>
<p>Let's write some client code to exercise our account:</p>
<pre><code class="lang-ruby">acc = Account.new(<span class="hljs-number">100</span>)

puts acc.deposit(<span class="hljs-number">10</span>)
puts acc.withdraw(<span class="hljs-number">50</span>)
puts Account.interest_rate_for(<span class="hljs-number">2000</span>)
</code></pre>
<p>which outputs:</p>
<pre><code class="lang-shell">*** intercepting method: deposit, args: [10]
110
*** intercepting method: withdraw, args: [50]
60
5.5%
</code></pre>
<p>That's pretty cool, but the beauty of it doesn't end here. We can use the very same mechanism to intercept class method calls. All we need to do is prepend the Proxy to the Account's singleton class:</p>
<pre><code class="lang-ruby">

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Account</span></span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(balance)</span></span>
    @balance = balance
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deposit</span><span class="hljs-params">(amount)</span></span>
    @balance += amount
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(amount)</span></span>
    @balance -= amount
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">self</span>.<span class="hljs-title">interest_rate_for</span><span class="hljs-params">(a_balance)</span></span>
    a_balance &gt; <span class="hljs-number">10_000</span> ? <span class="hljs-string">'3.2%'</span> : <span class="hljs-string">'5.5%'</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-class"><span class="hljs-keyword">class</span> &lt;&lt; self</span>
    prepend Proxy
  <span class="hljs-keyword">end</span>

  prepend Proxy

<span class="hljs-keyword">end</span>
</code></pre>
<p>Once again, the Object Model is manipulated to our favour:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632571240986/6iFheDs31.png" alt="prepending_proxy_to_singleton_class" /></p>
<p>And now by running our client code again, we see that both instance and class methods of the Account class are caught by the proxy:</p>
<pre><code class="lang-ruby">acc = Account.new(<span class="hljs-number">100</span>)

puts acc.deposit(<span class="hljs-number">10</span>)
puts acc.withdraw(<span class="hljs-number">50</span>)
puts Account.interest_rate_for(<span class="hljs-number">2000</span>)
</code></pre>
<p>which outputs:</p>
<pre><code class="lang-shell">*** intercepting method: deposit, args: [10]
110
*** intercepting method: withdraw, args: [50]
60
*** intercepting method: interest_rate_for, args: [2000]
5.5%
</code></pre>
<p>This way of writing proxies has a number of advantages:</p>
<ol>
<li><p>It's transparent. Client code doesn't need to change, it doesn't even need to know that a proxy is being used, we just need to prepend the proxy module to the service object class.</p>
</li>
<li><p>It's performant. Some basic bench-marking has indicated a 3-4x performance gain against a method_missing proxy.</p>
</li>
<li><p>It works for both instance and class methods.</p>
</li>
</ol>
<p>So there you have it: Another way of building proxies. Ruby keeps evolving and so do we. Happy 25th birthday!</p>
<p>1: a Ruby object that encapsulates some business logic</p>
]]></content:encoded></item></channel></rss>