<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.7.3">Jekyll</generator><link href="https://petermellett.co.uk/feed.xml" rel="self" type="application/atom+xml" /><link href="https://petermellett.co.uk/" rel="alternate" type="text/html" /><updated>2020-08-10T12:40:20+00:00</updated><id>https://petermellett.co.uk/</id><title type="html">Peter Mellett</title><subtitle>About me! A professional software engineer living in Cornwall, UK.</subtitle><entry><title type="html">Cryptocurrency Exchange Website</title><link href="https://petermellett.co.uk/projects/cryptocurrency-exchange" rel="alternate" type="text/html" title="Cryptocurrency Exchange Website" /><published>2019-09-04T00:00:00+00:00</published><updated>2019-09-04T00:00:00+00:00</updated><id>https://petermellett.co.uk/projects/cryptocurrency-exchange</id><content type="html" xml:base="https://petermellett.co.uk/projects/cryptocurrency-exchange">&lt;h1 id=&quot;cryptocurrency-exchange-website&quot;&gt;Cryptocurrency Exchange Website&lt;/h1&gt;

&lt;p&gt;My client was approached to facilitate a rebrand and update the website for a cryptocurrency exchange. Their current focus is on professional clients. I was brought in to implement the new design with an emphasis on motion and transition while adhering to low carbon emission and responsible web development principles.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/kBUfvkbFIoE&quot;&gt;Photo by Tomasz Frankowski on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;

&lt;p&gt;Being a cryptocurrency exchange, security was of utmost concern to the client. They placed restrictions on the technology we were able to use and the approach we should take. This aligned with the internal team’s ability to review and audit the codebase along with use of standard metrics and error capturing tools.&lt;/p&gt;

&lt;p&gt;To avoid introducing dependencies on too much third party code we avoided libraries where possible, instead choosing to implement lightweight alternatives which were easier to audit and maintain.&lt;/p&gt;

&lt;p&gt;In order to provide a top quality and fast service from the site we elected to use a static site, only small amounts of the site are dynamically populated and have established APIs around them.&lt;/p&gt;

&lt;p&gt;Node was expressed as a preference in early planning meetings, so it was the clear choice for rendering and delivering the static pages.&lt;/p&gt;

&lt;h2 id=&quot;methodology&quot;&gt;Methodology&lt;/h2&gt;

&lt;p&gt;Because the site was designed to be composed of modular blocks, I chose to use React with Serverside Rendering (SSR). This meant that if in future we wished for a fully interactive site then hydrating the application on the client side would be possible and not require rebuilding all the application code as a duplicate.&lt;/p&gt;

&lt;p&gt;Initially any custsom JavaScript behaviour in the browser was written in plain JavaScript and injected into the page as a bundle. These scripts remained reasonably small as CSS transitions and animations were used to take advantage of hardware acceleration and the reduced CPU load per visitor.&lt;/p&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;n.b. photography and text have been replaced with generic alternatives here, so please excuse any poor image selection!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The site contains some novel features using CSS animation and interactivity without drowning out the content.&lt;/p&gt;

&lt;p&gt;Here is a small selection of some elements from the site.&lt;/p&gt;

&lt;p&gt;A CSS-animated carousel with JavaScript triggers on the navigation arrows and blobs. It uses an overflowed container to allow the elements to be moved using a transition and &lt;code class=&quot;highlighter-rouge&quot;&gt;translateX&lt;/code&gt; which is calculated from the available slides. The same system is used for the text.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/projects/carousel-screenshot.png&quot; alt=&quot;Carousel powered by CSS animations with a simple JavaScript trigger to switch classes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/Cecb0_8Hx-o&quot;&gt;Photo by Duy Pham on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A “meet the team” section, designed to be fully responsive and accept any number of team members. Each has a personalised bio revealed on click or tap, with a hover effect to remove the blue hue.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/projects/team.png&quot; alt=&quot;Team element with responsive grid&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://randomuser.me/&quot;&gt;Images from https://randomuser.me/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is all very nice but doesn’t reveal the motion of the elements, here is a screen recording of the create your account section. It’s a series of animations tied together by a delay so they can all be triggered by adding a single class to the component.&lt;/p&gt;

&lt;p&gt;Please excuse the autoplay video, the gif was too low framerate to show it off.&lt;/p&gt;

&lt;video autoplay=&quot;&quot; muted=&quot;&quot; loop=&quot;&quot; src=&quot;/assets/images/projects/create-account.webm&quot;&gt;&lt;/video&gt;

&lt;p&gt;The project has certainly been a learning experience, CSS animations were something I used to avoid but they are very accessible and with a bit of thought behind them can have a big impact.&lt;/p&gt;</content><author><name>pete</name></author><category term="project" /><summary type="html">Cryptocurrency Exchange Website</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/projects/tomasz-frankowski-kBUfvkbFIoE-unsplash.jpg" /></entry><entry><title type="html">Delayed indexing with background_job and searchkick</title><link href="https://petermellett.co.uk/blog/searchkick-metadata/" rel="alternate" type="text/html" title="Delayed indexing with background_job and searchkick" /><published>2018-10-15T00:00:00+00:00</published><updated>2018-10-15T00:00:00+00:00</updated><id>https://petermellett.co.uk/blog/searchkick-metadata</id><content type="html" xml:base="https://petermellett.co.uk/blog/searchkick-metadata/">&lt;p&gt;&lt;a href=&quot;https://photos.icons8.com/&quot;&gt;Photo by Icons8 team&lt;/a&gt; via &lt;a href=&quot;https://unsplash.com/photos/dhZtNlvNE8M&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;A client had problems with their search indexes being out of date compared to the latest updates to their content. Due to the distributed nature of the app a blocking solution provided a less-than optimal user experience so a more appropriate solution was required.&lt;/p&gt;

