Richard Hart

Head of Something @ Somewhere
Kent, UK

My Music
My Photos

LinkedIn
Mastodon

  • The best developers I have worked with have all been incredible in self-directed learning. And yet, a typical pattern that I see more junior developers get stuck on is not knowing what to learn to progress their skills as a developer.

    I spend a lot of time reading, whether blog posts or books, watching talks or listening to podcasts. I have never found myself short of topics to look into more or things to try out. So when I try to picture how someone can feel stuck, I struggle to see where that obstacle is coming from. 

    There could be many reasons at play here. A more benign reason being fear and uncertainty: “There are so many things I could learn. What if I pick the wrong one?”. To that, I say start anywhere and see where it takes you. This approach then takes you on a “just in time” approach to learning, where, as you get deeper down the rabbit hole, the topics you need to learn change depending on where you end up. Whereas a more serious reason would be that the individual feels it’s not their responsibility: “My manager should tell me what to learn”. I could see how this way of thinking could be born out of schooling or bootcamps where we are told what to learn. So the responsibility for one’s development has been abdicated. 

    Self-directed learning is a skill but an important one for anyone looking to get ahead. For those that struggle, my advice is to jump in. It doesn’t matter where and it doesn’t matter how. Pick up a book. Watch a video. Listen to a podcast. But no matter what, put as much of it into action as you can and link the dots. Every step you take will lead to the next, and that next step may require you to change course, but that’s okay, as now you’re learning.


  • While trying to setup a new Rails 5.2 app in Intellij it would continually be detected as a web project and give no way of adding the Ruby on Rails module.

    The solution is to delete the node_modules folder before creating the new project.


  • Test Working with a SOAP API with Spring Boot WS. I was getting the following error trying to create the request.

    com.sun.istack.SAXException2: unable to marshal type "com.ur-ban.GetSubscriptioninfo" as an element because it is missing an @XmlRootElement annotation

    I was originally directly using the JAXB generated classes to form my requests:

    GetSubscriptioninfo subscriptionInfo = new GetSubscriptioninfo();
    subscriptionInfo.setSubscriptionId(1);
    
    GetSubscriptioninfoResponse subscriptionInfoResponse = 
      (GetSubscriptioninfoResponse) getWebServiceTemplate()
          .marshalSendAndReceive("http://www.ur-ban.com/subscription", subscriptionInfo);

    The correct way is to use the provided JAXB ObjectFactory:

    ObjectFactory objectFactory = new ObjectFactory();
    GetSubscriptioninfo subscriptionInfo = objectFactory.createGetSubscriptioninfo();
    subscriptionInfo.setSubscriptionId(1);
    
    GetSubscriptioninfoResponse subscriptionInfoResponse = 
      (GetSubscriptioninfoResponse) getWebServiceTemplate()
          .marshalSendAndReceive("http://www.ur-ban.com/subscription", subscriptionInfo);

    But to prevent marshalling errors you need to wrap your object in a JAXBElement object:

    ObjectFactory objectFactory = new ObjectFactory();
    GetSubscriptioninfo subscriptionInfo = objectFactory.createGetSubscriptioninfo();
    subscriptionInfo.setSubscriptionId(1);
    
    JAXBElement jaxbGetSubscriptionInfo = objectFactory.createGetSubscriptioninfo(subscriptionInfo);
    
    GetSubscriptioninfoResponse subscriptionInfoResponse = 
      (GetSubscriptioninfoResponse) getWebServiceTemplate()
          .marshalSendAndReceive("http://www.ur-ban.com/subscription", jaxbGetSubscriptionInfo);

  • One of my bad habits is constantly buying computing books. This wouldn’t be so bad if I read them, but I have amassed a huge backlog of books that will most probably never be read and which ends up being a waste of money.

    A couple of posts I read recently have led me to the decision that I should stop, or at least drastically cut down on, buying computing books. The first post talked about “learning voyerism” where you are really more interested in the idea of learning new things instead of learning the thing itself and the second talked about spending time going deeper into topics instead of boucing lightly through many different ones.

    It is very difficult to stay focused on one thing when there are so many things happening in the world of computing all the time. There are a tonne of new and exciting languages and frameworks being released all the time. And while it would be great to try them all, that can only mean that you’ll never actually become good at one of them.

    I have always been a bit of a generalist and while knowing a bit about everything isn’t a bad thing, there is a fine line between knowing a bit of everything while being proficient at some things and knowing not quite enough of everything to be unable to do anything at all.


  • Trying out chruby and ruby-install and installing Ruby 2.2.2 with ruby-install was giving be the error:

    directory not found for option '-L/usr/local/Cellar/openssl/1.0.2k/lib'

    Setting the LDFLAGS env var solved this for me:

    export LDFLAGS=-L/usr/local/opt/openssl/lib

  • To run Alacritty without spawning a new terminal window you can use Automator to create a new application wrapper.

    Create a new application document and choose Run Shell Script as the action. Then simply add the path to your built Alacritty and save the document to your Applications folder.


  • Made this mistake today while writing some code to delete data out of Redis.

    def delete_keys
      cursor = "0"
    
      loop do
        cursor, keys = redis.scan(cursor, count: SCAN_COUNT)
        keys.reject! { |key| keep_key?(key) }
    
        redis.del(keys) unless keys.empty?
    
        break if cursor == "0"
      end
    end
    
    def keep_key?(key)
      keep_keys.any? { |keep_key| key.include?(keep_key) }
    end
    
    def keep_keys
      flattened_keys << transaction_id
    end

    The mistake was in the final method keep_keys. Every check to see if a key should be rejected I was adding an element to the flattened_keys array over and over again, causing my deletion to slow down over time. A simple change to memoize the keep keys made the process go from never finishing to, completing in a few seconds.

    def keep_keys
      @keep_keys ||= (flattened_keys << transaction_id)
    end
    

  • Copying files to a Synology NAS was painfully slow with Carbon Copy Cloner. Following the official help of ejecting the volume in Finder made all the difference. Syncs now happen as fast as they should do.


  • Text in iTerm looked a little fuzzy after moving to a non-retina 4K monitor. I discovered that setting thin strokes to always be on has made text look a lot crisper.


  • Being able to recommend products to shoppers is a vital part of any online store. The “Customers Who Bought This Item Also Bought” section can lead to a lot of extra sales if done well. The Jaccard Index is a way of measuring similarity between items. Using some custom SQL we can extract the values we need:

    WITH
      product_sales AS (
        SELECT spree_variants.id AS variant_id, SUM(quantity) as sold
          FROM spree_line_items
          JOIN spree_variants ON spree_line_items.variant_id = spree_variants.id
          GROUP BY spree_variants.id ),
    
      product_joint_frequency AS (
        SELECT v1.product_id AS product_a, v2.product_id AS product_b, COUNT(*) AS frequency
          FROM spree_line_items AS li1
          JOIN spree_line_items AS li2 ON li1.order_id = li2.order_id
          JOIN spree_variants AS v1 ON li1.variant_id = v1.id
          JOIN spree_variants AS v2 ON li2.variant_id = v2.id
          WHERE li1.variant_id != li2.variant_id
          GROUP BY product_a, product_b )
    
    SELECT product_a, product_b, frequency, sales1.sold AS a_sold, sales2.sold AS b_sold
      FROM product_joint_frequency
      JOIN product_sales AS sales1 ON product_joint_frequency.product_a = sales1.variant_id
      JOIN product_sales AS sales2 ON product_joint_frequency.product_b = sales2.variant_id
      ORDER BY product_a, joint_frequency DESC, product_b
    

    With these values we can then calculate the affinity between sold products:

    frequency / (a_sold + b_sold - frequency)

     


  • Since my last post about generating random email addresses in TextExpander, I’ve move from using Ruby to Applescript to do so. Here is my script which essentially does the same thing.

    set email to (do shell script "date +%s")
    set email to email & "@yopmail.com"
    set the clipboard to email
    email

     


  • A great library for adding typeahead support to your site is Twitter’s Typeahead.js. Even better is the excellent Bloodhound suggestion engine which comes with it. Sometimes though if you’re dealing with a remote suggestion engine like Elasticsearch’s completion suggester you don’t need to run remote results once again through another suggestion engine. Bypassing Bloodhound is as simple as hooking your own source function into your Typeahead definition.

    $('input#keywords').typeahead({
      highlight: true,
    },
    {
      name: 'brands',
      display: 'value',
      source: function(query, syncResults, asyncResults) {
        $.get('/search?q=' + query, function(data) {
          asyncResults(data);
        });
      }
    })
    

     


  • One of the core ideas of Rails is convention over configurations. Models go in app/models, controllers go in app/controllers and views go in app/views. The danger is that we stick to those conventions no matter what and we end up either with fat controllers, fat models or even worse a mixture of both.

    Many times we don’t take enough advantage of Ruby’s object oriented nature and the ability to extract functionality out into separate classes. Doing so can make an entire application easier to extend, understand and test. I have tried approaching this from different angles in different projects and I’ve found that the two main hurdles to getting this right are naming classes and putting them in the right place.

    I have experimented with naming such as UserAuthenticator and UserAuthenticationService, and always end up feeling uncomfortable I constantly wonder if the other name is better or if there is a better way entirely. Using agent nouns in class names is considered a code smell, but the more that I think about it the important part is picking a choice and sticking to it. I was looking at the GitLab repository and noticed that they’ve done exactly that, everything is named in a consistent manner. I think many may dislike that but it makes things extremely clear and easier for anyone contributing as to what they should name their classes and where they should put them.


  • There was a post on TechCrunch a few days ago about the big lie in startups and how you can’t run a startup without lying about a million things. When I really think about all these funded startups it upsets me, mainly because I’ve tried to bootstrap all my endeavours. That does make me sound bitter, and I am if I’m honest about it. If you start a company, get funding and fail you’re still often seen as a winner in people’s eyes where as those that bootstrap a company and fail are just seeing as losers. When did we reach a point where getting VC was the main priority after starting a business? When did we lose sight of actually trying to make money first?


  • After a rash of security updates for WordPress, I finally buckled down to move our install of WordPress off to its own server. This would be easy if we were running the blog on it’s own domain, but it was actually living under /blog on our main domain for SEO reasons. Running nginx as a reverse proxy to WordPress on a different machine was not an easy task. A lot of guides did not seem to have the information I needed so here are the steps I took to get it working.

    Add the proxy_pass definition to your nginx config:

    location /blog/ {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Forwarded-Proto https;
      proxy_pass http://your-wordpress-ip-or-domain/;
    }

    Update your WordPress settings. This is the key to getting your site to work correctly. Leave the WordPress Address as where the site is hosted, but change the Site Address to be where URLs are to be written as linking to

    Screenshot 2015-07-27 21.43.38

    I also had to add a rewrite to my .htaccess file to remove trailing slashes:

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteRule (.+)/$ http://www.yourdomain.com/blog/$1 [R=301,L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    </IfModule>


    Be warned that the permalinks section will suggest a .htaccess file which has /blog/ as the RewriteBase, but this just sends things into a redirect loop. Leave it as the standard root setting.

    Make sure you also check that the canonical tags generated by your theme are correct. You want to be sure that any references on the WordPress Address URL canonicalise to Site Address URLs.