<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Joe Ray</title>
    <description>Joe&#39;s writings</description>
    <link>https://joeray.me/</link>
    <atom:link href="https://joeray.me/feed.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
        <title>Running a Game Day</title>
        <description>&lt;p&gt;Recently at &lt;a href=&#34;https://www.koothplc.com&#34;&gt;Kooth&lt;/a&gt; we ran a game day. We assembled
all the engineers, and some of the product team, to practice what we would do
in case of a production incident and learn how we and our systems might
respond.&lt;/p&gt;
&lt;p&gt;There are a few reasons we wanted to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not everyone has experience dealing with incidents and we want to ensure
that all our engineers feel confident responding to incidents themselves.&lt;/li&gt;
&lt;li&gt;Everyone can improve how they handle incidents, especially understanding how
best to work collaboratively to solve them.&lt;/li&gt;
&lt;li&gt;It allowed us to explore our systems and better understand how they function,
as well as how they can fail, in a safe environment.&lt;/li&gt;
&lt;li&gt;We wanted to see if we could come up with user stories or processes to
mitigate failure that we hadn&amp;rsquo;t considered.&lt;/li&gt;
&lt;li&gt;We haven&amp;rsquo;t yet had to deal with a major security incident—thankfully!—but we
wanted to be more prepared should that happen.&lt;/li&gt;
&lt;li&gt;It gave us an opportunity to work with people we wouldn&amp;rsquo;t normally work with
and form better bonds.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-did-we-plan&#34;&gt;How did we plan?&lt;/h2&gt;
&lt;p&gt;Three of us volunteered to plan and run the game day and we started by figuring
out what we wanted to achieve from the day. Since very few of us had experience
with participating in a game day we decided that we would start by coming up
with a set of scenarios we could control, rather than doing something like
&lt;a href=&#34;https://securitytrails.com/blog/cybersecurity-red-blue-team&#34;&gt;red team/blue
team&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We came up with a list of scenarios we could run that would help achieve one or
more of our goals. We wanted to ensure that people could practice incident
response collaboratively and in as real a way as possible. Examples included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A data breach is discovered.&lt;/li&gt;
&lt;li&gt;A third party provider we heavily rely on is down.&lt;/li&gt;
&lt;li&gt;One of our databases is unavailable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also thought about who would be participating and split people up into
teams. We tried to spread out people with more experience as well as splitting
up people who normally work together.&lt;/p&gt;
&lt;p&gt;One question we discussed was how we would run the scenarios: what environment
should we use? Should we resize it to production scale? Should we try and
simulate production levels of traffic? Should each team do the same scenario or
each take a different one?&lt;/p&gt;
&lt;p&gt;In the end, we decided to keep it as simple as possible and just use one of our
non-production environments without any modifications. We figured that if it
was successful, we could do something more complex at a future game day. We
also decided to allocate each team a different scenario, partly because some of
the scenarios might result in teams interfering with each other and partly
because we felt we would learn more that way.&lt;/p&gt;
&lt;p&gt;Finally, we discussed how to structure the day. We wanted to make sure we left
time for teams to showcase what they&amp;rsquo;d learned, as well as hold a retrospective
at the end of the day on how the day went.&lt;/p&gt;
&lt;h2 id=&#34;what-did-we-learn&#34;&gt;What did we learn?&lt;/h2&gt;
&lt;p&gt;Overall the day was a great success! People had a lot of fun exploring the
scenarios and we gained plenty of useful insights.&lt;/p&gt;
&lt;p&gt;The biggest thing that came out of the retro was that the day had highlighted
people&amp;rsquo;s knowledge gaps which will allow us to focus training and future
exercises.&lt;/p&gt;
&lt;p&gt;We also learned about where our processes, documentations and systems are
lacking and now have ideas of how to improve them.&lt;/p&gt;
&lt;p&gt;Finally, people really liked spending time working with people from outside
their teams. This sparked a lengthy discussion about team rotations.&lt;/p&gt;
</description>
        <pubDate>Sat, 29 May 2021 00:00:00 UTC</pubDate>
        <link>https://joeray.me/running-a-game-day/</link>
        <guid isPermaLink="true">https://joeray.me/running-a-game-day/</guid>
    </item>
    
    
    
    <item>
        <title>Remote working: tools and tips</title>
        <description>&lt;p&gt;Over the past six months or so, I&amp;rsquo;ve been working in a team that&amp;rsquo;s almost entirely remote. Working in a team that is at least somewhat remote seems to be an increasingly common pattern in ThoughtWorks so it&amp;rsquo;s something that&amp;rsquo;s being discussed more.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s been a lot of useful discussion on how to make it successful. Here are some of the highlights.&lt;/p&gt;
&lt;h1 id=&#34;meetings&#34;&gt;Meetings&lt;/h1&gt;
&lt;h2 id=&#34;retrosworkshops&#34;&gt;Retros/Workshops&lt;/h2&gt;
&lt;figure&gt;
    &lt;img alt=&#34;Workshop with stickies&#34; src=&#34;https://joeray.me/i/posts/workshop.jpg&#34; /&gt;
    &lt;cite&gt;&lt;a href=&#34;https://www.flickr.com/photos/gdsteam/39569118662/in/photolist-23hA5pQ-2bnX9Ru-28ebniC-23hA5ej-Fcu93h-23kaHFp-26ymy1C-JotYBK-JotZ3z-2a4HUqv-29fsD4Y-2aUYZWG-RxHCDu-28dNPzE-28ebz9S-KCCceP-RxHDq9-RxHDu7-2dB3Ems-2czCrqb-aagjiy-da6qok-ejVjrT-8ze4nr-hTiQFA-8Lg6pm-JotYFn-hTipKc-66tjfX-hTjyve-66tjrt-YuuMRD-2aYBSer-um518-23hA5bo-69QkZG-z5xYiL-29eYLnb-23kaJ4t-zZhj7Q-zJZvwJ-24Qz7at-kpd81T-23hA51U-MbpSzj-ZzFibM-DNe4td-23hA5u9-23hA4M7-29DvKni&#34;&gt;Cross-government design systems workshop at GDS, December 2017&lt;/a&gt; &lt;a href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC By 2.0&lt;/a&gt;&lt;/cite&gt;
&lt;/figure&gt;
&lt;p&gt;We love using stickies in meetings at ThoughtWorks and you don&amp;rsquo;t have to go far on the &lt;a href=&#34;https://www.thoughtworks.com/clients&#34;&gt;ThoughtWorks website&lt;/a&gt; to find this out!&lt;/p&gt;
&lt;p&gt;There are some tools which attempt to recreate this digitally.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://funretro.io/&#34;&gt;funretro.io&lt;/a&gt; and &lt;a href=&#34;http://ideaboardz.com/&#34;&gt;Ideaboardz&lt;/a&gt;: Used by lots of teams to facilitate remote retros.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://realtimeboard.com/&#34;&gt;RealTimeBoard&lt;/a&gt;: Recommended by many teams, allows more flexibility than tools dedicated to retros, for example to draw circles around groups of stickies etc.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mural.co/&#34;&gt;Mural&lt;/a&gt;: An alternative to RealTimeBoard.&lt;/li&gt;
&lt;li&gt;Google Docs/Sheets: My team uses Google Docs to facilitate retros at the moment which works pretty well. &lt;a href=&#34;https://twitter.com/yrnclndym&#34;&gt;Andy Yates&lt;/a&gt; also suggested using Google Sheets for workshops where each cell could represent a place to put a sticky. We tried Google Draw but it was too clunky to be useful in the context of a retro.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;diagramming&#34;&gt;Diagramming&lt;/h2&gt;
&lt;figure&gt;
    &lt;img alt=&#34;Woman explaining whiteboard diagram&#34; src=&#34;https://joeray.me/i/posts/diagramming.jpg&#34; /&gt;
    &lt;cite&gt;&lt;a href=&#34;https://www.flickr.com/photos/juhansonin/12140602074/in/photolist-juPL2q-4PYss7-4eUew5-Redcry-c4jqZ5-o8WS-4JsQRP-7zeWuY-79jUDv-7zeWHU-5j1XQY-um518-mH19dR-5iW3jD-z4RtW-4EsfYY-diUhqt-5qFikY-798Lmu-gCV7MF-5BnvsJ-5vAncB-5iLHtt-7hSKBq-uuisL-5iLGS8-7icyVt-4Eo1Ua-4E5roB-52DRC-5mNV73-5iR14Q-6uwZKC-9e1juZ-6uwZyQ-4FxDUK-6nVTpR-5La8Gk-7yHzX2-6ANRu-ayHWSE-6gRdcq-7uVcMD-6gM3rH-8X6B5a-7tfSdW-b7NG1V-5KPznQ-9Ht7mr-cnHxF&#34;&gt;Jennifer drawing ideas&lt;/a&gt; &lt;a href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC By 2.0&lt;/a&gt;&lt;/cite&gt;