&lt;h2 id=&quot;the-situation&quot;&gt;The situation&lt;/h2&gt;

&lt;p&gt;The software comprises of three applications:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A &lt;a href=&quot;https://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt; JSON-speaking API&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; front-end for consuming the data as a user&lt;/li&gt;
  &lt;li&gt;A second React app for backoffice tasks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The application is search-first in design so it’s important that data is findable and accurate in the search results. With the existing implementation, reindexing may not occur until a server restart which could be some time.&lt;/p&gt;

&lt;p&gt;The search functionality is backed by &lt;a href=&quot;https://www.elastic.co/products/elasticsearch&quot;&gt;Elasticsearch&lt;/a&gt; and interfaced using the &lt;a href=&quot;https://github.com/ankane/searchkick&quot;&gt;searchkick&lt;/a&gt; gem.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;/h2&gt;

&lt;p&gt;I chose &lt;code class=&quot;highlighter-rouge&quot;&gt;delayed_job&lt;/code&gt; as my queue manager, owing to the fact that this is a commercial project and &lt;code class=&quot;highlighter-rouge&quot;&gt;delayed_job&lt;/code&gt; does not place any restrictions on use with the MIT license.&lt;/p&gt;

&lt;p&gt;Following their readme, it was easy to fit to the specifics of the application. Since the application is using mongoid I needed specific support for this, which what provided with the &lt;code class=&quot;highlighter-rouge&quot;&gt;delayed_job_mongoid&lt;/code&gt; gem.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'delayed_job_mongoid'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because the application is using Rails 4, it is necessary to specify the queue adapter in &lt;code class=&quot;highlighter-rouge&quot;&gt;application.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# config/application.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active_job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queue_adapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:delayed_job&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;delayed_job&lt;/code&gt; needs a redis instance to manage its queues so I provided one on the default ports.&lt;/p&gt;

&lt;h4 id=&quot;queueing-jobs&quot;&gt;Queueing jobs&lt;/h4&gt;

&lt;p&gt;Now it is easy to delay any existing method by calling it through the &lt;code class=&quot;highlighter-rouge&quot;&gt;delay&lt;/code&gt; method, like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# old&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reindex&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# new&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reindex&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The real data models in question have some &lt;code class=&quot;highlighter-rouge&quot;&gt;embedded&lt;/code&gt; models which needed to also be reindexed, as their data is provided to searchkick to represent one entity in the elasticsearch index.&lt;/p&gt;

&lt;p&gt;In order to accommodate this and to automatically queue a reindex on save for the model, add an &lt;code class=&quot;highlighter-rouge&quot;&gt;after_save&lt;/code&gt; hook with the reindex action.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# models/article.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;after_save&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reindex&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# and any other relations that are embedded!&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# If you're using active record then ymmv&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now whenever our model is saved or updated, it’ll automatically generate a job to reindex the model. Neat!&lt;/p&gt;

&lt;h4 id=&quot;consuming-the-queue&quot;&gt;Consuming the queue&lt;/h4&gt;

&lt;p&gt;To recap so far there is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A method to queue our long-running jobs&lt;/li&gt;
  &lt;li&gt;A datastore to back our job queue&lt;/li&gt;
  &lt;li&gt;Integration with mongoid and the &lt;code class=&quot;highlighter-rouge&quot;&gt;after_save&lt;/code&gt; callback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next thing is for us to actually execute the queued jobs.&lt;/p&gt;

&lt;p&gt;In local development this is pretty straightforward. Run our rails app again but use the provided rake task to wait and listen for our jobs and take the necessary action.&lt;/p&gt;

&lt;p&gt;In the simplest form this can be done with&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;bundle&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rake&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:work&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The deployment environment understands &lt;code class=&quot;highlighter-rouge&quot;&gt;Procfile&lt;/code&gt; so add a &lt;code class=&quot;highlighter-rouge&quot;&gt;queue&lt;/code&gt; process to that and ensure it’s running as part of the deployment monitoring process.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Procfile&lt;/span&gt;
web: bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;puma &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; config/puma.rb
queue: bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rake &lt;span class=&quot;nb&quot;&gt;jobs&lt;/span&gt;:work
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;monitoring-queue-health&quot;&gt;Monitoring queue health&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;rake jobs:work&lt;/code&gt; task is fine, but not very chatty so it can be hard during development to ensure that jobs aren’t being missed or some other problem is occurring now that we’re relying on a separate worker process to do some of our work.&lt;/p&gt;

&lt;p&gt;To ensure our queue is being consumed correctly I took a couple of steps.&lt;/p&gt;

&lt;p&gt;Firstly in the API application I provided an endpoint to poll for the queue status. This simply returns the count of jobs in the &lt;code class=&quot;highlighter-rouge&quot;&gt;delayed_job&lt;/code&gt; queue.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;queue_status&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;in_queue: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Delayed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;json: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;status: :ok&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The admin application can then poll this endpoint and report on the state of the queue to the administrator. It should be obvious if the number is high, really anything above zero for more than a few seconds, or rising that something is not right with the queue.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/index-status.png&quot; alt=&quot;Screenshot of indexing status&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Secondly for local and staging purposes including the &lt;code class=&quot;highlighter-rouge&quot;&gt;delayed_job_web&lt;/code&gt; gem mounts a small sinatra application which provides stats on queue length, pending, processing and failed jobs.&lt;/p&gt;

&lt;p&gt;It doesn’t go into huge detail but it’s a massive boost to confidence and being able to test out tasks and actions through the rails console with visual feedback.&lt;/p&gt;

&lt;p&gt;If you’re using mongoid like I am then there’s an &lt;a href=&quot;https://github.com/ejschmitt/delayed_job_web/pull/108&quot;&gt;open pull request with mongoid support&lt;/a&gt; and you can use the proposed fork in your gemfile by targeting https://github.com/brunoporto/delayed_job_web&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;delayed_job_web&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;github: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'brunoporto/delayed_job_web'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ejschmitt/delayed_job_web#quick-start-for-rails-3-and-4-applications&quot;&gt;Delayed job web quickstart&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;All in all this has increased confidence in the search indexing with visual feeback to the administrator and ensured that updates do not tie up the web server unnecessarily.&lt;/p&gt;

&lt;p&gt;If you have a Ruby on Rails application with tricky bugs or unexpected requirements, why not &lt;a href=&quot;/contact&quot;&gt;get in touch&lt;/a&gt;?&lt;/p&gt;</content><author><name>pete</name></author><category term="blog" /><category term="Freelance" /><category term="Ruby on Rails" /><category term="Elasticsearch" /><summary type="html">Photo by Icons8 team via Unsplash</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/icons8-team-643498-unsplash.jpg" /></entry><entry><title type="html">Work Experience: Fun and Games</title><link href="https://petermellett.co.uk/blog/work-experience-fun-and-games/" rel="alternate" type="text/html" title="Work Experience: Fun and Games" /><published>2018-07-20T00:00:00+00:00</published><updated>2018-07-20T00:00:00+00:00</updated><id>https://petermellett.co.uk/blog/work-experience-fun-and-games</id><content type="html" xml:base="https://petermellett.co.uk/blog/work-experience-fun-and-games/">&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/QMDap1TAu0g&quot;&gt;Photo by Patrick Tomasso on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the last two weeks of school term, I took on Joe for some work experience. As punishment I made him help me write this blog post.&lt;/p&gt;

&lt;p&gt;We decided to take on a project related to our shared interest of cycling and, hopefully, produce something fun for our cycling club to use. Joe’s words are in italics.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Our initial idea was to create something to use for the &lt;a href=&quot;https://wadebridgecoasters.co.uk&quot;&gt;Wadebridge Coasters cycling club&lt;/a&gt; (as we are both passionate about cycling) and also use the strava API to create a website that will add even more fun into the club.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I proposed the idea to create an automated version of the club’s “challenges” page, which uses popular local Strava segments and has top-3 leaderboards for each. Joe agreed to the project so we set about planning how it would work.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;We came about the idea of making a challenges page that shows the rankings of people in the club up climbs and loops around the local area. These challenges update daily and we hoped would bring a fun ‘get up and go ride’ addition to the club.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/planning.jpg&quot; alt=&quot;The notebook of planning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Over the two weeks the notebook would take a bit of a battering. Everything from planning, to designs, to “to do” lists and problems encountered. We made a habit of writing down as much as we could to help keep momentum during development.&lt;/p&gt;

&lt;p&gt;Firstly Joe was tasked with implementing the HTML for the pages we designed. Using Bootstrap provided a good foundation and allowed Joe to lay out all the pages before we needed to worry about sourcing any data. It also helped to guide the design as we progressed.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I firstly got the layout of the pages and then added javascript and css to style these and get all of the features to work with help in the trickier more advanced coding.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once this step was completed we used a template-first approach to creating our backend application. Firstly identifying the dynamic data in our design, checking it against Strava’s API documentation.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;One of the main problems we came across was having personal profiles, this idea was made redundant as we did not have all of the access that we wanted and could not get a lot of data.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Strava have recently reduced the available data from their JSON APIs so we were unable to implement some features, such as linking from the leaderboards to club member’s profiles, or building our own profile pages with compiled ‘best results’ built from the challenge leaderboards.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;This aside we worked hard to achieve all of our goals set out at the start and it turned out to work fully and I am very happy with the final result.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Very refreshing to create someting from scratch of our own volition and release it within two weeks. It’s a shame we had to cut some features but we adapted to the roadblocks well and ended up with an app to be proud of.&lt;/p&gt;

&lt;p&gt;I’m sure there will be further development on the project and we’ll dust off the feature wishlist in future.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If I did it again things I would not change much as I really enjoyed it! But if I had to change something it would  probably be making more notes on paper of what I wanted to do before starting the task as I started doing this before the end and it was very beneficial instead of spamming stuff into the code until it worked.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We spent some time on the final day putting together a retrospective of the project as an example of working in an agile fashion, although there is no ‘next sprint’ we went through the process of understanding where the strengths and weaknesses of our team are and where we would take the project next.&lt;/p&gt;

&lt;p&gt;It’s been a very enjoyble two weeks and great to see Joe’s progress even in such a short time. Finally, some parting words from Joe:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;From this experience I have learnt a lot. The main thing I will take from this is the skills of some more advanced programming features. And also how to manage time efficiently. But I have also learnt other important things like working in an office environment and how different jobs work.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I have definitely taken a lot from this and I have a clearer picture of what I want to do when I leave school and go to college/university. I would like to thank Pete for taking me on and giving me a very intuitive and hands on experience and providing a genuine work experience&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can see our challenges application at &lt;a href=&quot;https://club-leaderboard.herokuapp.com&quot;&gt;https://club-leaderboard.herokuapp.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/club-leaderboard.png&quot; alt=&quot;Screenshot of our club leaderboard application&quot; /&gt;&lt;/p&gt;</content><author><name>pete</name></author><category term="blog" /><category term="Freelance" /><category term="Strava" /><summary type="html">Photo by Patrick Tomasso on Unsplash</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/patrick-tomasso-208114-unsplash.jpg" /></entry><entry><title type="html">My First Month</title><link href="https://petermellett.co.uk/blog/my-first-month/" rel="alternate" type="text/html" title="My First Month" /><published>2018-05-03T00:00:00+00:00</published><updated>2018-05-03T00:00:00+00:00</updated><id>https://petermellett.co.uk/blog/my-first-month</id><content type="html" xml:base="https://petermellett.co.uk/blog/my-first-month/">&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/eZIzlTVgqNU&quot;&gt;Photo by Nathan Dumlao on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve put my first invoices out. What a relief! It’s been a whirlwind few weeks so I thought I’d write about it a bit.&lt;/p&gt;