&lt;/figure&gt;
&lt;p&gt;You don&amp;rsquo;t have access to a whiteboard when you&amp;rsquo;re all remote so here are some suggestions for alternatives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some video conferencing software (e.g. &lt;a href=&#34;https://zoom.us&#34;&gt;Zoom&lt;/a&gt;) allows drawing on shared screens for quick collaborations.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/jimbarritt&#34;&gt;Jim Barritt&lt;/a&gt; had success sharing &lt;a href=&#34;https://www.omnigroup.com/omnigraffle&#34;&gt;Omnigraffle&lt;/a&gt; over video conferencing software when needing to explain things.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;audiovisual&#34;&gt;Audio/Visual&lt;/h1&gt;
&lt;h2 id=&#34;accessibility&#34;&gt;Accessibility&lt;/h2&gt;
&lt;p&gt;Some tips were about how to make remote working accessible to those with hearing impairments but really, given how difficult it can sometimes be to follow what people are saying on video conferencing calls, these apply to anyone:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t allow more than one person to talk at once.&lt;/li&gt;
&lt;li&gt;Encourage people to screen share and use diagrams, charts, documents etc. to back up what they are saying.&lt;/li&gt;
&lt;li&gt;Ask people to send links to supporting materials (diagrams, docs, etc.) in advance of the meeting and then refer to them during the call.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ava.me/&#34;&gt;Ava&lt;/a&gt; and &lt;a href=&#34;https://support.google.com/docs/answer/4492226?hl=en&#34;&gt;Google Docs text-to-speech&lt;/a&gt; can be used for doing text-to-speech in real-time, though experiences with this were mixed.&lt;/li&gt;
&lt;li&gt;Take minutes for meetings (using a Google Doc means it can be real-time and people can follow along).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;audio&#34;&gt;Audio&lt;/h2&gt;
&lt;p&gt;It was universally agreed that good headsets are a must. As &lt;a href=&#34;https://twitter.com/kornys&#34;&gt;Korny Sietsma&lt;/a&gt; put it, “I think having a boom mic is quite critical - no amount of smart technology can quite compare with just putting a small microphone quite close to your mouth.”&lt;/p&gt;
&lt;p&gt;Some recommendations were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.co.uk/gp/product/B005NPHQ04/ref=oh_aui_search_asin_title?ie=UTF8&amp;amp;psc=1&#34;&gt;Jabra UC Voice 550 Duo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Gaming headsets, e.g. &lt;a href=&#34;https://www.amazon.co.uk/HyperX-HX-HSCS-BK-EM-Stinger-Headset/dp/B01LRX2DSA/&#34;&gt;HyperX HX-HSCS-BK/EM&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;Often these will have features such as raising the mic to mute.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.jabra.co.uk/business/speakerphones/jabra-speak-series&#34;&gt;Jabra speakers&lt;/a&gt; are good when you have some members of the team clustered around the same laptop, but only when you&amp;rsquo;re in a quiet room.&lt;/li&gt;
&lt;li&gt;Directional (karaoke-style) mics were recommended by one colleague for groups of people as it forces people to speak one at a time and only the person with the mic can speak.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://krisp.ai/&#34;&gt;krisp.ai&lt;/a&gt;: Some reported installing this improved sound quality, but someone else found they experienced a lot of cut-outs when they did and had to revert.&lt;/li&gt;
&lt;li&gt;Push-to-talk features of video conferencing software are useful to prevent background noise from leaking in when you&amp;rsquo;re not talking, though as Korny said, &amp;ldquo;you&amp;rsquo;re on mute&amp;rdquo; must be the most common phrase in video conferencing meetings. &lt;a href=&#34;https://mizage.com/shush/&#34;&gt;Shush&lt;/a&gt; provides the same functionality on a Mac but system-wide.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pairing&#34;&gt;Pairing&lt;/h2&gt;
&lt;figure&gt;
    &lt;img alt=&#34;Two people pairing&#34; src=&#34;https://joeray.me/i/posts/pairing.jpg&#34; /&gt;
    &lt;cite&gt;&lt;a href=&#34;https://www.flickr.com/photos/wocintechchat/25392428253/in/photolist-EFQQg8-dfkTYU-FsKCaU-gu8XTz-gu9mZ7-22YwBXe-edueFv-FbStZf-gueNiG-dgc4Fm-edufpR-gu9xcm-edufKV-gu9WDR-edzU4G-Fv3KQX-EFQUr2-dfkSVE-eduenD-edufWX-gud8Rq-9xvTZA-9xsMpa-9xCP6J-buRsjJ-edudhc-edtDCT-edzRzs-gufuWB-bHLWMB-9xwFM7-9xvLz5-gufXj4-ebiGBZ-9xvQ9y-9xsUHP-edzQPY-edz9Vj-buRV1A-gufSAr-gueUmU-gueY67-edzPZ7-9xvSUs-gufBvE-gufjYF-edu6AB-ebiNyc-bHuaQz-gug6m6&#34;&gt;wocintech (microsoft) - 62&lt;/a&gt; &lt;a href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC By 2.0&lt;/a&gt;&lt;/cite&gt;
&lt;/figure&gt;
&lt;p&gt;I love pairing but doing it remotely can be a challenge. There are a couple of tools we&amp;rsquo;ve come across to help:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slack has good screen-sharing capabilities and allows others to control/draw on your screen.&lt;/li&gt;
&lt;li&gt;Zoom/other video conferencing software can also be used for this.&lt;/li&gt;
&lt;li&gt;Some were suggesting using the collaborative features in &lt;a href=&#34;https://code.visualstudio.com/&#34;&gt;Visual Studio Code&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For remote pairing in a terminal environment, &lt;a href=&#34;https://github.com/tmux/tmux/&#34;&gt;tmux&lt;/a&gt; can be used to allow two or more people to connect to the same session. &lt;a href=&#34;https://github.com/tmate-io/tmate&#34;&gt;tmate&lt;/a&gt; helps with the setup of this.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;dealing-with-problems&#34;&gt;Dealing with Problems&lt;/h2&gt;
&lt;p&gt;Technical problems can really hamper remote teams.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One important point, highlighted by &lt;a href=&#34;https://twitter.com/claresudbery&#34;&gt;Clare Sudbery&lt;/a&gt;, was that “people will just put up with remote communication problems when they shouldn&amp;rsquo;t, so anticipate that and be deliberate and proactive about addressing issues with hardware, software and connectivity. Put time aside to triage any problems as a matter of urgency and be prepared to be quite pushy about demanding investment and support in decent tools to facilitate remote meetings.”&lt;/li&gt;
&lt;li&gt;If you do encounter problems, having an alternative channel such as the chat window in your video conferencing software, the team Slack or a Google Doc, can help mitigate them. (See also the Accessibility section above.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;culture&#34;&gt;Culture&lt;/h1&gt;
&lt;p&gt;The team culture is really key in making a remote team successful. There are a number of aspects to this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If even one person is remote, everyone should be remote (use headsets, even be in separate rooms).&lt;/li&gt;
&lt;li&gt;Invest in building relationships with remote team members:
&lt;ul&gt;
&lt;li&gt;Make sure that the team meets regularly in person.&lt;/li&gt;
&lt;li&gt;Try and also meet in person when new people join/at the start of the project.&lt;/li&gt;
&lt;li&gt;‘Water-cooler’ conversations have to happen intentionally. You could e.g. have a rule where the first ten minutes of every meeting is officially set aside for non-meeting-related chat.&lt;/li&gt;
&lt;li&gt;Check in and out in your team chat when you arrive at/leave work.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Where a significant part of the team is co-located, or there are multiple teams in different locations, you could try having big TVs set up by the team(s) with webcams and mics. This allows you to see the other groups of people working, and if you have an impromptu question you can walk up and use the mic to get the other location&amp;rsquo;s attention.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 02 Feb 2019 00:00:00 UTC</pubDate>
        <link>https://joeray.me/remote-working-tools-and-tips/</link>
        <guid isPermaLink="true">https://joeray.me/remote-working-tools-and-tips/</guid>
    </item>
    
    
    
    <item>
        <title>Dynamically loading classes from modules in Python</title>
        <description>&lt;p&gt;I was working on a bit of code today where I wanted to be able to dynamically
load a class from a module that&amp;rsquo;s specified by some external configuration.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you have a package called &lt;code&gt;widgets&lt;/code&gt; and within that, a series of
modules (e.g. &lt;code&gt;mail&lt;/code&gt;, &lt;code&gt;sms&lt;/code&gt;, &lt;code&gt;irc&lt;/code&gt; etc.) that contain classes which all inherit
from the superclass &lt;code&gt;WidgetAPI&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To load the &lt;code&gt;widgets.mail.WidgetAPI&lt;/code&gt; class when given the string &lt;code&gt;&#39;mail&#39;&lt;/code&gt; by
the user, you could do:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;inspect&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;importlib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;widgets&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;WidgetAPI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_widget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;widget_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;importlib&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;widgets.&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{0}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;widget_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inspect&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isclass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;issubclass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;WidgetAPI&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;WidgetAPI&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find_widget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;mail&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find_widget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blah&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will produce the output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;class &amp;#39;widgets.mail.MailWidget&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 UTC</pubDate>
        <link>https://joeray.me/dynamically-load-classes-from-module-python/</link>
        <guid isPermaLink="true">https://joeray.me/dynamically-load-classes-from-module-python/</guid>
    </item>
    
    
    
    <item>
        <title>PGP Keys with a Yubikey and GPGTools</title>
        <description>&lt;p&gt;I finally got around to getting a &lt;a href=&#34;https://www.yubico.com/products/yubikey-hardware/&#34;&gt;Yubikey NEO&lt;/a&gt; last week and along with setting it up for 2FA for some of my online accounts, I took the opportunity to generate a new PGP key.&lt;/p&gt;
&lt;p&gt;I mostly followed Simon Josefsson&amp;rsquo;s &lt;a href=&#34;https://blog.josefsson.org/2014/06/23/offline-gnupg-master-key-and-subkeys-on-yubikey-neo-smartcard/&#34;&gt;excellent post on the subject&lt;/a&gt; and ended up with a new private key and set of subkeys.&lt;/p&gt;
&lt;p&gt;The one snag I had was getting it to work with GPGTools for Mac OSX – I kept getting the message &amp;ldquo;card error&amp;rdquo; when trying to access it. I upgraded to the latest version (with GPG 2.0.30) today and it seemed to work, though I also noticed you can&amp;rsquo;t use the Yubico Authenticator at the same time as using the GPG tool so that may have explained my problems.&lt;/p&gt;
&lt;p&gt;My new key ID is &lt;a href=&#34;https://pgp.mit.edu/pks/lookup?search=0x804EFECC&amp;amp;op=index&#34;&gt;804EFECC&lt;/a&gt;, a copy of which can be found &lt;a href=&#34;https://joeray.me/804EFECC.txt&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;https://keybase.io/joerayme/key.asc&#34;&gt;on Keybase.io&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 05 Jul 2016 00:00:00 UTC</pubDate>
        <link>https://joeray.me/pgp-keys-with-yubikey-and-gpgtools/</link>
        <guid isPermaLink="true">https://joeray.me/pgp-keys-with-yubikey-and-gpgtools/</guid>
    </item>
    
    
    
    <item>
        <title>The environmental case for eating less meat</title>
        <description>&lt;blockquote class=&#34;pullquote&#34;&gt;Unless there is a &#34;radical shift in meat and dairy consumption&#34;, it&#39;s unlikely we&#39;ll be able to keep global temperature rises to two degrees Celsius.&lt;/blockquote&gt;
&lt;p&gt;The world eats a lot of meat and we&amp;rsquo;re eating more and more each year. The supply of meat per person across the globe almost doubled in the 50 years between 1961 and 2011.&lt;/p&gt;
&lt;p&gt;Growth in countries like China is even more pronounced – in 2011 the supply of meat to China was almost 16 times as much per person as in 1961. Even in countries such as the US and UK, supply has increased.&lt;/p&gt;
&lt;figure class=&#34;right&#34;&gt;
    &lt;figcaption&gt;&lt;a name=&#34;fig-1&#34;&gt;&lt;/a&gt;Fig. 1: Meat supply per person 1961-2011&lt;/figcaption&gt;
    &lt;svg id=&#34;meat-chart&#34;&gt;&lt;/svg&gt;
    &lt;cite&gt;Source: Meat supply data from &lt;a href=&#34;http://faostat3.fao.org/browse/FB/CL/E&#34;&gt;Food and
    Agriculture Organization of the United States&lt;/a&gt; and population data from &lt;a
    href=&#34;http://data.worldbank.org/indicator/SP.POP.TOTL&#34;&gt;The World Bank&lt;/a&gt;&lt;/cite&gt;
&lt;/figure&gt;
&lt;h2 id=&#34;the-problems-with-meat&#34;&gt;The problems with meat&lt;/h2&gt;
&lt;p&gt;The problem with meat production is that it&amp;rsquo;s so inefficient: livestock consume 4.6 billion tonnes of carbon per year in feed but the produce (meat and dairy) ends up as only 2.6% of that, even before losses are taken into account.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s more, agriculture represents a significant proportion of global emissions, exceeding those from the transport sector. Experts estimate that the total global greenhouse gas emissions for livestock alone account for at least 14.5%,&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; and possibly up to 51%.&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;And one paper predicts that if current population and consumption trends continue, greenhouse gas emissions for livestock would increase by around 77%.&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://joeray.me/i/posts/cows_eating.jpg&#34; alt=&#34;Cows eating&#34;&gt;
&lt;cite&gt;&lt;a href=&#34;https://www.flickr.com/photos/46891418@N08/8118957951/&#34;&gt;Gideon Mendel/WSPA&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;With meat production being so inefficient, huge amounts of land are given over just to feeding animals – 75% of agricultural land and 23% of arable land worldwide&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt; – and increasing demand means that cattle farming also plays a major role in deforestation, both directly and indirectly.&lt;/p&gt;
&lt;p&gt;In 2011, 90% of deforestation in Columbia was caused by cattle farming.&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt; 75% of soy production, also a major contributor to deforestation, goes to feeding animals.&lt;sup id=&#34;fnref:7&#34;&gt;&lt;a href=&#34;#fn:7&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;7&lt;/a&gt;&lt;/sup&gt; Not only does deforestation destroy habitats and reduce biodiversity, it also destroys a valuable source of carbon storage and is a primary cause of carbon emissions in the agricultural sector.&lt;sup id=&#34;fnref:8&#34;&gt;&lt;a href=&#34;#fn:8&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&#34;how-will-stopping-eating-meat-help&#34;&gt;How will stopping eating meat help?&lt;/h2&gt;
&lt;p&gt;In principle, if everyone moved to a purely vegan diet we could feed 4 billion extra people.&lt;sup id=&#34;fnref:9&#34;&gt;&lt;a href=&#34;#fn:9&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;9&lt;/a&gt;&lt;/sup&gt; A reduction in the production of meat will also result in there being more land available for other uses – planting forests, bioenergy etc.&lt;/p&gt;
&lt;p&gt;But perhaps most importantly, a 2014 report states that unless there is a &amp;ldquo;radical shift in meat and dairy consumption&amp;rdquo;, it&amp;rsquo;s unlikely we&amp;rsquo;ll be able to keep global temperature rises to two degrees Celsius.&lt;sup id=&#34;fnref:10&#34;&gt;&lt;a href=&#34;#fn:10&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Not all land that is used for grazing would be suitable for growing crops – about 14% of feed by carbon mass comes from land like this – but its use for grazing &amp;ldquo;can still have negative consequences for carbon storage and biodiversity.&amp;rdquo;&lt;sup id=&#34;fnref:11&#34;&gt;&lt;a href=&#34;#fn:11&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;However, it is difficult to envisage a transition to entirely plant-based diets in all areas of the globe: many regions rely on livestock for food security as they can be fed on plants that aren&amp;rsquo;t suitable for human consumption or graze on land that couldn&amp;rsquo;t support crops.&lt;sup id=&#34;fnref:12&#34;&gt;&lt;a href=&#34;#fn:12&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&#34;whats-your-impact&#34;&gt;What&amp;rsquo;s your impact?&lt;/h2&gt;
&lt;p&gt;By transitioning from a &lt;span id=&#34;current-diet&#34;&gt;high-meat&lt;/span&gt; to a &lt;span id=&#34;new-diet&#34;&gt;vegetarian&lt;/span&gt; diet, your carbon emissions could &lt;span id=&#34;diet-carbon-inc&#34; data-var=&#34;&#34; class=&#34;pos&#34;&gt;decrease&lt;/span&gt; by about &lt;span data-var=&#34;diet-carbon-diff&#34; id=&#34;diet-carbon-diff&#34; class=&#34;pos&#34;&gt;3.38&lt;/span&gt; kgCO2e per day (that&amp;rsquo;s &lt;span data-var=&#34;diet-carbon-diff-pc&#34;&gt;47&lt;/span&gt;% &lt;span data-var=&#34;diet-carbon-diff-pc-inc&#34;&gt;less&lt;/span&gt;). That&amp;rsquo;s roughly the equivalent of &lt;span data-var=&#34;diet-equiv&#34;&gt;eating 3,084 loaves of bread&lt;/span&gt; per year.&lt;/p&gt;
&lt;p&gt;If everyone in the UK aged 15-64 did the same thing, we could save about &lt;span data-var=&#34;diet-carbon-diff-uk&#34;&gt;38,605,845&lt;/span&gt; tonnes of CO2e per year. That&amp;rsquo;s roughly the equivalent of &lt;span data-var=&#34;diet-equiv-uk&#34;&gt;taking 8,392,575 return flights from the UK to Hong Kong&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;And don&amp;rsquo;t forget this just shows the estimated potential carbon savings, without taking into account the extra benefits that come with more land being available.&lt;/p&gt;
&lt;p&gt;&lt;a name=&#34;diet-explain&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;explaining-the-diet-calculations&#34;&gt;Explaining the diet calculations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;span data-var=&#34;diet-current-value&#34;&gt;high-meat&lt;/span&gt; diet in the UK equates to &lt;span data-var=&#34;diet-current-carbon-value&#34;&gt;7.19&lt;/span&gt; kgCO2e per day, or &lt;span data-var=&#34;diet-current-carbon-value-yearly&#34;&gt;2,624.35&lt;/span&gt; kgCO2e per year. A &lt;span data-var=&#34;diet-new-value&#34;&gt;vegetarian&lt;/span&gt; diet in the UK equates to &lt;span data-var=&#34;diet-new-carbon-value&#34;&gt;3.81&lt;/span&gt; kgCO2e per day, or &lt;span data-var=&#34;diet-new-carbon-value-yearly&#34;&gt;1,391&lt;/span&gt; kgCO2e per year.&lt;sup id=&#34;fnref:13&#34;&gt;&lt;a href=&#34;#fn:13&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;As a comparison, &lt;span data-var=&#34;diet-equiv-item&#34;&gt;loaves of bread&lt;/span&gt; equates to about &lt;span data-var=&#34;diet-equiv-value&#34;&gt;0.40&lt;/span&gt; kgCO2e, and &lt;span data-var=&#34;diet-equiv-item-uk&#34;&gt;1 return flight from the UK to Hong Kong&lt;/span&gt; equates to about &lt;span data-var=&#34;diet-equiv-value-uk&#34;&gt;4.60&lt;/span&gt; tonnes CO2e.&lt;sup id=&#34;fnref:14&#34;&gt;&lt;a href=&#34;#fn:14&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;There are an estimated 41,972,000 people (UK) in the 15-64 age bracket as of 2016.&lt;sup id=&#34;fnref:15&#34;&gt;&lt;a href=&#34;#fn:15&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;15&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Bajželj, Bojana, Keith S. Richards, Julian M. Allwood, Pete Smith, John S. Dennis, Elizabeth Curmi, and Christopher A. Gilligan. 2014. &lt;a href=&#34;http://sbc.ucdavis.edu/files/202364.pdf&#34;&gt;Importance Of Food-Demand Management For Climate Mitigation&lt;/a&gt; pp. 925-926&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Bailey, Rob, Antony Frogatt, and Laura Wellesley. 2014. &lt;a href=&#34;https://www.chathamhouse.org/sites/files/chathamhouse/field/field_document/20141203LivestockClimateChangeBaileyFroggattWellesley.pdf&#34;&gt;Livestock – Climate Change&amp;rsquo;s Forgotten Sector&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;Anhang, Jeff, Robert Goodland. 2009. &lt;a href=&#34;http://worldwatch.org/node/6294&#34;&gt;Livestock and Climate Change: What if the key actors in climate change are&amp;hellip;cows, pigs, and chickens?&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;Bajželj et al. 2014. pp. 925-926&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;Bailey et al. 2014.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:6&#34;&gt;
&lt;p&gt;Kissinger, Gabrielle, Martin Herold, Veronique De Sy. 2012. &lt;a href=&#34;http://www.somcon.com/sites/default/files/&#34;&gt;Drivers of Deforestation and Forest Degradation&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:7&#34;&gt;
&lt;p&gt;WWF. &lt;a href=&#34;http://wwf.panda.org/what_we_do/footprint/agriculture/soy/facts/&#34;&gt;&lt;em&gt;Soy is everywhere&lt;/em&gt;&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:7&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:8&#34;&gt;
&lt;p&gt;&amp;ldquo;The AFOLU sector is responsible for just under a quarter (~10–12 GtCO2eq/yr) of anthropogenic GHG emissions mainly from deforestation and agricultural emissions from livestock, soil and nutrient management.&amp;rdquo; IPCC. 2014. Climate Change 2014: &lt;a href=&#34;https://www.ipcc.ch/report/ar5/wg3/&#34;&gt;Agriculture, Forestry and Other Land Use&lt;/a&gt; p816&amp;#160;&lt;a href=&#34;#fnref:8&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:9&#34;&gt;
&lt;p&gt;Ravilios, Kate. 2014. &lt;a href=&#34;http://environmentalresearchweb.org/cws/article/news/54791/&#34;&gt;Cutting meat consumption could feed 10 billion&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:9&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:10&#34;&gt;
&lt;p&gt;Bailey et al. 2014.&amp;#160;&lt;a href=&#34;#fnref:10&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:11&#34;&gt;
&lt;p&gt;Bajželj et al. 2014.&amp;#160;&lt;a href=&#34;#fnref:11&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:12&#34;&gt;
&lt;p&gt;&amp;ldquo;livestock can be fed on plants not suitable for human consumption or growing on land with high soil carbon stocks not suitable for cropping; hence, food production by grazing animals contributes to food security in many regions of the world.&amp;rdquo; IPCC. 2014. Climate Change 2014: &lt;a href=&#34;https://www.ipcc.ch/report/ar5/wg3/&#34;&gt;Agriculture, Forestry and Other Land Use&lt;/a&gt; p838&amp;#160;&lt;a href=&#34;#fnref:12&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:13&#34;&gt;
&lt;p&gt;Scarborough, Peter, Paul N. Appleby, Anja Mizdrak, Adam D. M. Briggs, Ruth C. Travis, Kathryn E. Bradbury, and Timothy J. Key. 2014. &lt;a href=&#34;http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4372775/&#34;&gt;Dietary greenhouse gas emissions of meat-eaters, fish-eaters, vegetarians and vegans in the UK&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:13&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:14&#34;&gt;
&lt;p&gt;Berners-Lee, Mike. 2010. &lt;a href=&#34;http://www.hive.co.uk/Product/Mike-Berners-Lee/How-Bad-are-Bananas--The-Carbon-Footprint-of-Everything/7015602&#34;&gt;&lt;em&gt;How Bad Are Bananas?: The Carbon Footprint of Everything&lt;/em&gt;&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:14&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:15&#34;&gt;
&lt;p&gt;World Bank. &lt;a href=&#34;http://databank.worldbank.org/data/reports.aspx?source=Health%20Nutrition%20and%20Population%20Statistics:%20Population%20estimates%20and%20projections&#34;&gt;World DataBank&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:15&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 29 Jun 2016 00:00:00 UTC</pubDate>
        <link>https://joeray.me/the-environmental-case-for-eating-less-meat/</link>
        <guid isPermaLink="true">https://joeray.me/the-environmental-case-for-eating-less-meat/</guid>
    </item>
    
    
    
    <item>
        <title>GNU Parallel or How to list millions of S3 objects</title>
        <description>&lt;p&gt;&lt;a href=&#34;https://www.gnu.org/software/parallel/&#34;&gt;GNU Parallel&lt;/a&gt; is a great tool for parallelising command line tasks and the &lt;a href=&#34;https://aws.amazon.com/cli/&#34;&gt;AWS CLI&lt;/a&gt; is a great tool for interacting with S3.&lt;/p&gt;
&lt;p&gt;Put them together and you have everything you might need to list millions of objects in an S3 bucket efficiently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;: Listing a lot of objects in an S3 bucket takes a seriously long time when using &lt;code&gt;aws s3 ls&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt;: GNU parallel can parallelise the process&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say your bucket contains a set of log files named by date and you&amp;rsquo;ve followed the &lt;a href=&#34;https://aws.amazon.com/blogs/aws/amazon-s3-performance-tips-tricks-seattle-hiring-event/&#34;&gt;S3 performance tips&lt;/a&gt; by prefixing each key with a hexadecimal character.&lt;/p&gt;
&lt;p&gt;We can use &lt;code&gt;parallel&lt;/code&gt; to split up the object listing into separate tasks which can then run simultaneously:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ parallel aws s3api list-objects &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --bucket my-bucket &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --prefix &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What we&amp;rsquo;re doing here is performing 16 &lt;code&gt;list-objects&lt;/code&gt; commands, one for each of the hexadecimal digits. &lt;code&gt;s3api list-objects&lt;/code&gt; is more efficient than &lt;code&gt;s3 ls&lt;/code&gt; as it just does the raw API call and returns the result to you. The &lt;code&gt;{1}&lt;/code&gt; is a placeholder for each of the digits and we generate the digits using &lt;a href=&#34;https://www.linuxjournal.com/content/bash-brace-expansion&#34;&gt;brace expansion&lt;/a&gt;. We tell &lt;code&gt;parallel&lt;/code&gt; that the hexadecimal digits are arguments using the &lt;code&gt;:::&lt;/code&gt; syntax.&lt;/p&gt;
&lt;p&gt;If we had a two-character prefix, we could further parallelise the work by using the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ parallel aws s3api list-objects &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --bucket my-bucket &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --prefix &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;}{&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This uses the same technique as before but will now parallelise all combinations of the hexadecimal digits, producing commands like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix ab
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix ac
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix ad
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix ae
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix af
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a6
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a7
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix a9
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix ba
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;aws s3api list-objects --bucket my-bucket --prefix bb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You could combine this technique with the &lt;code&gt;jq&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt; commands to count the number of objects and sum the total size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ parallel aws s3api list-objects &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --bucket my-bucket &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --prefix &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;}{&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  jq &lt;span class=&#34;s1&#34;&gt;&amp;#39;.Contents[].Size&amp;#39;&lt;/span&gt; --raw-output &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ count += 1; size += $1 } END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;       { print &amp;#34;Count:&amp;#34;, count, &amp;#34;Size:&amp;#34;, size }&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or use parallel again to copy the objects locally:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;parallel aws s3api list-objects &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --bucket my-bucket &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --prefix &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;}{&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ::: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;a..f&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;0..9&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  jq &lt;span class=&#34;s1&#34;&gt;&amp;#39;.Contents[].Key&amp;#39;&lt;/span&gt; --raw-output &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  parallel -j20 aws s3 cp s3://my-bucket/&lt;span class=&#34;o&#34;&gt;{}&lt;/span&gt; .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we use the &lt;code&gt;-j&lt;/code&gt; flag to increase the level of parallelisation. By default, parallel will create as many parallel tasks as you have cores on your CPU but if your workload involves waiting for network IO (as in the case of accessing S3) then you can probably safely increase the level of parallelisation to speed things up.&lt;/p&gt;
</description>
        <pubDate>Tue, 24 May 2016 00:00:00 UTC</pubDate>
        <link>https://joeray.me/gnu-parallel-how-to-list-millions-objects/</link>
        <guid isPermaLink="true">https://joeray.me/gnu-parallel-how-to-list-millions-objects/</guid>
    </item>
    
    
    
    <item>
        <title>Analysing Oyster data dumps with Clojure</title>
        <description>&lt;p&gt;Having moved to London last December, I&amp;rsquo;m using my Oyster card a lot more than I used to. In March I discovered that if you&amp;rsquo;ve registered your Oyster card with TfL you can get them to send you CSV dumps of your usage. And what&amp;rsquo;s a programmer to do with CSV dumps if not analyse them somehow?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been interested in Clojure for a while and started learning it just before Christmas so it seemed a natural fit to write the analyser in Clojure.&lt;/p&gt;
&lt;p&gt;The result is a command line application which takes a set of the CSV files and outputs some figures like the average journey cost, total journey time etc. Here&amp;rsquo;s my output for the first three months of 2015:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; From 01 Jan 2015 to 31 Mar 2015
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Total Duration  21 hrs 2 mins
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     Avg. Duration        23 mins
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Shortest Journey        10 mins
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Longest Journey   1 hrs 6 mins
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        Total Cost        £213.99
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         Avg. Cost          £1.88
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          Journeys            114
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Most popular mode      bus (51%)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, depressingly I&amp;rsquo;ve spent almost a whole day on public transport since January and that only includes journeys that were timed – 51% of my journeys were by bus which don&amp;rsquo;t have end times!&lt;/p&gt;
&lt;p&gt;You can find the &lt;a href=&#34;https://github.com/joerayme/oyster-analyser&#34;&gt;code on GitHub&lt;/a&gt; – improvements and comments welcome.&lt;/p&gt;
&lt;h2 id=&#34;thoughts-on-clojure&#34;&gt;Thoughts on Clojure&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m really enjoying programming in Clojure. I&amp;rsquo;ve not done any functional programming before and the approach is making a lot of sense to me, especially in the context of the Alan Perlis quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://www.braveclojure.com/&#34;&gt;Clojure for the Brave and True&lt;/a&gt; has been an unusual introduction to the language, the &lt;a href=&#34;http://exercism.io/&#34;&gt;Exercism.io&lt;/a&gt; and &lt;a href=&#34;http://www.4clojure.com/&#34;&gt;4Clojure&lt;/a&gt; exercises are useful for practicing and I&amp;rsquo;ve used &lt;a href=&#34;https://jafingerhut.github.io/cheatsheet/grimoire/cheatsheet-tiptip-cdocs-summary.html&#34;&gt;jafingerhut&amp;rsquo;s Grimoire cheatsheet&lt;/a&gt; a lot for reference.&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2015 00:00:00 UTC</pubDate>
        <link>https://joeray.me/analysing-oyster-data-dumps-with-clojure/</link>
        <guid isPermaLink="true">https://joeray.me/analysing-oyster-data-dumps-with-clojure/</guid>
    </item>
    
    
    
    <item>
        <title>Inclusivity and devops</title>
        <description>&lt;p&gt;One of the themes that came out of the recent devopsdays Ghent conference was empathy, and Bridget Kromhout wrote afterwards about making the first rule of devops, &lt;a href=&#34;http://bridgetkromhout.github.io/blog/2014/11/03/the-first-rule-of-devops-club&#34;&gt;&amp;lsquo;welcome to the club&amp;rsquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Inclusivity is something I&amp;rsquo;ve been increasingly thinking about over the last couple of years, especially with regards to women and minorities in tech. I have just about all kinds of privilege going for me – white, heterosexual, university educated male from a nuclear family with a middle-class background and supportive, open parents – and so understanding the difficulties that others face when trying to enter and thrive in the industry has been a challenge.&lt;/p&gt;
&lt;p&gt;This post is not an attempt to provide solutions to the problems facing women and minorities in tech; I wouldn&amp;rsquo;t presume to know better than the people it actually affects and inclusivity is only one part of the problem in any case. Rather it is a synthesis of the thoughts I&amp;rsquo;ve had around inclusivity and the people and posts that have inspired me. Making tech an inclusive space is incredibly important to me and I hope to show you why.&lt;/p&gt;
&lt;p&gt;Many of the examples in this post are about gender as that is what I have been mostly reading about recently. However, my desire for inclusivity extends to everyone, not just women, as I hope you&amp;rsquo;ll see.&lt;/p&gt;
&lt;h2 id=&#34;why-should-we-care-about-inclusivity&#34;&gt;Why should we care about inclusivity?&lt;/h2&gt;
&lt;p&gt;Before talking about inclusivity, I&amp;rsquo;d like to talk about diversity.&lt;/p&gt;
&lt;p&gt;If you have a homogeneous, white/Asian male workforce (and &lt;a href=&#34;https://docs.google.com/spreadsheet/ccc?key=0AlZH8QBl60oodEJTdFA5TlZOcDJCMU02RkZoSHF5SHc#gid=0&#34;&gt;statistically you do&lt;/a&gt;, at least in the more technical roles) then your teams will all have had similar experiences, they&amp;rsquo;ll likely lead similar lives, have had a similar education. Whilst this makes collaboration easy, the flip side is that we all think in similar ways and so come up with similar solutions.&lt;/p&gt;
&lt;p&gt;By introducing people who don&amp;rsquo;t fit the mould you introduce new ideas, new ways of approaching problems and thus improve the quality of your work. Research has shown that &lt;a href=&#34;http://web.mit.edu/cortiz/www/Diversity/PDFs/Jehn%20et%20al%201999.pdf&#34;&gt;a more socially diverse environment increases performance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But quite apart from the benefits for the organisation, I just don&amp;rsquo;t want to work in a homogeneous environment: it&amp;rsquo;s boring. I want to speak with people not like me, hear their stories and learn about their lives. Surrounding ourselves with people that are different from us enriches our own lives as well as producing better outcomes for everyone else.&lt;/p&gt;
&lt;p&gt;Of course, introducing people who don&amp;rsquo;t fit in can be painful, as illustrated by Erica Joy Baker in her recent post &lt;a href=&#34;https://medium.com/@ericajoy/the-other-side-of-diversity-1bb3de2f053e&#34;&gt;&lt;em&gt;The other side of diversity&lt;/em&gt;&lt;/a&gt;. This is supported by research, and not only that cited in her article: whilst a more socially diverse environment was shown to increase performance, the same study also showed it increased relationship conflict.&lt;/p&gt;
&lt;p&gt;The reason posited for the increase in relationship conflict comes from social identity theory: “group members establish positive social identity and confirm affiliation by showing favouritism to members of their own social category.”&lt;/p&gt;
&lt;p&gt;For me, this is where inclusivity comes in. By creating an inclusive environment where individuals are accepting of others, and by creating a communal social identity which is inherently inclusive, hopefully we can decrease the risk of conflict.&lt;/p&gt;
&lt;p&gt;I also want to work in an environment where acceptance is the norm. Inclusivity is so often associated with gender, race or sexuality, and those things are all incredibly important, but it&amp;rsquo;s also about the smaller things such as understanding that &lt;a href=&#34;https://modelviewculture.com/pieces/alcohol-and-inclusivity-planning-tech-events-with-non-alcoholic-options&#34;&gt;not everyone wants to drink alcohol&lt;/a&gt; – people are different from us in many different ways, and that&amp;rsquo;s OK; we can still share things with them.&lt;/p&gt;
&lt;p&gt;We have a habit in the tech community of creating separations between us based on perceived knowledge gaps, where we judge and exclude others because they&amp;rsquo;re too nerdy or not nerdy enough. I really want to break down this &amp;rsquo;nerd hierarchy&amp;rsquo; where someone more or less &amp;lsquo;beardy&amp;rsquo; than you is not seen as &amp;lsquo;other&amp;rsquo; (and while we&amp;rsquo;re at it, can we stop using beard length as a measure of nerdiness please?) so that we can work together, respecting everyone&amp;rsquo;s skill level.&lt;/p&gt;
&lt;h2 id=&#34;whats-it-got-to-do-with-devops&#34;&gt;What&amp;rsquo;s it got to do with devops?&lt;/h2&gt;
&lt;p&gt;For me, devops is fundamentally about collaboration and human interaction. The movement attempts to address the problems of separate teams working towards shared goals (in our case, producing quality, functional software that is easily deployed and scaled) and so, whilst the tools associated with devops help us achieve some of that, our primary concern should be culture, not technology.&lt;/p&gt;
&lt;p&gt;The classic situation that devops hopes to address is the organisation that has separate development and operations teams where the development team rarely takes responsibility for their software once it is deployed in production, &amp;rsquo;throwing their software over the wall&amp;rsquo; for the operations team to deal with. This problem won&amp;rsquo;t be solved by throwing a configuration management tool at it.&lt;/p&gt;
&lt;p&gt;Given devops culture depends so heavily on the ability of individuals to collaborate, and if we want a diverse workforce (which hopefully you&amp;rsquo;re convinced we do), then we need to find ways to minimise conflict.&lt;/p&gt;
&lt;p&gt;One of the ways in which we can achieve an improved culture of collaboration is to increase the quality of our communication. For me, allowing people the ability to express themselves free from constraints (in a constructive, respectful manner of course) is key and so building an environment where team members feel comfortable is crucial. Creating a culture where everyone is welcome goes a long way towards this and, as Bridget stated, this should extend not only to the development and operations teams but also to the other people we work with to produce software.&lt;/p&gt;
&lt;h2 id=&#34;but-im-already-inclusive&#34;&gt;But I&amp;rsquo;m already inclusive!&lt;/h2&gt;
&lt;p&gt;The short answer to this is that even if you think you are, you&amp;rsquo;re probably not, at least not entirely.&lt;/p&gt;
&lt;p&gt;When attending an interview recently, I was talking with my interviewer about inclusivity. The company had recently moved offices and my interviewer mentioned that the landlord had installed a sign on the unisex toilets with a man and a woman on the front. In order to be more inclusive of transgender people the company had requested the sign be removed, which would have never occurred to me before now.&lt;/p&gt;
&lt;p&gt;The problem is that we are shaped by the world around us and we adopt the traits and biases of the culture we grow up in. Take gender bias, for example. There is no country in the world that has achieved perfect gender equality (the World Economic Forum &lt;a href=&#34;http://reports.weforum.org/global-gender-gap-report-2014/rankings/&#34;&gt;puts Iceland at the front&lt;/a&gt; and according to the index they&amp;rsquo;re still 14 percent off total equality) and so we are all influenced by stereotypically associating men with breadwinning work and women with childcare.&lt;/p&gt;
&lt;p&gt;Even if we work to redress this imbalance in our conscious thoughts, our actions are still influenced by our unconscious biases and we can often do things which exclude people without thinking.&lt;/p&gt;
&lt;p&gt;There is plenty of anecdotal evidence to suggest unconscious (or perhaps, sadly, even conscious) bias when hiring. Take the post describing a &lt;a href=&#34;http://selfuni.wordpress.com/2013/12/29/unemployed-black-woman-pretends-to-be-white-job-offers-suddenly-skyrocket/&#34;&gt;black woman who pretended to be white&lt;/a&gt;, resulting in a marked increase in job offers.&lt;/p&gt;
&lt;p&gt;Research backs this up, both for &lt;a href=&#34;http://www.povertyactionlab.org/evaluation/discrimination-job-market-united-states&#34;&gt;racial discrimination&lt;/a&gt; and &lt;a href=&#34;http://www.cos.gatech.edu/facultyres/Diversity_Studies/Steinpreis_Impact%20of%20gender%20on%20review.pdf&#34;&gt;gender discrimination&lt;/a&gt;. Given two CVs of fictional candidates for an academic job which differ only in the name (Dr. Karen Miller vs. Dr. Brian Miller), the male candidate is either perceived as more hireable than the female candidate or reviewers are four times more likely to express reservations about the female candidate even if they think she has the required expertise.&lt;/p&gt;
&lt;h2 id=&#34;so-what-do-we-do&#34;&gt;So what do we do?&lt;/h2&gt;
&lt;p&gt;Firstly, I think it&amp;rsquo;s really important for people in a position of privilege to acknowledge this and constantly challenge it. I do this by having great people around me who inspire me and call me out on my speech and actions (thanks sis!) and by reading as much as I can. For example, for more information on the research into gender differences, I can&amp;rsquo;t recommend Cordelia Fine&amp;rsquo;s &lt;a href=&#34;http://www.amazon.co.uk/gp/product/1848312202/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1634&amp;amp;creative=6738&amp;amp;creativeASIN=1848312202&amp;amp;linkCode=as2&amp;amp;tag=joerayme-21&amp;amp;linkId=QTUMKYFCEHUZYUA7&#34;&gt;&lt;em&gt;Delusions of Gender&lt;/em&gt;&lt;/a&gt; enough.&lt;/p&gt;
&lt;p&gt;I know I said I wouldn&amp;rsquo;t propose solutions but it would be silly of me not to at least point you in the direction of other people with suggestions. A lot of this is related to gender, as that&amp;rsquo;s been my main focus for my reading, but some of it equally applies to including other groups and individuals.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you&amp;rsquo;re a man, read about &lt;a href=&#34;http://www.ncwit.org/resources/read-online-maleadvocate&#34;&gt;ways to be a male advocate for women&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Be aware of &lt;a href=&#34;http://www.psychologytoday.com/blog/microaggressions-in-everyday-life/201011/microaggressions-more-just-race&#34;&gt;microaggressions&lt;/a&gt; and what effect they can have.&lt;/li&gt;
&lt;li&gt;If you&amp;rsquo;re in a position where you&amp;rsquo;re hiring people, learn about how to overcome unconscious bias by, for example, watching &lt;a href=&#34;http://www.youtube.com/watch?v=nLjFTHTgEVU&#34;&gt;how Google is trying to do that in their hiring process&lt;/a&gt; or consider &lt;a href=&#34;http://www.brw.com.au/p/leadership/taking_quotas_tokenism_why_some_TbHMUch7hmTuWLFyCq5ocO?mkt_tok=3RkMMJWWfF9wsRokvqrJZKXonjHpfsX56%2BUsXKexlMI%2F0ER3fOvrPUfGjI4DScpkI%2BSLDwEYGJlv6SgFS7nMMbFk37gPUhA%3D&#34;&gt;implementing a quota system&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Think about the parts of your culture that might not be inclusive and read &lt;a href=&#34;https://modelviewculture.com/&#34;&gt;Model View Culture&lt;/a&gt;, a great resource on diversity and inclusivity issues.&lt;/li&gt;
&lt;li&gt;Examine the language you use and consider avoiding terms that are not inclusive, such as addressing a mixed-gender group as &amp;lsquo;guys&amp;rsquo;.&lt;/li&gt;
&lt;li&gt;Take lessons from conferences that have successfully implemented a code of conduct.&lt;/li&gt;
&lt;li&gt;Try and &lt;a href=&#34;https://medium.com/@ericajoy/no-solution-e55af3bd4d1a&#34;&gt;empathise with others more&lt;/a&gt;, even if that takes you to uncomfortable places.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 01 Dec 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/inclusivity-and-devops/</link>
        <guid isPermaLink="true">https://joeray.me/inclusivity-and-devops/</guid>
    </item>
    
    
    
    <item>
        <title>Puppet: &#39;could not find class&#39; and metadata.json</title>
        <description>&lt;p&gt;This week, when writing some custom Puppet modules, I was getting the following error when trying to use them:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Could not find class &amp;lt;class&amp;gt; for &amp;lt;node&amp;gt; on &amp;lt;node&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I could not for the life of me figure out what the problem was – the module was in the correct place, with the correct name and layout.&lt;/p&gt;
&lt;p&gt;I finally realised it was a problem with the &lt;code&gt;metadata.json&lt;/code&gt; file in the root of the module&amp;rsquo;s directory. It turns out that if there&amp;rsquo;s a syntax error in this file, Puppet will refuse to load the module without letting you know why. And it&amp;rsquo;s a &lt;a href=&#34;http://projects.puppetlabs.com/issues/20728&#34;&gt;known bug&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my case it wasn&amp;rsquo;t even a syntax error that was the problem – it was caused by one of the required fields being missing from the manifest. For reference, the &lt;a href=&#34;https://docs.puppetlabs.com/puppet/latest/reference/modules_publishing.html#fields-in-metadatajson&#34;&gt;PuppetLabs docs&lt;/a&gt; specify the required fields for the metadata.json file.&lt;/p&gt;
</description>
        <pubDate>Fri, 10 Oct 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/puppet-could-not-find-class/</link>
        <guid isPermaLink="true">https://joeray.me/puppet-could-not-find-class/</guid>
    </item>
    
    
    
    <item>
        <title>Ten months travelling: UK to India overland by train</title>
        <description>&lt;p&gt;&lt;em&gt;On 18th October 2013 I set off on an overland adventure, travelling from my home town of Frome, UK all the way to Kerala in the south of India. Here&amp;rsquo;s how I did it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Prior to experiencing the post-university world of jobs and houses and responsibility I&amp;rsquo;d never really had the travel bug. My younger sister interrailed throughout Europe in her gap year before university; I did the ‘sensible’ thing and got a job.&lt;/p&gt;
&lt;p&gt;Although enjoying my fledgeling career, thoughts of travel started to surface. I wanted to see totally different cultures and, inspired by my parents&amp;rsquo; own travels, I started setting my sights on India.&lt;/p&gt;
&lt;p&gt;I like to think of myself as environmentally conscious and so I was fairly set on not flying – train travel can reduce the carbon emissions of a trip &lt;a href=&#34;http://www.theguardian.com/environment/2010/apr/06/aviation-q-and-a&#34;&gt;by a factor of 5 to 10&lt;/a&gt; compared to flying. I occasionally perused &lt;a href=&#34;http://seat61.com/India-overland.htm&#34;&gt;Seat61&amp;rsquo;s UK to India Overland&lt;/a&gt; page with nothing more than vague plans about how I would get there.&lt;/p&gt;
&lt;p&gt;But one evening in August 2013, personal circumstances led me to decide that now was the time. The next day I handed in my notice and foolishly decided I needed only six weeks to plan the trip that would take me out of Europe for the first time in my life.&lt;/p&gt;
&lt;h2 id=&#34;the-route&#34;&gt;The Route&lt;/h2&gt;
&lt;p&gt;The Seat61 site suggested two possible routes. One, detailed in the UK to India Overland page linked to above, went via Turkey, Iran and Pakistan; the other via Russia, China and Tibet.&lt;/p&gt;
&lt;p&gt;I agonised over the choice as well as briefly considering flying, given the considerable challenges of both routes. Both offered adventure but both also came with difficulties: the first, getting through Iran and Pakistan overland and dealing with potential security issues; the second, being able to get into Tibet at all given the complicated political situation there.&lt;/p&gt;
&lt;div class=&#34;map&#34;&gt;&lt;/div&gt;
&lt;p&gt;In the end I decided that the Russia/China/Tibet option was the one for me – I figured that getting from the UK to Beijing wouldn&amp;rsquo;t be too challenging and if all else failed I could just get a flight from China to Kathmandu to skip out Tibet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://joeray.me/i/travel/trans-mongolian.jpg&#34; alt=&#34;The Trans-Mongolian Express Train rounding a corner in Mongolia&#34;&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d also always been interested in taking the Trans-Siberian train and this would be my opportunity.&lt;/p&gt;
&lt;h2 id=&#34;the-details&#34;&gt;The Details&lt;/h2&gt;
&lt;h3 id=&#34;tickets&#34;&gt;Tickets&lt;/h3&gt;
&lt;p&gt;Because of the number of changes involved, and my desire to get to Tibet and Nepal as soon as possible to avoid visiting in the harsh winter, I decided I would book as much of my trip as I could in advance (preferably to Nepal).&lt;/p&gt;
&lt;p&gt;Using a &lt;a href=&#34;https://docs.google.com/spreadsheets/d/1xCTT2bVXojLSpxW9P1W0QEaC3jsKBB-Qs2e2tcvPShs/edit?usp=sharing&#34;&gt;spreadsheet&lt;/a&gt; I planned out each train I&amp;rsquo;d need to take and booked them in three separate segments: London to Frankfurt, Frankfurt to Moscow (both with RailEurope, now &lt;a href=&#34;http://en.voyages-sncf.com/&#34;&gt;Voyages SNCF&lt;/a&gt;) and Moscow to Beijing (with &lt;a href=&#34;http://realrussia.co.uk/&#34;&gt;Real Russia&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;visas&#34;&gt;Visas&lt;/h3&gt;
&lt;p&gt;Going through so many countries, I needed to apply for my visas pretty quickly. I did not think this through before deciding when to leave and I would not recommend applying for five visas (Belarus, Russia, Mongolia, China and India) with only six weeks to go.&lt;/p&gt;
&lt;p&gt;Fortunately I had just enough time. I applied for the India visa independently first (before I had made my mind up on my route), then applied for the rest through Real Russia who were incredibly helpful and got my passport back to me with one day to go before leaving.&lt;/p&gt;
&lt;p&gt;The only downside was that it cost me much more than it otherwise would have done, both to get the visas processed through Real Russia instead of doing it myself and to get some of them fast-tracked so I would receive them in time.&lt;/p&gt;
&lt;p&gt;The visa for Nepal was simple for me: just turn up at the border with 40 USD (or a friend who has it – thanks Sara!) and pay the border guards.&lt;/p&gt;
&lt;h3 id=&#34;tibet&#34;&gt;Tibet&lt;/h3&gt;
&lt;p&gt;This was the trickiest part of the planning. Once I&amp;rsquo;d decided on the route I started researching tour companies. Due to travel restrictions, foreigners can only enter Tibet as part of an organised tour. I then started sending out a few enquiries. I also posted on the &lt;a href=&#34;https://www.lonelyplanet.com/thorntree/forums/asia-north-east-asia/tibet&#34;&gt;Tibet section of the Lonely Planet forum&lt;/a&gt;, asking whether anyone would be willing to join me.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://joeray.me/i/travel/tibet.jpg&#34; alt=&#34;Sculptures on a Tibetan monastery&#34;&gt;&lt;/p&gt;
&lt;p&gt;Luckily I was able to find two travelling companions interested in doing the same trip as me (Lhasa to Nepal) and with less than 48 hours before I left home, we requested our tour with &lt;a href=&#34;http://snowliontours.com/&#34;&gt;Snow Lion Tours&lt;/a&gt;. This Tibetan-owned operator had come highly recommended by other travellers and having now travelled with them I would also not hesitate to recommend them.&lt;/p&gt;
&lt;p&gt;However, I couldn&amp;rsquo;t pay my deposit until I got to Moscow and even then I wasn&amp;rsquo;t 100% sure it would all go through. I finally received confirmation half-way through my Trans-Mongolian journey. Phew!&lt;/p&gt;
&lt;h3 id=&#34;the-timings&#34;&gt;The Timings&lt;/h3&gt;
&lt;p&gt;All-in-all, travelling from Frome, UK to Cochin, Kerala, India took me about five months. That includes three months around India and a month in Nepal.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;London to Moscow took the best part of two days.&lt;/li&gt;
&lt;li&gt;The Trans-Mongolian took six and a half days.&lt;/li&gt;
&lt;li&gt;I spent seven days in and around Beijing.&lt;/li&gt;
&lt;li&gt;Beijing to Lhasa took less than 24 hours.&lt;/li&gt;
&lt;li&gt;Our Tibet tour was nine days.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;the-costs&#34;&gt;The Costs&lt;/h3&gt;
&lt;p&gt;Well, it certainly would have been a lot cheaper to fly directly to India, but where would the fun have been in that?!&lt;/p&gt;
&lt;p&gt;My &lt;a href=&#34;https://docs.google.com/spreadsheets/d/1xCTT2bVXojLSpxW9P1W0QEaC3jsKBB-Qs2e2tcvPShs/edit?usp=sharing&#34;&gt;spreadsheet&lt;/a&gt; details the ticket costs which totalled £856 to Beijing. In addition I spent about £600 on the visas and ¥5730 (approx £560) for the Tibet tour. As I said before, the visas would have been cheaper had I started the application process sooner and the tickets would have undoubtedly also been cheaper if booked further in advance.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re thinking of taking any trains from Moscow east and fancy taking a bit of a risk, you can buy your tickets at the station in Moscow for a fraction of the cost you get them for through an agency. However, don&amp;rsquo;t expect the process to be straightforward (I spoke to a couple on the train who did this and it took them several attempts), or that the tickets you want will necessarily be available.&lt;/p&gt;
&lt;h2 id=&#34;the-reality&#34;&gt;The Reality&lt;/h2&gt;
&lt;p&gt;In short, I had an amazing time. The Trans-Siberian was fantastic, partly due to the experience and the scenery and partly due to my fellow passengers, China and in particular Tibet were eye-opening and Nepal was the highlight of my trip.&lt;/p&gt;
&lt;p&gt;In fact, I had such a great time that once I got to India I decided I didn&amp;rsquo;t want to stop so I flew to Australia (I agonised over this decision and spent a long time trying to figure out how to get there overland; alas, it was just too difficult) and spent five months there and in New Zealand working and travelling (but mostly travelling).&lt;/p&gt;
</description>
        <pubDate>Fri, 16 May 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/ten-months-travelling-uk-to-india-overland/</link>
        <guid isPermaLink="true">https://joeray.me/ten-months-travelling-uk-to-india-overland/</guid>
    </item>
    
    
    
    <item>
        <title>Scratching an itch: why I like programming</title>
        <description>&lt;p&gt;&lt;a href=&#34;https://xkcd.com/1319/&#34;&gt;&lt;img src=&#34;http://imgs.xkcd.com/comics/automation.png&#34; alt=&#34;Automation&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Indian train system is amazing considering the number of passengers it hauls around the country but it can be frustrating as a foreigner trying to book tickets, especially at the last minute.&lt;/p&gt;
&lt;p&gt;Certain trains have tickets put aside for foreign tourists. This is fantastically useful given that most trains sell out well in advance but discovering which trains have foreign tourist quota tickets available is problematic as online booking agents don&amp;rsquo;t have the information – you can only buy them at stations. The process invariably involves the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href=&#34;http://www.cleartrip.com/trains&#34;&gt;Cleartrip&lt;/a&gt; site.&lt;/li&gt;
&lt;li&gt;Enter journey details and submit.&lt;/li&gt;
&lt;li&gt;Discover no trains run between the two chosen stations (Cleartrip only searches direct routes).&lt;/li&gt;
&lt;li&gt;Repeat step 2 until you get some results.&lt;/li&gt;
&lt;li&gt;Note down the train number and figure out the short codes for the departing and arriving stations.&lt;/li&gt;
&lt;li&gt;Navigate to the seat availability form on the &lt;a href=&#34;http://indianrail.gov.in/&#34;&gt;Indian Rail website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enter journey details and submit.&lt;/li&gt;
&lt;li&gt;Find you&amp;rsquo;ve entered something wrong – very likely given how exacting the form is.&lt;/li&gt;
&lt;li&gt;Go back, amend details and submit form.&lt;/li&gt;
&lt;li&gt;Find the site has reset the date of travel you put in.&lt;/li&gt;
&lt;li&gt;Repeat step 9.&lt;/li&gt;
&lt;li&gt;Repeat steps 8 through 11 once or twice.&lt;/li&gt;
&lt;li&gt;No foreign tourist quota tickets for this train.&lt;/li&gt;
&lt;li&gt;Go back to step 5.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If there are 20 trains on a given day for your route, checking each one manually becomes unfeasible.&lt;/p&gt;
&lt;p&gt;Instead, I wrote a &lt;a href=&#34;http://www.greasespot.net/&#34;&gt;Greasemonkey&lt;/a&gt; script that automates the Indian Rail website portion of the process and reports the results right in my Cleartrip results page. What might have taken thirty minutes for each journey (and as I have no firm travel plans I&amp;rsquo;ve been checking a lot of journeys) now takes seconds. Itch scratched.&lt;/p&gt;
&lt;p&gt;Another little automation project I recently completed was prompted by one of my guesthouses&amp;rsquo; ISPs. To use the WiFi you were first required to log into a web page. Unfortunately, unless you kept the login page open, the session would frequently time out requiring another visit to the login page. Rather than doing this manually every time I discover I&amp;rsquo;d been logged out, I wrote a simple Python script to perform the task for me.&lt;/p&gt;
&lt;p&gt;All it does is mimics the keepalive request that the login page performs once you&amp;rsquo;ve logged in. If the script detects the session has timed out anyway, it posts the login details to the ISP. This may not sound like a big win but when you&amp;rsquo;re working all day and the session is timing out several times an hour, it saved me going prematurely bald.&lt;/p&gt;
&lt;p&gt;These two scripts only took me a couple of enjoyable hours to build, taught me more about the languages and domains I wrote them in and, most importantly, unlike the experience in the above XKCD comic, have saved me a lot of time.&lt;/p&gt;
&lt;h2 id=&#34;teaching-the-world-to-scratch-their-itches&#34;&gt;Teaching the world to scratch their itches&lt;/h2&gt;
&lt;p&gt;A lot has been said of late about the value of teaching schoolchildren to code as the UK Government launched its &lt;a href=&#34;http://www.yearofcode.org/&#34;&gt;&lt;em&gt;Year of Code&lt;/em&gt;&lt;/a&gt; initiative earlier this year, although not without &lt;a href=&#34;http://www.youtube.com/watch?v=-7x7GYItzS4#t=32&#34;&gt;some controversy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the reasons I love being a programmer is having the ability to solve problems by bashing out a few lines of code. Not only does this make my life easier but I get to enjoy the process as well.&lt;/p&gt;
&lt;p&gt;My skills mean I&amp;rsquo;m able to recognise and understand a problem and come up with the solution, not just because I know a handful of programming languages (although that obviously helps) but because of the knowledge programming has given me about the world of technology and how it works.&lt;/p&gt;
&lt;p&gt;My hope for the Year of Code is not that a whole generation decides they want to become the next Zuckerburg but that youth are given the tools to understand the technology that the world is increasingly reliant on and be able to make it serve their needs.&lt;/p&gt;
&lt;p&gt;If that also results in someone being more likely to create the next Facebook, so much the better.&lt;/p&gt;
</description>
        <pubDate>Thu, 01 May 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/scratching-an-itch/</link>
        <guid isPermaLink="true">https://joeray.me/scratching-an-itch/</guid>
    </item>
    
    
    
    <item>
        <title>Why TDD isn&#39;t dead</title>
        <description>&lt;p&gt;Rails creator David Heinemeier Hansson (DHH) recently wrote &lt;a href=&#34;http://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html&#34;&gt;a post denouncing TDD&lt;/a&gt;. Cue mass outrage/support from the web. I&amp;rsquo;m a bit of a TDD convert so I wanted to put my thoughts down on why that was.&lt;/p&gt;
&lt;p&gt;DHH seems to conflate unit testing and TDD and ends up hating on them both so I&amp;rsquo;ll address each topic separately. Sure, the emphasis with TDD is generally on testing the unit because that&amp;rsquo;s the focus of your short-term efforts but you don&amp;rsquo;t have to be limited by that – I sometimes use TDD to create system/integration tests before I start writing the underlying units.&lt;/p&gt;
&lt;h2 id=&#34;unit-testing&#34;&gt;Unit testing&lt;/h2&gt;
&lt;h3 id=&#34;encourages-single-responsibility-in-your-design&#34;&gt;Encourages Single Responsibility in your design&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re writing your unit tests properly, each unit is tested in isolation from the other units it interacts with. This encourages the developer to design with the &lt;a href=&#34;http://en.wikipedia.org/wiki/Single_responsibility_principle&#34;&gt;Single Responsibility principle&lt;/a&gt; in mind – each unit only being responsible for one aspect of the system.&lt;/p&gt;
&lt;p&gt;This seems to be DHH&amp;rsquo;s main argument against TDD, that it somehow leads to an overly complex architecture where you have a huge set of objects just to enable you to test each one in isolation. Whilst I can sympathise with this somewhat I think there&amp;rsquo;s a tradeoff to be made.&lt;/p&gt;
&lt;p&gt;For me, the advantages of having unit-level test coverage generally outweigh the disadvantage of introducing a few extra objects. And in my experience it is just a few; most separation of functionality into units is not merely contrived for the purposes of testing if following the Single Responsibility principle correctly.&lt;/p&gt;
&lt;h3 id=&#34;pinpoints-a-problems-source&#34;&gt;Pinpoints a problem&amp;rsquo;s source&lt;/h3&gt;
&lt;p&gt;By testing each unit in isolation it&amp;rsquo;s much easier to pinpoint the source of a problem – you should have a failing test which tells you the rough, if not exact, point in your unit which is misbehaving. With a system-level test you just know what the problem is, not where it resides.&lt;/p&gt;
&lt;p&gt;Also if you&amp;rsquo;re correctly isolating your units then the unit tests you write will be testing your code and your code alone. This means that when a failing unit test occurs, you can rule out any bugs with collaborators (such as framework code).&lt;/p&gt;
&lt;h3 id=&#34;makes-unit-level-refactoring-a-breeze&#34;&gt;Makes unit-level refactoring a breeze&lt;/h3&gt;
&lt;p&gt;Once you&amp;rsquo;ve got a good level of coverage for a unit&amp;rsquo;s public interface, you can easily change the underlying implementation of the unit without worrying about affecting other units relying on that interface. If all your unit tests pass, you have a good degree of confidence that your refactor was successful.&lt;/p&gt;
&lt;h3 id=&#34;makes-for-reliably-reproducible-tests&#34;&gt;Makes for reliably reproducible tests&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re isolating your units away from other systems – a database, the filesystem etc. – you can rely on the fact that your test will run the same every time. If you have tests which do interact with an external system then you have to have a way of controlling their state, else you can&amp;rsquo;t ensure that they won&amp;rsquo;t give your system under test inconsistent responses. Furthermore, by having control over collaborators you can ensure your unit handles edge-cases such as network timeouts correctly.&lt;/p&gt;
&lt;p&gt;This also has the advantage of tests not affecting persistent state systems such as an external API – you don&amp;rsquo;t want your tests asking MailChimp to send a campaign to your customers every time they run!&lt;/p&gt;
&lt;h2 id=&#34;test-first&#34;&gt;Test-first&lt;/h2&gt;
&lt;h3 id=&#34;tests-actually-get-written&#34;&gt;Tests actually get written&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s face it, despite lots of tooling, literature and evidence that automated testing is a Good Thing™, the web development industry isn&amp;rsquo;t particularly good at writing tests. I&amp;rsquo;ve seen tonnes of production code from many different projects that for various reasons didn&amp;rsquo;t have any automated test coverage whatsoever. If you encourage a test-first mentality it&amp;rsquo;s harder to let the tests slide.&lt;/p&gt;
&lt;h3 id=&#34;you-get-to-check-your-code-works-before-integrating&#34;&gt;You get to check your code works before integrating&lt;/h3&gt;
&lt;p&gt;By writing the tests first, you get to test your code actually works before you even make it interface with anything else. This works both at unit-level and higher levels: I recently used a test-first approach to write integration tests for an API that didn&amp;rsquo;t yet have a consumer. I was able to write the API to a specification and check it matched that specification without writing the client.&lt;/p&gt;
&lt;h2 id=&#34;a-bit-of-pragmatism&#34;&gt;A bit of pragmatism&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s not a good idea to adopt TDD, blanket mocking or any other technique blindly without analysing why you&amp;rsquo;re using it and how it affects the code you write. Having a test-first attitude can lead you to create wasteful tests just to ensure every line you code works (something I&amp;rsquo;ve definitely been guilty of), just as wanting perfectly isolated unit-level coverage can lead to an overly complex system design.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s needed with any approach to testing is pragmatism – as Gary Bernhardt says in his reaction piece &lt;a href=&#34;https://www.destroyallsoftware.com/blog/2014/tdd-straw-men-and-rhetoric&#34;&gt;TDD, Straw Men, and Rhetoric&lt;/a&gt;, “TDD is useful and test isolation is useful, but they both involve making trade-offs.” Let&amp;rsquo;s not discard unit testing or TDD simply because they can be harmful or wasteful when used in extremis.&lt;/p&gt;
</description>
        <pubDate>Wed, 19 Feb 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/why-tdd-isnt-dead/</link>
        <guid isPermaLink="true">https://joeray.me/why-tdd-isnt-dead/</guid>
    </item>
    
    
    
    <item>
        <title>Framework scalability</title>
        <description>&lt;p&gt;Whenever I start a project with a new tool or framework I spend a lot of time seeking out its best practices, especially with regards to code organisation. I want to make sure that the habits I adopt early in the project won&amp;rsquo;t come back to bite me later on as the project grows to avoid refactoring swathes of code.&lt;/p&gt;
&lt;p&gt;So when I started looking into &lt;a href=&#34;http://angularjs.org&#34;&gt;Angular.js&lt;/a&gt;, I did the same thing. As it&amp;rsquo;s a Google-backed project and is &lt;a href=&#34;http://www.google.com/trends/explore#q=angular%20js%2C%20backbone%20js%2C%20knockout%20js%2C%20ember%20js&amp;amp;date=1%2F2011%2037m&amp;amp;cmpt=q&#34;&gt;one of the more popular Javascript libraries&lt;/a&gt; of its ilk in the development community I expected to find it easy to discover how to scale an Angular application – after all, there should be plenty of people who have developed large projects with it.&lt;/p&gt;
&lt;p&gt;However, as I went through &lt;a href=&#34;http://docs.angularjs.org/tutorial&#34;&gt;the tutorial&lt;/a&gt; and searched for example projects it became apparent that this would be harder than I expected. Even to my beginner&amp;rsquo;s eye, it was obvious the code in the tutorial would not scale – it creates single files for each &amp;rsquo;type&amp;rsquo; (controllers, services etc.) in the application and each one is referenced individually in the HTML file.&lt;/p&gt;
&lt;p&gt;I knew that even a modest application with more than a few pages would likely require several of each type and that organising each one into a separate file would make maintenance much simpler. By doing this, referencing them manually in the HTML quickly becomes unfeasible as you have lots of script tags to maintain and reduced client performance when loading the application.&lt;/p&gt;
&lt;p&gt;And yet I could not find a project template which addressed these concerns and made it simple to start building:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/angular/angular-seed&#34;&gt;angular-seed&lt;/a&gt;, the official project bootstrap, has the same problems&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/angular-app/angular-app&#34;&gt;angular-app&lt;/a&gt; is a step in the right direction but lacks documentation on how it works rather than just how to run it and would need lots of work to get a new project up and running&lt;/li&gt;
&lt;li&gt;Whilst more what I was looking for, I found &lt;a href=&#34;https://github.com/CaryLandholt/AngularFun/&#34;&gt;AngularFun&lt;/a&gt; very complicated and if you&amp;rsquo;re not writing your project in CoffeeScript then you won&amp;rsquo;t be able to use all the components in the build script.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, I came across &lt;a href=&#34;http://radian.io&#34;&gt;Radian&lt;/a&gt;. It provides an interactive generator for Angular projects, loads modules with &lt;a href=&#34;http://requirejs.org&#34;&gt;RequireJS&lt;/a&gt;, sets up a CSS preprocessor if you want; in short, does everything I was looking for.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not perfect – its documentation was a bit lacking and setting it up required a little bit of tinkering – but otherwise I&amp;rsquo;m very pleased with the results. I&amp;rsquo;ve already contributed to the documentation which will hopefully improve the experience for other people wanting to use it.&lt;/p&gt;
&lt;p&gt;I can understand why the Angular tutorial avoids the complexity of build tools and script loaders – it&amp;rsquo;s not straightforward to explain itself – but for anything other than a very simple application its own complexity necessitates using ancillary tools to manage it. Surely it wouldn&amp;rsquo;t be too hard to provide a build script and explain it iteratively through the tutorial?&lt;/p&gt;
&lt;p&gt;This process highlighted to me just how important it is to provide users with tools to get started quickly and remain productive as their projects scale. Django does this well by bundling a project generator with the framework and the default structure is fairly scalable. Sites such as &lt;a href=&#34;http://lincolnloop.com/django-best-practices/index.html&#34;&gt;Django best practices&lt;/a&gt; are an excellent complement.&lt;/p&gt;
&lt;p&gt;Hopefully as Angular matures and more people document their experiences of using it in larger projects it will become easier for newcomers to start building scalable applications. Tools like Radian will fill the gaps in the meantime.&lt;/p&gt;
</description>
        <pubDate>Sat, 08 Feb 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/framework-scalability/</link>
        <guid isPermaLink="true">https://joeray.me/framework-scalability/</guid>
    </item>
    
    
    
    <item>
        <title>Mocking files and file storage for unit testing Django models</title>
        <description>&lt;p&gt;When writing unit tests, avoiding interaction with external services (for example the filesystem or network) is
considered &lt;a href=&#34;https://groups.yahoo.com/neo/groups/extremeprogramming/conversations/topics/111829&#34;&gt;best practice&lt;/a&gt;
because you can&amp;rsquo;t rely on external services&amp;rsquo; availability, nor do they present a consistent, predictable state
when running your tests. Normally, the way to achieve this is to mock out the particular service your code depends
upon but when your code uses a large framework such as &lt;a href=&#34;http://djangoproject.com&#34;&gt;Django&lt;/a&gt; it becomes difficult to
ascertain what to mock and how to mock it.&lt;/p&gt;
&lt;p&gt;In my case, I was trying to unit test some code which interacted with a Model with a FileField on it. To create
an instance of the Model I needed to populate the FileField with a value but the field expects a
&lt;code&gt;django.core.files.File&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;This presents two problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If I give the Model an actual File instance I need a file to read which would involve touching the filesystem&lt;/li&gt;
&lt;li&gt;Upon saving the Model the file will be processed and stored using the
&lt;a href=&#34;https://docs.djangoproject.com/en/1.6/topics/files/#file-storage&#34;&gt;file storage system&lt;/a&gt;, again touching the filesystem
(or the network if you have a custom storage system to e.g. upload to S3)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My solution involved mocking both the File instance and the file storage system. For my unit tests, I use the Python library
&lt;a href=&#34;http://www.voidspace.org.uk/python/mock/&#34;&gt;mock&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First we need to mock the File instance. There is only one thing we really need to mock – the name attribute – because
it is primarily the file storage system which accesses the File&amp;rsquo;s methods:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;mock&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;django.core.files&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;File&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;file_mock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MagicMock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;FileMock&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;file_mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;test1.jpg&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This tells the mock library to create a mock object based on Django&amp;rsquo;s &lt;code&gt;File&lt;/code&gt; class and assigns the name attribute.
Using this is as simple as assigning the mock to the file attribute on our model:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;asset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Asset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;asset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file_mock&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The file storage system is a little more tricky. We need to create the mock and then get Django to use it. Luckily
there&amp;rsquo;s one way in which Django retrieves the default file storage system. We can use mock&amp;rsquo;s
&lt;a href=&#34;http://www.voidspace.org.uk/python/mock/patch.html#mock.patch&#34;&gt;patch&lt;/a&gt; function to force it to return our mock instead of
the default:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;django.core.files.storage&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Storage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;storage_mock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MagicMock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;StorageMock&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;storage_mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MagicMock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;storage_mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;return_value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/tmp/test1.jpg&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;django.core.files.storage.default_storage._wrapped&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;storage_mock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# The asset is saved to the database but our mock storage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# system is used so we don&amp;#39;t touch the filesystem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;asset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We need to patch &lt;code&gt;_wrapped&lt;/code&gt; because &lt;code&gt;default_storage&lt;/code&gt; is a lazy-loaded object and &lt;code&gt;_wrapped&lt;/code&gt; is the property which it uses to
determine if it&amp;rsquo;s been loaded yet or not. Note: this won&amp;rsquo;t work if you&amp;rsquo;re manually setting the &lt;code&gt;storage&lt;/code&gt; property of
the file field when creating the Model; you must be relying on the &lt;code&gt;DEFAULT_FILE_STORAGE&lt;/code&gt; setting.&lt;/p&gt;
&lt;p&gt;Edit: A previous version of this article recommended patching &lt;code&gt;get_storage_class&lt;/code&gt;. &lt;a href=&#34;https://twitter.com/Paul_Collins&#34;&gt;@Paul_Collins&lt;/a&gt;
spotted that this would lead to Django caching the first test&amp;rsquo;s mock rather than replacing each time. This new method of
patching &lt;code&gt;_wrapped&lt;/code&gt; should get around that.&lt;/p&gt;
</description>
        <pubDate>Sat, 08 Feb 2014 00:00:00 UTC</pubDate>
        <link>https://joeray.me/mocking-files-and-file-storage-for-testing-django-models/</link>
        <guid isPermaLink="true">https://joeray.me/mocking-files-and-file-storage-for-testing-django-models/</guid>
    </item>
    
    
	</channel>
</rss>