&lt;p&gt;My previous employer, &lt;a href=&quot;https://insidefoundry.co.uk&quot;&gt;Foundry&lt;/a&gt;, unfortunately had to close doors last month. We had an amicable end to the business and made the mature decision as a team to do so. Still, that left the five of us at a loose end with not much time to do something about it!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/business-card.jpg&quot; alt=&quot;My business card&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I decided that the next step in my career should be to go freelance and see if I can use all the lessons learned over the past 6 years to manage my own workload and have a bit more flexibility in my life.&lt;/p&gt;

&lt;h3 id=&quot;being-the-boss&quot;&gt;Being the boss&lt;/h3&gt;

&lt;p&gt;Now that I’m unemployed - Uh oh! I’m in charge and also responsible for 100% of my output. There’s a certain stress that comes from not having a team to work with if you have a bad day or need to flex your availability a little bit.&lt;/p&gt;

&lt;p&gt;On the other side I get to pick how I fill my schedule, so it’s less a feeling of ‘you must do x today’ and more a feeling of ‘I have decided to do x today’, which gives a better feeling of control over my day-to-day.&lt;/p&gt;

&lt;p&gt;Selling myself is a new skillset I’m having to learn on the fly. It’s a strange feeling to be your own salesman- it feels a bit like a conflict of interest.&lt;/p&gt;

&lt;p&gt;I’ve also been able to pivot to selling my skills in more of a specialist role, targeting technologies that I prefer to work with while using the foundation of knowledge I’ve built over my career so far.&lt;/p&gt;

&lt;h3 id=&quot;worklife-balance&quot;&gt;Work/life balance&lt;/h3&gt;

&lt;p&gt;I do all this to provide a good home for my wife and boys. Making sure to shut the laptop after a good day’s work is important.&lt;/p&gt;

&lt;p&gt;Trying to establish the new normal with a routine that gets me out of the house every day is tricky too - I’ve got the option of working from a few different locations so I can mix up my week and hide from the teething baby.&lt;/p&gt;

&lt;p&gt;An unfortunate side-effect is that cycling has taken a hit. Without my one-hour a day commute it’s been hard to keep the miles up. This is something I hope will settle down as my routine comes together and I can get back to keeping myself fit.&lt;/p&gt;

&lt;h3 id=&quot;a-good-start&quot;&gt;A good start&lt;/h3&gt;

&lt;p&gt;Through panicking, more than a bit of luck, some great friends, and sending buckets of emails I’ve been lucky enough to have a strong start.&lt;/p&gt;

&lt;p&gt;I was up against the deadline that our meagre savings could provide while I established myself. There’s no safety net and guaranteed salary anymore.&lt;/p&gt;

&lt;p&gt;Allowing myself more freedom to attend social events in tech locally, reach out to other freelancers locally and generally putting myself out there has given me a lot of confidence - and helped me make new friends!&lt;/p&gt;

&lt;p&gt;It would have been all too easy to hide away and work without the social interaction, so I’m glad I didn’t go down that route!&lt;/p&gt;

&lt;h3 id=&quot;overall&quot;&gt;Overall&lt;/h3&gt;

&lt;p&gt;So far, so good. The stress levels are coming down as I get myself established and build a good working relationship with my clients.&lt;/p&gt;

&lt;p&gt;Thanks to those of you that have listened to me panic for all different reasons &amp;amp; my clients that have shown faith in my abilities - despite a slim portfolio initially!&lt;/p&gt;

&lt;p&gt;If you’ve read this far then why not &lt;a href=&quot;mailto:web@petermellett.co.uk&quot;&gt;get in touch&lt;/a&gt; and tell me about your experience? I’m still finding my rhythm and any tips you may have are very much appreciated.&lt;/p&gt;</content><author><name>pete</name></author><category term="blog" /><category term="Freelance" /><summary type="html">Photo by Nathan Dumlao on Unsplash</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/nathan-dumlao-608723-unsplash.jpg" /></entry><entry><title type="html">Injectable Angular</title><link href="https://petermellett.co.uk/blog/an-injectable-angular-app/" rel="alternate" type="text/html" title="Injectable Angular" /><published>2018-04-16T00:00:00+00:00</published><updated>2018-04-16T00:00:00+00:00</updated><id>https://petermellett.co.uk/blog/an-injectable-angular-app</id><content type="html" xml:base="https://petermellett.co.uk/blog/an-injectable-angular-app/">&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/BW0vK-FA3eg&quot;&gt;Photo by Clint Adair on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a recent project I needed to be able to deliver an angular application as a stand-alone widget that could be dropped in to an existing website.&lt;/p&gt;

&lt;p&gt;The danger here is that the third party website could be using any number of frameworks, including Angular, that may interfere with the bootstrapping of the code.&lt;/p&gt;

&lt;p&gt;In this particular example the third party was using requirejs in their front-end code and the angular libraries are all module-aware.&lt;/p&gt;

&lt;p&gt;This caused the application to attempt to load itself through require when in fact it did not need to do so. The application code was not expecting this and anything that would normally be bound to window - &lt;code class=&quot;highlighter-rouge&quot;&gt;angular&lt;/code&gt; in particular - was not present at runtime.&lt;/p&gt;

&lt;p&gt;This caused some headache until a solution presented itself.&lt;/p&gt;

&lt;p&gt;Wrapping the application in an &lt;code class=&quot;highlighter-rouge&quot;&gt;iife&lt;/code&gt; to give it its own scope while redefining &lt;code class=&quot;highlighter-rouge&quot;&gt;define&lt;/code&gt; so that for the parse and execute time of the code it would not be present.&lt;/p&gt;

&lt;p&gt;A pre and post snippet files were added to the build that opened and subsequently closed the iife were added to the gulp build on the project.&lt;/p&gt;

&lt;p&gt;This meant that code my colleagues and I had integrated elsewhere did not need to change its knowledge of the widget in order to continue operating correctly, but the new sites that &lt;em&gt;would&lt;/em&gt; be affected by angular would be safe from intefering with, or being interfered by the widget.&lt;/p&gt;

&lt;p&gt;A similar approach was used for sites that pre-included jQuery, which is used to enable the fuller jQuery features over jqLite in Angular 1.x.&lt;/p&gt;

&lt;p&gt;pre.js is the first file to be concatenated during build. It wraps all the code in an iife and reassigns &lt;code class=&quot;highlighter-rouge&quot;&gt;define&lt;/code&gt; so it can’t be picked up by any of the application’s modules.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/wadtech/17dded6c64e44e4cdcb3ecfd4b42a8d2.js?file=pre.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;post.js is the last file to be concatenated in the build. It re-assigns the original &lt;code class=&quot;highlighter-rouge&quot;&gt;define&lt;/code&gt; to where it was and closes the iife.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/wadtech/17dded6c64e44e4cdcb3ecfd4b42a8d2.js?file=post.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Additionally I considered another problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if angular has already been bootstrapped?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this situation angular will refuse to be included again with &lt;code class=&quot;highlighter-rouge&quot;&gt;WARNING: Tried to Load Angular More Than Once&lt;/code&gt; which may (or may not) be a problem.&lt;/p&gt;

&lt;p&gt;For this eventuality I decided that a version check should suffice to ensure that the detected version was equivalent to the bundled version. In that case I could use the existing angular version and not bother to attempt to run the bundled one.&lt;/p&gt;

&lt;p&gt;In addition a check to ensure that, if the booted version of angular is new enough, it has been loaded with jQuery instead of jqLite. If not then &lt;code class=&quot;highlighter-rouge&quot;&gt;angular.element&lt;/code&gt; can be extended.&lt;/p&gt;

&lt;p&gt;pre-angular.js is added before the main angular library is included. This way it can detect an already-bootstrapped angular and take evasive action. The widget has a hard minimum supported version number so it has to fail if this cannot be met and a new copy of angular cannot be loaded to run it.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/wadtech/17dded6c64e44e4cdcb3ecfd4b42a8d2.js?file=pre-angular.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;post-angular.js then comes after angular.js itself. It simply closes the iife from pre-angular but does not do any cleanup of its own.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/wadtech/17dded6c64e44e4cdcb3ecfd4b42a8d2.js?file=post-angular.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;For a more visual guide. Here’s how the logical blocks end up post-build:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/angular-wrapped.svg&quot; alt=&quot;Diagram representing the above text&quot; class=&quot;centered-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;note: I am no graphic designer!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At last the build provides a distributable, injectable application that is isolated from it’s runtime context without resorting to iframes! Phew!&lt;/p&gt;

&lt;p&gt;I’ve got some years’ experience with Angular now, and this is the first time I’ve not had any control over the environment- beyond the browser that is!&lt;/p&gt;</content><author><name>pete</name></author><category term="blog" /><category term="Angular" /><category term="Asset Delivery" /><summary type="html">Photo by Clint Adair on Unsplash</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/clint-adair-68588-unsplash.jpg" /></entry><entry><title type="html">Johnny-Five in Production</title><link href="https://petermellett.co.uk/blog/johnny-five/" rel="alternate" type="text/html" title="Johnny-Five in Production" /><published>2018-03-28T00:00:00+00:00</published><updated>2018-03-28T00:00:00+00:00</updated><id>https://petermellett.co.uk/blog/johnny-five</id><content type="html" xml:base="https://petermellett.co.uk/blog/johnny-five/">&lt;p&gt;&lt;em&gt;I found this writeup that I never published. Oops! Here it is at long last&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/MCKz5KGAmdQ&quot;&gt;Photo by Mariana B. on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in the halcyon days of 2015, I was involved with a project to build a virtual periscope. The brief was that motion of a periscope, sensed via a digital compass, would control a panorama of a cold-war bunker. Said bunker is actually there under your feet, but it’s not the sort of thing you want to be drilling a big hole in to install a real periscope in - you have to lower your equipment down on a bit of rope to get in. It’s certainly not the sort of place you expect to deploy software.&lt;/p&gt;

&lt;p&gt;We required a relatively simple way to read from our magnetometer - an &lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/2/27/Digital_Compass_sensor.jpg&quot; title=&quot;By Ctg4Rahat (Own work) [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons&quot;&gt;HMC5883L&lt;/a&gt; if you’re interested - and feed this heading data to our panorama running in the browser to position and interact with the virtual tour.&lt;/p&gt;

&lt;p&gt;We chose a Raspberry Pi since it can run a large part of our application and interface with the magnetometer over its GPIO pins. This also opened up the choice of technologies we could use to prototype and trial the idea before committing to a solution.&lt;/p&gt;

&lt;p&gt;The decision was neatly narrowed down by a quick glance around the office. We didn’t all have the Ruby or Go experience to use something like &lt;a href=&quot;http://artoo.io/&quot; title=&quot;Ruby on Robots&quot;&gt;Artoo&lt;/a&gt; or &lt;a href=&quot;http://gobot.io/&quot; title=&quot;Golang framework for robotics&quot;&gt;Gobot&lt;/a&gt;, but we all have plenty of JavaScript experience.&lt;/p&gt;

&lt;p&gt;I was aware of two robotics libraries for JavaScript: &lt;a href=&quot;http://cylonjs.com/&quot; title=&quot;JavaScript framework for robotics&quot;&gt;CylonJS&lt;/a&gt;, and &lt;a href=&quot;http://johnny-five.io/&quot; title=&quot;The JavaScript robotics framework&quot;&gt;johnny-five&lt;/a&gt;. In the end familiarity won out as I’d used johnny-five more than cylonjs while toying about with my own Raspberry Pi.&lt;/p&gt;

&lt;p&gt;As an aside, all of the linked libraries rely on the &lt;a href=&quot;https://github.com/firmata/protocol/&quot;&gt;firmata protocol&lt;/a&gt; in order to communicate with the host device. In the case of an arduino a program is flashed to the arduino that bootstraps and runs firmata, accepting instructions over a serial connection. With the Raspberry Pi it uses &lt;a href=&quot;https://github.com/nebrius/raspi-io/&quot;&gt;raspi-io&lt;/a&gt;, which uses a firmata-compatible API. It has the downside of requiring a serial connection to run your code.&lt;/p&gt;

&lt;p&gt;Getting started with johnny-five is about as straightforward as it gets:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s1&quot;&gt;'use strict'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RasPi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'raspi-io'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;five&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'johnny-five'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;five&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RasPi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;with the &lt;code class=&quot;highlighter-rouge&quot;&gt;board&lt;/code&gt; object, we can now make connections over the Raspberry Pi’s GPIO and use asynchronous features of the language to wait for and respond to events from the hardware.&lt;/p&gt;

&lt;p&gt;Firstly we need to tell johnny-five to expect a magnetometer to be connected- luckily there is a preset Magnetometer contructor (it’s actually a compass object with some presets for the HMC5888L sensor, see &lt;a href=&quot;https://github.com/rwaldron/johnny-five/blob/80107fd66fd671425893e6803e77f5d30fb11074/docs/magnetometer.md&quot; title=&quot;Magnetometer Example&quot;&gt;the example code&lt;/a&gt; for more details.) The default read rate is 50ms, which can be adjusted up or down depending on the datasheet of the sensor.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'ready'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;compass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;five&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Magnetometer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we have our &lt;code class=&quot;highlighter-rouge&quot;&gt;compass&lt;/code&gt; object, which is an &lt;a href=&quot;https://nodejs.org/api/events.html#events_class_events_eventemitter&quot; title=&quot;nodejs EventEmitter documentation&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;EventEmitter&lt;/code&gt;&lt;/a&gt;, we can listen to a set of events by binding callbacks and respond to them in our application.&lt;/p&gt;

&lt;p&gt;The event we want to listen out for is called &lt;code class=&quot;highlighter-rouge&quot;&gt;headingchange&lt;/code&gt;, this is fired when the magnetometer reading has changed since the last reading and contains a heading object bound to &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;. There is also a &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; event which fires on each read of the heading regardless of all previous. Since we don’t need the data if the reading has not changed, we can safely use &lt;code class=&quot;highlighter-rouge&quot;&gt;headingchange&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;compass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'headingchange'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&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;c1&quot;&gt;// Send the new heading to the waiting browser&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// to tell it to pan the virtual tour appropriately.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The best part about the johnny-five code is that it stays out of the way. A colleague with very little johnny-five experience was able to read and work on the code in short order since it introduces no new syntax and keeps the simple event/callback logic that is familiar.&lt;/p&gt;

&lt;p&gt;I didn’t find many articles about this so I think it’s safe to say that most johnny-five (and really, most firmata-based) code is being used in a hobbyist or setting. Since we had an unusual environment to run our application and didn’t need to run our hardware in an environment where firmata would be impractical it was the perfect chance to use it in a production setting.&lt;/p&gt;</content><author><name>pete</name></author><category term="blog" /><category term="JavaScript" /><category term="Electronics" /><summary type="html">I found this writeup that I never published. Oops! Here it is at long last</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/mariana-b-604752-unsplash.jpg" /></entry><entry><title type="html">Cornwall Ruby</title><link href="https://petermellett.co.uk/projects/cornwall-ruby" rel="alternate" type="text/html" title="Cornwall Ruby" /><published>2018-01-01T00:00:00+00:00</published><updated>2018-01-01T00:00:00+00:00</updated><id>https://petermellett.co.uk/projects/cornwall-ruby</id><content type="html" xml:base="https://petermellett.co.uk/projects/cornwall-ruby">&lt;p&gt;&lt;img src=&quot;/assets/images/projects/crug-red.svg&quot; alt=&quot;Cornwall Ruby&quot; class=&quot;centered-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Graphics kindly donated by @djnetherton&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cornwallruby.co.uk&quot;&gt;Cornwall Ruby&lt;/a&gt; is the RUG I’ve set up to find other like-minded developers and interested amateurs in the county.&lt;/p&gt;

&lt;p&gt;We use Slack for our normal chat and arrange semi-regular meetups.&lt;/p&gt;

&lt;p&gt;If you’d like to get involved don’t hesitate to join us. Everyone is welcome regardless of ability or level of interest - it’s always nice to meet someone with a shared interest!&lt;/p&gt;</content><author><name>pete</name></author><category term="project" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/projects/cornwall-ruby.png" /></entry><entry><title type="html">How to build a custom session driver for Laravel 5</title><link href="https://petermellett.co.uk/blog/session-driver-laravel/" rel="alternate" type="text/html" title="How to build a custom session driver for Laravel 5" /><published>2016-12-06T00:00:00+00:00</published><updated>2016-12-06T00:00:00+00:00</updated><id>https://petermellett.co.uk/blog/session-driver-laravel</id><content type="html" xml:base="https://petermellett.co.uk/blog/session-driver-laravel/">&lt;p&gt;&lt;em&gt;Originally written for and posted on &lt;a href=&quot;https://insidefoundry.co.uk/2016/12/06/custom-laravel-session-driver.html&quot;&gt;Foundry&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/l_VVd8nV3PE&quot;&gt;Photo by Ilze Lucero on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a recent project built using the PHP framework &lt;a href=&quot;https://laravel.com/&quot;&gt;Laravel&lt;/a&gt; a requirement was the ability to have certain resources be removed when the current session expires.&lt;/p&gt;

&lt;p&gt;Using the built-in database session driver we maintain a pivot table linking Entities in a transient state to the current session. Later, if the current session logs in or registers an account we can store the processed entities against that account and remove them from the transients table, disassociating them from the current session.&lt;/p&gt;

&lt;p&gt;This was brilliant and worked flawlessly-&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/laravel-session-driver-oops.png&quot; alt=&quot;Snippet of error screen during Laravel development&quot; /&gt;&lt;/p&gt;

&lt;p&gt;-until the session garbage collection attempted to clean up some old sessions and didn’t know to remove their related models.&lt;/p&gt;

&lt;p&gt;This garbage collection, which can be triggered at any time (with a default probability of 2 in 100 requests) will affect another user’s session if it causes an error. In order to prevent any foreign key errors from occurring we need to ensure that all would-be orphaned records are removed before the session itself is destroyed.&lt;/p&gt;

&lt;p&gt;To accomplish this, we devised a custom session handler to extend the provided database session handler.&lt;/p&gt;

&lt;noscript&gt;&lt;pre&gt;&amp;lt;?php
namespace App\Extensions;

// we have an Eloquent model that allows using the session table with the querybuilder methods and eloquent fluent interface- read only!
use App\Session;

// use the provided database session handler to avoid too much duplicated effort.
use Illuminate\Session\DatabaseSessionHandler;

class AppDatabaseSessionHandler extends DatabaseSessionHandler
{
    /**
     * The destroy method can be called at any time for a single session. Ensure that our related records are removed to prevent foreign key constraint errors.
     *
     * {@inheritdoc}
     */
    public function destroy($sessionId)
    {
        $session = $this-&amp;gt;getQuery()-&amp;gt;where(&amp;#39;id&amp;#39;, $sessionId);

        // tidy up any orphaned records by this session going away.
        $sessionModel = Session::find($sessionId);
        foreach ($sessionModel-&amp;gt;myModels as $model) {
            $sessionModel-&amp;gt;myModels()-&amp;gt;detach($model-&amp;gt;id);
            $model-&amp;gt;delete();
        }

        $session-&amp;gt;delete();
    }

    /**
     * Replicate the existing gc behaviour but call through to our modified destroy method instead of the default behaviour
     *
     * {@inheritdoc}
     */
    public function gc($lifetime)
    {
        $sessions = $this-&amp;gt;getQuery()-&amp;gt;where(&amp;#39;last_activity&amp;#39;, &amp;#39;&amp;lt;=&amp;#39;, time() - $lifetime)-&amp;gt;get();
        foreach ($sessions as $session) {
            $this-&amp;gt;destroy($session-&amp;gt;id);
        }
    }
}&lt;/pre&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/wadtech/74b4363bf065245a2ef055dea6adfc72.js?file=AppDatabaseSessionHandler.php&quot;&gt; &lt;/script&gt;

&lt;p&gt;Registering the &lt;code class=&quot;highlighter-rouge&quot;&gt;AppDatabaseSessionHandler&lt;/code&gt; is done through a provider, which is set up the same way as the built-in &lt;code class=&quot;highlighter-rouge&quot;&gt;DatabaseSessionHandler&lt;/code&gt;.&lt;/p&gt;

&lt;noscript&gt;&lt;pre&gt;&amp;lt;?php
namespace App\Providers;

use App\Extensions\AppDatabaseSessionHandler;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\ConnectionInterface;

use Session;
use Config;

class SessionProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot(ConnectionInterface $connection)
    {
        Session::extend(&amp;#39;app-database&amp;#39;, function($app) use ($connection) {
            $table   = Config::get(&amp;#39;session.table&amp;#39;);
            $minutes = Config::get(&amp;#39;session.lifetime&amp;#39;);
            return new AppDatabaseSessionHandler($connection, $table, $minutes);
        });
    }

    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}&lt;/pre&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/wadtech/74b4363bf065245a2ef055dea6adfc72.js?file=SessionProvider.php&quot;&gt; &lt;/script&gt;

&lt;p&gt;For our application, it relies on this functionality so it’s fine to hard-code the config into the app. Otherwise we would edit our &lt;code class=&quot;highlighter-rouge&quot;&gt;.env&lt;/code&gt; files. To make it a simpler default for the application, editing it in &lt;code class=&quot;highlighter-rouge&quot;&gt;.env.example&lt;/code&gt; would be a good idea too.&lt;/p&gt;

&lt;noscript&gt;&lt;pre&gt;&amp;lt;?php

return [
    &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;app-database&amp;#39;,

    // etc...
];&lt;/pre&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/wadtech/74b4363bf065245a2ef055dea6adfc72.js?file=session.php&quot;&gt; &lt;/script&gt;

&lt;p&gt;Now when a session expires, it transparently cleans up any model that has not been committed to permanent storage. Neat!&lt;/p&gt;

&lt;p&gt;For an alternative approach, and one that may become more valuable once the project increases in scope, would be to leverage queuing and implement a garbage collection job that looks for orphaned records and removes them from the database.&lt;/p&gt;</content><author><name>pete</name></author><category term="blog" /><category term="PHP" /><category term="Laravel" /><summary type="html">Originally written for and posted on Foundry</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/ilze-lucero-61471-unsplash.jpg" /></entry><entry><title type="html">Comprobo</title><link href="https://petermellett.co.uk/projects/comprobo" rel="alternate" type="text/html" title="Comprobo" /><published>2016-01-01T00:00:00+00:00</published><updated>2016-01-01T00:00:00+00:00</updated><id>https://petermellett.co.uk/projects/comprobo</id><content type="html" xml:base="https://petermellett.co.uk/projects/comprobo">&lt;p&gt;&lt;img src=&quot;/assets/images/projects/comprobo-logo.png&quot; alt=&quot;Comprobo Online Validation&quot; class=&quot;centered-image&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How do you know your training was completed and understood?&lt;br /&gt;
Do you know who is &lt;em&gt;really&lt;/em&gt; being trained or tested?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://unsplash.com/photos/YoadQb46v6k&quot;&gt;Photo by Craig Garner on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://comprobo.co.uk&quot;&gt;Comprobo&lt;/a&gt; approached us with the aim to rebuild a prototyped interface for their automated invigilation and identity verification solution.&lt;/p&gt;

&lt;p&gt;Choosing to approach the problem from a design-first perspective, we produced UX and UI design guidelines using atomic design patterns.&lt;/p&gt;

&lt;p&gt;For the implementation we chose &lt;a href=&quot;https://angularjs.org/&quot;&gt;Angular&lt;/a&gt; for its stability and battle-tested APIs in business of all scales, from start-up to international bank.&lt;/p&gt;

&lt;p&gt;Over the next few months we prototyped, reviewed and implemented sections of the user interface, as guided by stakeholders and prospective users.&lt;/p&gt;

&lt;p&gt;Consuming a set of structured JSON APIs and responding to user events across Angular directives we built a modular system that can be expanded and reworked without massive rewrites and allowing us a degree of flexibility to replace modules that are unsatisfactory with improved versions as and when they are required.&lt;/p&gt;

&lt;p&gt;In particular we were tasked to solve problems around capturing webcam streams, audio information and device and connectivity information to provide administrators a huge wealth of information to help them understand the nature of their user’s actions and to inform them on their subsequent actions.&lt;/p&gt;

&lt;p&gt;Using Agile methodologies we regularly review progress and priorities with other stakeholders and remote members of the team.&lt;/p&gt;

&lt;p&gt;My duties on this project include supporting third parties integrating with the software, architecting and implementing modules and features in the Angular codebase, scheduling, and managing ticket backlog.&lt;/p&gt;

&lt;p&gt;I also provide some backup support for the AWS infrastructure and some back-end services when required.&lt;/p&gt;</content><author><name>pete</name></author><category term="project" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/projects/craig-garner-202-unsplash.jpg" /></entry><entry><title type="html">MatchHero Cycling</title><link href="https://petermellett.co.uk/projects/matchhero" rel="alternate" type="text/html" title="MatchHero Cycling" /><published>2015-01-01T00:00:00+00:00</published><updated>2015-01-01T00:00:00+00:00</updated><id>https://petermellett.co.uk/projects/matchhero</id><content type="html" xml:base="https://petermellett.co.uk/projects/matchhero">&lt;p&gt;I was tasked to build a prototype for a social cycling application, used to advertise and respond to people looking for riding partners or clubs in their local area.&lt;/p&gt;

&lt;p&gt;In order to facilitate rapid development the &lt;a href=&quot;https://laravel.com&quot;&gt;Laravel PHP framework&lt;/a&gt; was utilised and the core of the application based around affordable and powerful third-party APIs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/projects/matchhero-rides.png&quot; alt=&quot;MatchHero Find a Ride interface&quot; class=&quot;centered-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In order to give the best coverage for signups, the &lt;a href=&quot;https://strava.com&quot;&gt;Strava&lt;/a&gt; API was used to build in social sign-up and login with ready-populated profile data including avatars, unit preferences and names.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/projects/matchhero-profile.png&quot; alt=&quot;MatchHero Profile snippet&quot; class=&quot;centered-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using the Strava API allows for future expansion of the platform to include more functionality based upon the data provided. It also provides access to the same privacy restrictions as the user would use with Strava, which we can then honour in the MatchHero application.&lt;/p&gt;

&lt;p&gt;To build out the ‘post a ride’ and ‘find a ride’ features, the &lt;a href=&quot;https://developers.google.com/maps/&quot;&gt;Google Maps API&lt;/a&gt; was used. Allowing a user to select their location by latitude and longitude, if known, or a street address if not using &lt;a href=&quot;https://developers.google.com/maps/documentation/geocoding/intro&quot;&gt;Geocoding&lt;/a&gt; services.&lt;/p&gt;

&lt;p&gt;During the build phase we used a round of wireframe and whiteboard sessions to target the desired functionality and MVP features that we thought would be appropriate for the platform.&lt;/p&gt;

&lt;p&gt;As keen cyclists in the team at the time, we all had a say in the design and initial functionality- and also a keen eye for what wasn’t working after launch. Some adjustments were made post-launch to improve the user experience and provide a solid application to extend and promote in the community.&lt;/p&gt;

&lt;p&gt;Using a prototype and review process I put together the initial release in around two weeks of development time.&lt;/p&gt;

&lt;p&gt;If you’re interested in the more social side of cycling then visit &lt;a href=&quot;https://matchhero.cc&quot;&gt;MatchHero Cycling Club&lt;/a&gt;.&lt;/p&gt;</content><author><name>pete</name></author><category term="project" /><summary type="html">I was tasked to build a prototype for a social cycling application, used to advertise and respond to people looking for riding partners or clubs in their local area.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://petermellett.co.uk/assets/images/projects/matchhero-banner.jpg" /></entry></feed>