<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Broonix Rants</title><description>Random UTF-8 characters</description><link>https://rants.broonix.ca/</link><language>en-us</language><item><title>Migrating my blog to Astro (with Claude)</title><link>https://rants.broonix.ca/blog/migrating-to-astro-with-ai/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/migrating-to-astro-with-ai/</guid><pubDate>Wed, 20 May 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So it&apos;s been ages since I posted or updated this blog. I don’t know what the root cause was, but life just got busy, and I didn’t have time for this blog.&lt;/p&gt;
&lt;p&gt;Now I plan to restart blogging; the practice of learning and writing has been helpful in growing as a developer. Given that the software development landscape is changing, expect many posts on AI.&lt;/p&gt;
&lt;p&gt;Now, if you’ve been to this blog before, you might notice it looks a little different, works a little faster, and renders a bit better. I wish I could take credit, maybe I can, but I haven’t touched a single line of code. I’ve been riffing with Claude. The LLM has done all the work, and I’ve been directing.&lt;/p&gt;
&lt;p&gt;I’ve fixed countless bugs that crept into the previous version. I’ll be honest, that was my fault. I had left this blog to rot. It was countless versions behind the Gatsby ecosystem.&lt;/p&gt;
&lt;p&gt;You’ll see this blog is now on Astro. It renders with zero CLS. It&apos;s been migrated to Tailwind. For me, this has been an exercise in working with LLMs. If you haven’t read &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04&quot;&gt;Steve Yeege’s Gas Town&lt;/a&gt;, here are his ‘The 8 Stages of Dev Evolution To AI’&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I’m somewhere between 6 and 7. I’m still not sure how I feel about all this. We have for sure found a new way of working. I just hope token costs do not become a limiting factor. Even my fun little blog refactor has easily eaten up my 5-hour token limits in 45 minutes.&lt;/p&gt;
&lt;p&gt;Going forward, the deal here is simple: I won&apos;t write code for this blog again, Claude will. But every word you read is mine. No LLM drafts, no &quot;polish this paragraph,&quot; no ghostwritten ideas. The writing is the part I&apos;m not outsourcing. Expect to see more posts as I stumble my way into this new AI era.&lt;/p&gt;</content:encoded><category>astro</category><category>gatsby</category><category>ai</category><category>blog</category><category>development</category></item><item><title>Brooksmarks Feb 14th 2020</title><link>https://rants.broonix.ca/blog/brooksmarks-02-14-2020/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/brooksmarks-02-14-2020/</guid><pubDate>Fri, 14 Feb 2020 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here are a few things I read over the last month that I wanted to share.&lt;/p&gt;
&lt;h2&gt;Your online activity is now effectively a social ‘credit score’&lt;/h2&gt;
&lt;p&gt;https://www.engadget.com/2020/01/17/your-online-activity-effectively-social-credit-score-airbnb/&lt;/p&gt;
&lt;h2&gt;Technical Debt Is like a Tetris Game&lt;/h2&gt;
&lt;p&gt;https://www.fluentcpp.com/2020/01/17/technical-debt-is-like-a-tetris-game/&lt;/p&gt;
&lt;h2&gt;Why do we fall into the rewrite trap?&lt;/h2&gt;
&lt;p&gt;https://www.justindfuller.com/2020/01/why-do-we-fall-into-the-rewrite-trap/&lt;/p&gt;
&lt;h2&gt;Burnout: &apos;Sick and tired of feeling sick and tired&apos;&lt;/h2&gt;
&lt;p&gt;https://www.bbc.com/news/technology-50604035&lt;/p&gt;</content:encoded><category>privacy</category><category>business</category><category>startups</category><category>engineering</category></item><item><title>Setting up source maps (NextJS 9, BugSnag)</title><link>https://rants.broonix.ca/blog/nextjs-bugsnag-heroku-source-maps/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/nextjs-bugsnag-heroku-source-maps/</guid><pubDate>Sat, 04 Jan 2020 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently set up &lt;a href=&quot;https://www.bugsnag.com&quot;&gt;bugsnag&lt;/a&gt; and had to fiddle around to get source maps working. I wanted to share my setup in case it is useful to anyone else out there. I&apos;m using NextJS 9 and hosting my application on Heroku. This guide assumes you have already installed and configured bugsnag in your NextJS app.&lt;/p&gt;
&lt;h2&gt;Generating source maps&lt;/h2&gt;
&lt;p&gt;Zeit provides a nice plugin to generate source maps as part of your build: https://www.npmjs.com/package/@zeit/next-source-maps. To setup source maps install this package.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save-dev @zeit/next-source-maps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or if you are using yarn&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn install --dev @zeit/next-source-maps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your &lt;code&gt;next.config.js&lt;/code&gt; import the plugin and configure it. I&apos;m using a few other plugins here &lt;code&gt;withOffline&lt;/code&gt; for PWA support, and &lt;code&gt;withBundleAnalyzer&lt;/code&gt; to check the size of the resulting bundle. The key items are &lt;code&gt;withSourceMaps&lt;/code&gt; and setting &lt;code&gt;devtool: &apos;hidden-source-map&apos;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const withOffline = require(&apos;next-offline&apos;)
const withSourceMaps = require(&apos;@zeit/next-source-maps&apos;)

const withBundleAnalyzer = require(&apos;@next/bundle-analyzer&apos;)({
  enabled: process.env.ANALYZE === &apos;true&apos;,
})

module.exports = withSourceMaps(withOffline(withBundleAnalyzer({
  devtool: &apos;hidden-source-map&apos;,
  publicRuntimeConfig: {
    BUG_SNAG_KEY: process.env.BUG_SNAG_KEY,
    STAGE: process.env.STAGE,
    VERSION: process.env.HEROKU_RELEASE_VERSION || &apos;dev&apos;,
    COMMIT: process.env.HEROKU_SLUG_COMMIT || &apos;dev&apos;,
  },
  workboxOpts: {
    ...
  },
})))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Uploading source maps post build on Heroku&lt;/h2&gt;
&lt;p&gt;The first thing you should do is enable &lt;a href=&quot;https://devcenter.heroku.com/articles/dyno-metadata&quot;&gt;dyno metadata&lt;/a&gt;. This will give you access to the build and dyno metadata so we can properly link releases to their code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;heroku labs:enable runtime-dyno-metadata -a &amp;lt;app name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if you pull your app&apos;s config you should see some additional metadata.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ heroku config -a &amp;lt;your app&amp;gt;
=== &amp;lt;your app&amp;gt; Config Vars
BUG_SNAG_KEY:           hidden
HEROKU_APP_ID:             hidden
HEROKU_APP_NAME:           &amp;lt;your app&amp;gt;
HEROKU_RELEASE_CREATED_AT: 2020-01-04T08:00:00Z
HEROKU_RELEASE_VERSION:    vNN
HEROKU_SLUG_COMMIT:        &amp;lt;some_sha&amp;gt;
HEROKU_SLUG_DESCRIPTION:   Deploy &amp;lt;some_sha&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next lets install &lt;a href=&quot;https://www.npmjs.com/package/bugsnag-sourcemaps&quot;&gt;bugsnag&apos;s upload CLI&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save-dev bugsnag-sourcemaps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or if you are using yarn&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn install --dev bugsnag-sourcemaps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sweet, now we have all the info we need to tell bugsnag about this release. We can set up a post build task that runs a script to upload our source maps. Lets add that to &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  ...
  &quot;scripts&quot;: {
    ....
    &quot;postbuild&quot;: &quot;./scripts/upload-sourcemaps.sh&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the post build script &lt;code&gt;/scripts/upload-sourcemaps.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./node_modules/bugsnag-sourcemaps/cli.js --api-key $BUG_SNAG_KEY --app-version $HEROKU_RELEASE_VERSION --upload-sources --directory ./.next/ --overwrite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s it, next time you release on heroku you should see source maps uploaded during the build process.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</content:encoded><category>nextjs</category><category>bugsnag</category><category>heroku</category><category>source maps</category><category>development</category></item><item><title>Brooksmarks January 2nd 2020</title><link>https://rants.broonix.ca/blog/brooksmarks-01-02-2020/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/brooksmarks-01-02-2020/</guid><pubDate>Thu, 02 Jan 2020 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here are a few things I read over the holidays that I wanted to share.&lt;/p&gt;
&lt;h2&gt;Alienated, Alone And Angry: What The Digital Revolution Really Did To Us&lt;/h2&gt;
&lt;p&gt;https://www.buzzfeednews.com/article/josephbernstein/in-the-2010s-decade-we-became-alienated-by-technology&lt;/p&gt;
&lt;h2&gt;Why Your Brain Needs Exercise&lt;/h2&gt;
&lt;p&gt;https://www.scientificamerican.com/article/why-your-brain-needs-exercise/&lt;/p&gt;
&lt;h2&gt;I asked my students to turn in their cell phones and write about living without them.&lt;/h2&gt;
&lt;p&gt;https://www.technologyreview.com/s/614934/teenagers-without-cell-phones/&lt;/p&gt;
&lt;h2&gt;Designing accessible color systems&lt;/h2&gt;
&lt;p&gt;https://stripe.com/gb/blog/accessible-color-systems&lt;/p&gt;</content:encoded><category>health</category><category>business</category><category>startups</category><category>design</category></item><item><title>NextJS: Fallback image</title><link>https://rants.broonix.ca/blog/nextjs-fallback-image/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/nextjs-fallback-image/</guid><pubDate>Sat, 31 Aug 2019 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Quick post on a problem I recently had to solve. I was looking to have an image tag fallback to a different image if the image set in the &lt;code&gt;src&lt;/code&gt; property doesn&apos;t load.&lt;/p&gt;
&lt;p&gt;Now normally you can handle this all on the client side using the image tags &lt;code&gt;onError&lt;/code&gt; listener. There is a bit of a trick to get this idea to work with NextJS. The &lt;code&gt;onError&lt;/code&gt; event may not be fired when rendering via SSR. This is because the image content may load before ReactDOM has completed hydration.&lt;/p&gt;
&lt;p&gt;Once simple way to work around this issue is to force this to happen after ReactDOM has completed hydration. This can be accomplished by forcing the fallback to always happen on the client side.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const renderImage = (image, fallbackImage) =&amp;gt; {
  const onerror = `this.onerror=null;this.src=this.dataset.fallbackImage;`
  return (
    &amp;lt;div dangerouslySetInnerHTML={{
      __html: `&amp;lt;img onError=&quot;${onerror}&quot; data-fallback-image=${fallbackImage} src=&quot;${image}&quot; /&amp;gt;`
    }}&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I created a small demo of this idea in GitHub: https://github.com/brookslyrette/image-with-fallback&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</content:encoded><category>next.js</category><category>react</category><category>images</category><category>image fallback</category></item><item><title>Brooksmarks Aug 18th 2019</title><link>https://rants.broonix.ca/blog/brooksmarks-12-08-2019/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/brooksmarks-12-08-2019/</guid><pubDate>Mon, 12 Aug 2019 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here are a few things I read this week that I wanted to share.&lt;/p&gt;
&lt;h2&gt;Work Together Anywhere: What Great Remote Teams Look Like&lt;/h2&gt;
&lt;p&gt;https://www.infoq.com/presentations/work-remotely-team/?utm_campaign=infoq_content&amp;amp;utm_source=infoq&amp;amp;utm_medium=feed&amp;amp;utm_term=global&lt;/p&gt;
&lt;h2&gt;Frontend Design, React, and a Bridge over the Great Divide&lt;/h2&gt;
&lt;p&gt;http://bradfrost.com/blog/post/frontend-design-react-and-a-bridge-over-the-great-divide/&lt;/p&gt;
&lt;h2&gt;GitHub stars won’t pay your rent&lt;/h2&gt;
&lt;p&gt;https://medium.com/@kitze/github-stars-wont-pay-your-rent-8b348e12baed&lt;/p&gt;
&lt;h2&gt;One job, many roles. The different skills needed to be a successful CTO&lt;/h2&gt;
&lt;p&gt;https://madewithlove.be/one-job-many-roles-the-different-skills-needed-to-be-a-successful-cto/&lt;/p&gt;</content:encoded><category>remote</category><category>CTO</category><category>business</category><category>startups</category></item><item><title>NextJS &amp; Heroku: HTTPS by default</title><link>https://rants.broonix.ca/blog/nextjs-heroku-https/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/nextjs-heroku-https/</guid><pubDate>Mon, 05 Aug 2019 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I recently ran into the case where I wanted to ensure all traffic to my NextJS site was happening over HTTPS. Since my site is deployed using &lt;code&gt;next start&lt;/code&gt; on heroku, I ended up implementing this using a &lt;a href=&quot;https://nextjs.org/docs#custom-server-and-routing&quot;&gt;custom server&lt;/a&gt; that checks all requests are https before handling them.&lt;/p&gt;
&lt;p&gt;Here is the completed &lt;code&gt;server.js&lt;/code&gt; implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const { createServer } = require(&apos;http&apos;)
const { parse } = require(&apos;url&apos;)
const next = require(&apos;next&apos;)

const dev = process.env.NODE_ENV !== &apos;production&apos;
const app = next({ dev })
const handle = app.getRequestHandler()
const port = process.env.PORT

app.prepare().then(() =&amp;gt; {
  createServer((req, res) =&amp;gt; {
    const parsedUrl = parse(req.url, true)
    if (!dev &amp;amp;&amp;amp; req.headers[&apos;x-forwarded-proto&apos;] != &apos;https&apos;) {
      const { host } = parse(process.env.HOSTNAME)
      res.writeHead(302, {
        Location: `https://${host}${req.url}`
      })
      res.end()
    } else {
      handle(req, res, parsedUrl)
    }
  }).listen(port, err =&amp;gt; {
    if (err) throw err
    console.log(`&amp;gt; Ready on http://localhost:${port}`)
  })
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The redirect only fires in production mode and it uses a environment variable that contains the servers hostname. You&apos;ll notice that it does not check the requests URL or protocol. This is because all traffic from Heroku&apos;s router to your server happens over HTTP. You can see if the request is https by checking the value of the &lt;code&gt;x-forwarded-proto&lt;/code&gt; header that Heroku adds.&lt;/p&gt;
&lt;p&gt;You&apos;ll also have to update your start script to run your server and not the nextjs default.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...
&quot;scripts&quot;: {
  &quot;dev&quot;: &quot;next&quot;,
  &quot;build&quot;: &quot;next build&quot;,
  &quot;export&quot;: &quot;next export&quot;,
  &quot;start&quot;: &quot;NODE_ENV=production node server.js&quot;
}
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Notes and Limitations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This does not support sites hosted using &lt;code&gt;next export&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Changes to &lt;code&gt;server.js&lt;/code&gt; are not hot reloaded.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>next.js</category><category>react</category><category>heroku</category><category>https</category></item><item><title>Remote working: Six months in</title><link>https://rants.broonix.ca/blog/back-to-remote-six-months-in/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/back-to-remote-six-months-in/</guid><pubDate>Sat, 09 Mar 2019 09:45:00 GMT</pubDate><content:encoded>&lt;p&gt;So I&apos;ve been back to working remotely for 6 months. I figured it was time to write a post about what it has been like getting back to working remotely.&lt;/p&gt;
&lt;p&gt;First a little background. This is not my first time working remote. I worked with a great company out of New York from 2007 - 2016. The last 7 years there I worked remotely from my home office in Ottawa. In 2016 I was looking for a change and ended up taking a role that required me to trek across the city to an office.&lt;/p&gt;
&lt;p&gt;For me at least, the cons of office work out weight the pros. The largest con was that my daily commute took 35 - 90 minutes each way depending on traffic. I&apos;m a guy who loves cars, but getting to and from work isn&apos;t a pleasure drive it&apos;s &lt;em&gt;commuting&lt;/em&gt;. I even adjusted my hours to try to minimize the time I lost daily sitting in a car staring at the bumper in front of me.&lt;/p&gt;
&lt;p&gt;Now office life isn&apos;t that bad. Over two years I worked at two companies both with different types of offices. The first was an open office. I quickly learnt that I&apos;m just not built to work in an open office. It&apos;s just too noisy and I get constantly distracted. Headphones really helped, but if you&apos;re going to isolate yourself with sound why not just find a quiet spot to write code? Cubicle land was much less distracting. I had the pleasure of working with some great developers and I still miss impromptu chats in the kitchen or around the coffee machine. Those organic moments of human interaction don&apos;t happen working remotely.&lt;/p&gt;
&lt;p&gt;I&apos;m in love with my new remote work schedule. All that time I was losing to my commute is now my time again. No more 7 am drives across the city to beat traffic. My morning is now fresh coffee at home, a workout and I&apos;m at my desk by 9 am. When I wrap up all I&apos;m already home.&lt;/p&gt;
&lt;p&gt;There are a few things I&apos;m keeping an eye on though, remote work isn&apos;t all roses. It can be hard to decompress when you wrap up your workday. Without a clear separation between work and play, life can get monotonous. I&apos;ll post another update in a few months.&lt;/p&gt;</content:encoded><category>work</category><category>remote</category></item><item><title>Styling with CSS variables</title><link>https://rants.broonix.ca/blog/styling-with-css-vars/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/styling-with-css-vars/</guid><pubDate>Sat, 16 Feb 2019 19:45:00 GMT</pubDate><content:encoded>&lt;p&gt;This weekend I&apos;ve messing around with CSS variables. I figured it might be fun to see if I could build a way to style this site using only CSS variables.&lt;/p&gt;
&lt;p&gt;What are CSS variables? Well after all these years we finally have a way to define a value and reuse it in other places. Sure SASS and LESS gave us access to something similar, but compile down to CSS. Variables are dynamic in CSS so we can even be redefine at run time. Their syntax is nice an simple.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick note:&lt;/strong&gt; &lt;em&gt;This post is best viewed in a modern browser that supports &lt;a href=&quot;https://caniuse.com/#feat=css-variables&quot;&gt;css variables&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Defining a new CSS variable looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.some-css-selector {
  --primary-text-color: #666;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then when you want to use that value:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.another-css-selector {
  color: var(--primary-text-color);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also provide a fallback value in the case the variable is not defined:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.another-css-selector {
  color: var(--primary-text-color, #777);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now this blog is a mix of pair CSS files and &lt;a href=&quot;https://emotion.sh/&quot;&gt;Emotion&lt;/a&gt; for component styling. The CSS files are &lt;code&gt;reset.css&lt;/code&gt; which is a generic CSS reset file to ensure a standard look and feel between browsers and &lt;code&gt;theme.css&lt;/code&gt; which styles things like links, code blocks and inline code.&lt;/p&gt;
&lt;p&gt;Updating everything to use CSS variables was ridiculously simple. I worked out the list of all colors used on my blog and their function.
I defined all the new values I need as part of the &lt;code&gt;:root&lt;/code&gt; &lt;a href=&quot;https://css-tricks.com/almanac/selectors/r/root/&quot;&gt;element of my document&lt;/a&gt; in &lt;code&gt;theme.css&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  --primary-text-color: #666;
  --secondary-text-color: #777;
  --heading-text-color: #333;
  --link-text-color: #333;
  --inverted-text-color: #f8f8f8;
  --body-bg-color: #fff;
  --highlight-color-dark: #0d6186;
  --highlight-color-light: rgba(13, 97, 134, 0.45);
  --footer-text-color: #b3b3b3;
  --footer-border-color: #ddd;
  --code-background-color: #fbfaf8;
  --code-inline-color: #c25;
  --code-block-color: #000;
  --code-border-color: rgba(0, 0, 0, 0.15);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then all I needed to do was update any usages of these colors in my CSS files and Emotion styled components. Here&apos;s what the &lt;code&gt;Tag&lt;/code&gt; component now looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const StyledTag = styled.span`
  display: inline-block;
  text-transform: uppercase;
  margin: 0 4px;
  font-size: .65rem;
  transition: all 350ms;
  position: relative;
  top: -1px;

  a {
    color: var(--inverted-text-color);
    text-decoration: none;
    padding: 3px 5px;
    border-radius: .25rem;
    background-color: var(--highlight-color-light);
    transition: all .5s;
    white-space: nowrap;

    &amp;amp;:hover {
      background-color: var(--highlight-color-dark);
    }
  }
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now to the viewer this blog it more or less looks the exact same as before, but one of the coolest things about CSS variables is that you can redefine them at runtime. Given my new blog format I can now create much more interactive demos. Something like this:&lt;/p&gt;
&lt;p&gt;You can load any theme and browse around &lt;a href=&quot;/&quot;&gt;my blog&lt;/a&gt;. The theme will remain active until you reload the page. If you are wondering how my &lt;code&gt;ThemeChanger&lt;/code&gt; control works here&apos;s the source code.&lt;/p&gt;
&lt;h2&gt;themeChanger.js:&lt;/h2&gt;
&lt;p&gt;This component handles updating the DOM and state when our CSS variables change.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { useState } from &apos;react&apos;
import styled from &apos;@emotion/styled&apos;
import { FontAwesomeIcon } from &apos;@fortawesome/react-fontawesome&apos;
import { faEdit } from &apos;@fortawesome/free-solid-svg-icons&apos;

import { themes, cssVarList } from &apos;./themes&apos;
import ColorEditor from &apos;./colorEditor&apos;
import ThemeButton from &apos;./themeButton&apos;

const prettyLabelName = label =&amp;gt; label.substring(2).replace(/-/g, &apos; &apos;)

/**
 * Sets new values for all CSS variables the sites theme uses.
 */
const updateCssVars = theme =&amp;gt; {
  const body = document.documentElement
  cssVarList.forEach((variable, i) =&amp;gt; {
    body.style.setProperty(variable, theme[i])
  })
}

export const ThemeChanger = () =&amp;gt; {
  // hook that stores the active theme
  const [activeTheme, setActiveTheme] = useState(themes[&apos;Default Theme&apos;])
  // hook that stores the active edit pane
  const [activeEditor, setActiveEditor] = useState(-1)

  const changeTheme = theme =&amp;gt; {
    setActiveTheme(theme)
    updateCssVars(theme)
  }

  const toggleEditor = index =&amp;gt; {
    setActiveEditor(activeEditor === index ? -1 : index)
  }

  return (
    &amp;lt;Wrapper&amp;gt;
      &amp;lt;h2&amp;gt;Theme Switcher&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;
        Select theme an existing theme or create your own!
      &amp;lt;/p&amp;gt;
      &amp;lt;ThemeButton themeName=&quot;Dark Theme v1&quot; setTheme={changeTheme} /&amp;gt;
      &amp;lt;ThemeButton themeName=&quot;Dark Theme v2&quot; setTheme={changeTheme} /&amp;gt;
      &amp;lt;ThemeButton themeName=&quot;Default Theme&quot; setTheme={changeTheme} /&amp;gt;
      &amp;lt;ThemeButton themeName=&quot;Green Theme&quot; setTheme={changeTheme} /&amp;gt;
      &amp;lt;ThemeButton themeName=&quot;Orange Theme&quot; setTheme={changeTheme} /&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;h2&amp;gt;Custom Theme Builder&amp;lt;/h2&amp;gt;
        {activeTheme.map((setting, i) =&amp;gt; (
          &amp;lt;StyleSettingContainer key={i}&amp;gt;
            &amp;lt;StyledLabel&amp;gt;{prettyLabelName(cssVarList[i])}&amp;lt;/StyledLabel&amp;gt;
            &amp;lt;StyledInput onClick={() =&amp;gt; toggleEditor(i)} readOnly type=&quot;text&quot; value={setting} /&amp;gt;
            &amp;lt;EditIcon open={i === activeEditor} onClick={() =&amp;gt; toggleEditor(i)} icon={faEdit} size=&quot;1x&quot; /&amp;gt;
            {activeEditor === i ? (
              &amp;lt;ColorEditor
                setting={setting}
                index={i}
                setActiveEditor={setActiveEditor}
                setTheme={changeTheme}
                activeTheme={activeTheme}
              /&amp;gt;
            ) : null}
          &amp;lt;/StyleSettingContainer&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/Wrapper&amp;gt;
  )
}

const StyleSettingContainer = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 6px;
`

const StyledLabel = styled.label`
  text-transform: capitalize;
  width: 200px;
  text-align: right;
  margin-right: 6px;
`

const StyledInput = styled.input`
  width: 200px;
  border-radius: 6px;
  padding: 5px;
  border: 1px solid #bebebe;
`

const EditIcon = styled(FontAwesomeIcon)`
  position: relative;
  left: -25px;
  color: var(${ ({ open }) =&amp;gt; open ? &apos;--highlight-color-dark&apos; : &apos;--highlight-color-light&apos; });
  cursor: pointer;

  &amp;amp;:hover {
    color: var(--highlight-color-dark);
  }
`

const Wrapper = styled.div`
  text-align: center;
  padding: 16px 0;
`
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;themeButton.js:&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;button&lt;/code&gt; element that loads a pre-made theme.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;
import styled from &apos;@emotion/styled&apos;
import { themes } from &apos;./themes&apos;

const ThemeButton = ({ themeName, setTheme }) =&amp;gt; {
  const selectedTheme = themes[themeName]
  return (
    &amp;lt;StyledThemeButton
      type=&quot;button&quot;
      color={selectedTheme[6]}
      onClick={() =&amp;gt; setTheme(selectedTheme)}
      value={themeName}
    /&amp;gt;
  )
}

const StyledThemeButton = styled.input`
  background-color: ${ ({ color }) =&amp;gt; color };
  border: 1px solid ${ ({ color }) =&amp;gt; color };
  color: #f8f8f8;
  margin: 2px 5px;
  height: 32px;
  border-radius: 6px;

  &amp;amp;:hover {
    cursor: pointer;
    color: ${ ({ color }) =&amp;gt; color };
    background-color: #f8f8f8;
  }
`

export default ThemeButton
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;colorEditor.js:&lt;/h2&gt;
&lt;p&gt;Opens a &lt;code&gt;react-color&lt;/code&gt; color picker and handles updating the color.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;
import styled from &apos;@emotion/styled&apos;
import { SketchPicker } from &apos;react-color&apos;
import { FontAwesomeIcon } from &apos;@fortawesome/react-fontawesome&apos;
import { faTimesCircle } from &apos;@fortawesome/free-solid-svg-icons&apos;

const handleColorPickerChange = (index, color, activeTheme, setTheme) =&amp;gt; {
  // use hex if the color is solid, else use rgba() so we can include the alpha channel.
  const value = color.rgb.a === 1
    ? color.hex
    : `rgba(${ color.rgb.r }, ${ color.rgb.g }, ${ color.rgb.b }, ${ color.rgb.a })`
  const updatedTheme = activeTheme.map((themeValue, i) =&amp;gt; {
    if (i !== index) {
      return themeValue
    }
    return value
  })

  setTheme(updatedTheme)
}

const ColorEditor = ({ setting, index, activeTheme, setTheme, setActiveEditor }) =&amp;gt; (
  &amp;lt;Wrapper&amp;gt;
    &amp;lt;Editor&amp;gt;
      &amp;lt;CloseButton onClick={() =&amp;gt; setActiveEditor(-1)} icon={faTimesCircle} size=&quot;1x&quot; /&amp;gt;
      &amp;lt;SketchPicker color={setting} onChange={color =&amp;gt; handleColorPickerChange(index, color, activeTheme, setTheme)}/&amp;gt;
    &amp;lt;/Editor&amp;gt;
  &amp;lt;/Wrapper&amp;gt;
)

const Wrapper = styled.div`
  position: relative;
`

const Editor = styled.div`
  position: absolute;
  top: -42px;
  left: 0px;
`

const CloseButton = styled(FontAwesomeIcon)`
  position: relative;
  right: -117px;
  top: 8px;
  z-index: 1;
  color: var(--primary-text-color);
  cursor: pointer;
`

export default ColorEditor
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;themes.js&lt;/h2&gt;
&lt;p&gt;The list of pre-made themes and variables used for this sites theme.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const themes = {
  &apos;Dark Theme v1&apos;: [
    &apos;#dedede&apos;,
    &apos;#b8b8b8&apos;,
    &apos;#fafafa&apos;,
    &apos;#dedede&apos;,
    &apos;#dedede&apos;,
    &apos;#02111D&apos;,
    &apos;#6c6ca3&apos;,
    &apos;rgb(88, 110, 128, 0.45)&apos;,
    &apos;#dedede&apos;,
    &apos;#dedede&apos;,
    &apos;#142534&apos;,
    &apos;#6c6ca3&apos;,
    &apos;##f8f8f8&apos;,
    &apos;rgba(255, 255, 255, 0.15)&apos;
  ],
  &apos;Default Theme&apos;: [
    &apos;#666&apos;,
    &apos;#777&apos;,
    &apos;#333&apos;,
    &apos;#333&apos;,
    &apos;#f8f8f8&apos;,
    &apos;#fff&apos;,
    &apos;#0d6186&apos;,
    &apos;rgba(13, 97, 134, 0.45)&apos;,
    &apos;#b3b3b3&apos;,
    &apos;#ddd&apos;,
    &apos;#fbfaf8&apos;,
    &apos;#c25&apos;,
    &apos;#000&apos;,
    &apos;rgba(0, 0, 0, 0.15)&apos;
  ],
  &apos;Dark Theme v2&apos;: [
    &apos;#dedede&apos;,
    &apos;#b8b8b8&apos;,
    &apos;#fafafa&apos;,
    &apos;#dedede&apos;,
    &apos;#dedede&apos;,
    &apos;#02111D&apos;,
    &apos;#ff3366&apos;,
    &apos;rgb(255,51,102, 0.45)&apos;,
    &apos;#b3b3b3&apos;,
    &apos;#ddd&apos;,
    &apos;#142534&apos;,
    &apos;#ff3366&apos;,
    &apos;#f8f8f8&apos;,
    &apos;rgba(255, 255, 255, 0.15)&apos;
  ],
  &apos;Green Theme&apos;: [
    &apos;#666&apos;,
    &apos;#777&apos;,
    &apos;#333&apos;,
    &apos;#333&apos;,
    &apos;#f8f8f8&apos;,
    &apos;#fff&apos;,
    &apos;#038c36&apos;,
    &apos;rgb(3, 140, 54, 0.45)&apos;,
    &apos;#b3b3b3&apos;,
    &apos;#ddd&apos;,
    &apos;#fbfaf8&apos;,
    &apos;#038c36&apos;,
    &apos;#000&apos;,
    &apos;rgba(0, 0, 0, 0.15)&apos;,
  ],
  &apos;Orange Theme&apos;: [
    &apos;#666&apos;,
    &apos;#777&apos;,
    &apos;#333&apos;,
    &apos;#333&apos;,
    &apos;#f8f8f8&apos;,
    &apos;#fff&apos;,
    &apos;#ff9900&apos;,
    &apos;rgb(255, 127, 0, 0.45)&apos;,
    &apos;#b3b3b3&apos;,
    &apos;#ddd&apos;,
    &apos;#fbfaf8&apos;,
    &apos;#ff9900&apos;,
    &apos;#000&apos;,
    &apos;rgba(0, 0, 0, 0.15)&apos;,
  ]
}

export const cssVarList = [
  &apos;--primary-text-color&apos;,
  &apos;--secondary-text-color&apos;,
  &apos;--heading-text-color&apos;,
  &apos;--link-text-color&apos;,
  &apos;--inverted-text-color&apos;,
  &apos;--body-bg-color&apos;,
  &apos;--highlight-color-dark&apos;,
  &apos;--highlight-color-light&apos;,
  &apos;--footer-text-color&apos;,
  &apos;--footer-border-color&apos;,
  &apos;--code-background-color&apos;,
  &apos;--code-inline-color&apos;,
  &apos;--code-block-color&apos;,
  &apos;--code-border-color&apos;
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you have it, my blog can now be easily themed via CSS variables. If you have any questions or comments let me know below. Happy coding!&lt;/p&gt;</content:encoded><category>blog</category><category>gatsby</category><category>css</category><category>coding</category></item><item><title>More notes on migrating my blog to Gatsby</title><link>https://rants.broonix.ca/blog/more-notes-on-gatsby/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/more-notes-on-gatsby/</guid><pubDate>Sat, 02 Feb 2019 15:38:59 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve nearly completed porting my blog to Gatsby. In &lt;a href=&quot;/displaying-expo-snacks-in-react-and-gatsby&quot;&gt;my last&lt;/a&gt; &lt;a href=&quot;/some-notes-on-gatsby&quot;&gt;few posts&lt;/a&gt; I outlined how I ported my ghost.io blog to GatsbyJS. Last week I pointed traffic at the new Gatsby version soft launching the new Gatsby version.&lt;/p&gt;
&lt;p&gt;Since then, I&apos;ve been still hacking away at the code. While I&apos;m really happy with the result, my solution of using the exported JSON files from ghost.io isn&apos;t really useful when creating new posts. Last week I ended up creating my post in a Markdown editor then converting that to a one-line string for a JSON config file. That&apos;s not a user-friendly or scalable way to blog.&lt;/p&gt;
&lt;p&gt;So I spent a little time today converting this blog to use a new format. I ended up discarding most of the old blog data and using a single JSON file to define all the posts. I&apos;ve moved the content of each post out to its own Markdown file. This is the first post I&apos;m writing with this new format and so far it is a pretty nice way to blog.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The next few things I plan to work on are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fixing up a few small bugs.&lt;/li&gt;
&lt;li&gt;Improving mobile support.&lt;/li&gt;
&lt;li&gt;Documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the end of all this, I&apos;m hoping to open source the code that powers this blog.&lt;/p&gt;</content:encoded><category>blog</category><category>gatsby</category><category>ghost-io</category></item><item><title>New Blog! Powered by Gatsby</title><link>https://rants.broonix.ca/blog/new-blog-powered-by-gatsby/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/new-blog-powered-by-gatsby/</guid><pubDate>Mon, 28 Jan 2019 14:58:31 GMT</pubDate><content:encoded>&lt;p&gt;Okay, I know I&apos;ve been negelecting this blog for too long. Why did my blogging slow down? It&apos;s complicated, I ended up leaving the remote workforce in 2016, which left me with a little less time for things like blogging. I do have good news, I&apos;m back to working remotely and I&apos;m hoping to blog a little more this year.&lt;/p&gt;
&lt;p&gt;This is my first post since relaunchign my blog. I&apos;ve replaced my old hosted ghost.io blog. It&apos;s now powered by GatsbyJs and hosted on Netlify. I gotta say it&apos;s been a great toolset to work with. I was able to use the exported data from ghost.io to preserve all my historical posts.&lt;/p&gt;
&lt;p&gt;I&apos;ve also tweaked the design a little. Here is a comparison of old vs new:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;My next big blog project will be updating this new blog to a new format. I&apos;d like to replace the legacy ghost.io data import with a simpler data format. Something more flexable and focused on my blogging use-case.&lt;/p&gt;</content:encoded><category>blog</category><category>gatsby</category><category>react</category></item><item><title>Displaying Expo Snacks Gists in React and Gatsby</title><link>https://rants.broonix.ca/blog/displaying-expo-snacks-in-react-and-gatsby/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/displaying-expo-snacks-in-react-and-gatsby/</guid><pubDate>Sat, 05 Jan 2019 14:58:31 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;m still in the process of rebuilding my blog in GatsbyJS. The project so far is going really well. In last weeks post, I outlined &lt;a href=&quot;/some-notes-on-gatsby/&quot;&gt;how I was loading data into Gatsby and rendering the basic pages&lt;/a&gt;. This week I&apos;ve been working on rendering the Gists and ExpoSnacks I have embedded in most of my posts.&lt;/p&gt;
&lt;p&gt;The data export from my old blog uses a mix of Markdown and HTML. Post content looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## Creating a navigator

We will use a StackNavigator for this example. 

&amp;lt;Gist id=&quot;brookslyrette/bf3df2ad046a3fa0f464203a6c0b7e2d&quot; /&amp;gt;

Above we create a StackNavigator with three screens. We provide it with...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After I finish getting my old posts on the Gatsby version of my blog, I&apos;ll want to create new posts. Now I don&apos;t want to again use a mix of Markdown and HTML. My hope is to mix Markdown and React. I&apos;m hoping new posts will look something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## Creating a navigator

We will use a StackNavigator for this example. 

&amp;lt;Gist id=&quot;bf3df2ad046a3fa0f464203a6c0b7e2d&quot; /&amp;gt;

Above we create a StackNavigator with three screens. We provide it with...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m using &lt;code&gt;markdown-to-jsx&lt;/code&gt; to render Markdown. This library will let me easily handle both cases. We will use &lt;a href=&quot;https://github.com/probablyup/markdown-to-jsx#optionsoverrides---override-any-html-tags-representation&quot;&gt;overrides&lt;/a&gt; to look for &lt;code&gt;script&lt;/code&gt; tags or &lt;code&gt;div&lt;/code&gt; that should be replaced with a Gist or ExpoSnack to handle old posts. We&apos;ll also setup overrides to which will let us use React components in new posts.&lt;/p&gt;
&lt;p&gt;First lets look at handling just the case of replacing &lt;code&gt;script&lt;/code&gt; tags that represent a &lt;code&gt;Gist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To handle the case of new blog posts all we have to do is add one more line of configuration. Now, lets set up &lt;code&gt;Gist&lt;/code&gt; as a component override.&lt;/p&gt;
&lt;p&gt;If you load a post now you&apos;ll see Gists rendered within posts.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the final version which also handles ExpoSnacks and converts &lt;code&gt;img&lt;/code&gt; tags to &lt;a href=&quot;https://using-gatsby-image.gatsbyjs.org/&quot;&gt;gatsby images&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>react</category><category>gatsby</category><category>expo</category><category>gist</category><category>blog</category></item><item><title>Some notes on migrating my blog to Gatsby</title><link>https://rants.broonix.ca/blog/some-notes-on-gatsby/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/some-notes-on-gatsby/</guid><pubDate>Tue, 01 Jan 2019 18:18:27 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been kicking around the idea of porting my blog to &lt;a href=&quot;http://gatsbyjs.org&quot;&gt;Gatsby&lt;/a&gt;. It&apos;s not that I&apos;m unhappy with my current blog set up, it&apos;s more of an excuse to play with Gatsby. If you haven&apos;t heard of Gatsby, it&apos;s a React based static site generator that creates blazing fast websites.&lt;/p&gt;
&lt;p&gt;In this post we will cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exporting data and images from my old Ghost blog.&lt;/li&gt;
&lt;li&gt;Using this data in Gatsby.&lt;/li&gt;
&lt;li&gt;Create an index and blog post pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Exporting existing data&lt;/h2&gt;
&lt;p&gt;My blog is currently powered and hosted by &lt;a href=&quot;http://ghost.org&quot;&gt;Ghost&lt;/a&gt;. Ghost&apos;s admin tools make it really easy to get an export of your blog&apos;s underlying data. The only thing that was a bit suboptimal was having to email their support team to get an export of the images used on my blog.&lt;/p&gt;
&lt;p&gt;Ghost&apos;s export data format is JSON based. It contains a bunch of data we won&apos;t need. Here&apos;s what the export looks like:&lt;/p&gt;
&lt;h2&gt;Using this data with Gatsby&lt;/h2&gt;
&lt;p&gt;One of the reasons I&apos;m excited to try out Gatsby is that it uses &lt;a href=&quot;https://graphql.org/learn/&quot;&gt;GraphQL&lt;/a&gt;, but before we can start querying data we must first import this data into something Gatsby can use. Luckily Gatsby ships with &lt;code&gt;gatsby-transformer-json&lt;/code&gt; which reads in JSON files.&lt;/p&gt;
&lt;p&gt;I decided to break up the JSON export into multiple files. I extracted &lt;code&gt;posts&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt; and &lt;code&gt;posts_tags&lt;/code&gt; into their own JSON files. This is so that we can have a bit more flexibility when query and filter this data.&lt;/p&gt;
&lt;p&gt;Now all you have to do is configure Gatsby to read in these JSON files. Configuration is very simple, just tell Gatsby where to find the files by adding this to your &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;Here&apos;s where I ran into a small issue. The export from Ghost uses numbers as ID&apos;s. Gatsby expects all ID&apos;s as strings. Now there are many ways to handle this, I ended up forking Gatsby and forcing it to convert any non-string IDs to a string: https://github.com/brookslyrette/gatsby/commit/50570fc9028297f1e508bf03da38f9c8f85bb922.&lt;/p&gt;
&lt;p&gt;Now you should be able to query your data using Gatsby&apos;s built-in Graph&lt;em&gt;i&lt;/em&gt;QL browser. You&apos;ll have root objects for &lt;code&gt;posts&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Creating an index page&lt;/h2&gt;
&lt;p&gt;If you are using any sort of Gatsby starter project you&apos;ll already have a file called &lt;code&gt;pages/index.js&lt;/code&gt;. Any file in &lt;code&gt;pages&lt;/code&gt; will get converted to a static page by Gatsby. Let&apos;s update this page to query our data and list all blog posts.&lt;/p&gt;
&lt;p&gt;Now if you navigate to http://localhost:8000 you&apos;ll see a page listing every post in the data set.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Creating pages for posts&lt;/h2&gt;
&lt;p&gt;Now we can create the pages for each post. Unlike our index file, these are dynamically created based on our data. We&apos;ll use &lt;code&gt;createPages&lt;/code&gt; from Gatsby&apos;s Node API. &lt;code&gt;createPages&lt;/code&gt; will be called after initial sourcing, transformation of nodes and creation of the GraphQL schema is complete. Now we can update &lt;code&gt;gatsby-nodes.js&lt;/code&gt; to query all posts and create pages using a template called &lt;code&gt;/src/templates/post.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The data exported from Ghost contains the markdown for each post. All we need to do is pass that content to &lt;code&gt;markdown-to-jsx&lt;/code&gt; to render a post.&lt;/p&gt;
&lt;p&gt;Clicking on any article now will show you a nicely formatted blog post.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;There we go, my Ghost blog is more or less working on Gatsby. There are however a bunch of outstanding items to be dealt with. Embedded scripts such as Gist&apos;s and Expo Snacks aren&apos;t being rendered, we need to add some paging to the homepage etc. Stay tuned for future posts as I add features and explore Gatsby.&lt;/p&gt;</content:encoded><category>gatsby</category><category>react</category><category>blog</category></item><item><title>Font Shadows in React-Native</title><link>https://rants.broonix.ca/blog/font-shadows-in-react-native/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/font-shadows-in-react-native/</guid><pubDate>Sun, 20 May 2018 12:56:27 GMT</pubDate><content:encoded>&lt;p&gt;A nice effect you can you in react native is text shadows. This works exactly as it does in HTML CSS but the setting names are just a little different.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const styles = StyleSheet.create({
  body: {
    flex: 1,
    backgroundColor: &apos;#555&apos;,
  },
  shadow: {
    color: &apos;#fff&apos;,
    textShadowOffset: { width: 2, height: 2 },
    textShadowRadius: 1,
    textShadowColor: &apos;#000&apos;,
  },
});

const Example = ({ children }) =&amp;gt; (
  &amp;lt;SafeAreaView style={styles.body}&amp;gt;
    &amp;lt;Text styles={styles.shadow}&amp;gt;Shadow Text&amp;lt;/Text
  &amp;lt;/SafeAreaView&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;</content:encoded><category>react-native</category></item><item><title>Setting up Google Home on a Google Apps for Business domain</title><link>https://rants.broonix.ca/blog/setting-up-google-home-on-a-google-apps-for-business-domain/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/setting-up-google-home-on-a-google-apps-for-business-domain/</guid><pubDate>Sat, 30 Dec 2017 14:00:06 GMT</pubDate><content:encoded>&lt;p&gt;So I recently setup a Google Home for one of my &lt;code&gt;@lyrette.ca&lt;/code&gt; domain users. Sadly the process was not seamless and I had to stumble around the internet to figure out why.&lt;/p&gt;
&lt;p&gt;So here are the two things I needed to enable to get actually setup a Google Home device on my domain.&lt;/p&gt;
&lt;p&gt;Under your domain admin navigate to Additional Google services.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;##Enable Voice Services&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;##Enable Web &amp;amp; App Activity&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Enabling Web and App Activity was the step it took me forever to figure out. Hopefully this saves someone a ton of Googling for answers.&lt;/p&gt;</content:encoded><category>google-home</category><category>setup-tag</category></item><item><title>Adding coverage reports to a React Create App using Coveralls.io</title><link>https://rants.broonix.ca/blog/adding-coverage-reports/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/adding-coverage-reports/</guid><pubDate>Sat, 14 Oct 2017 12:27:28 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In my last post, we covered &lt;a href=&quot;http://rants.broonix.ca/building-and-deploying-a-react-create-app-to-github-pages-using-travisci/&quot;&gt;creating a CI pipeline using TravisCI and GitHub Pages&lt;/a&gt;. Now let&apos;s add coverage reports to the build setup.&lt;/p&gt;
&lt;h1&gt;Setting up Coveralls.io&lt;/h1&gt;
&lt;p&gt;We will use &lt;a href=&quot;https://coveralls.io/&quot;&gt;coveralls.io&lt;/a&gt; to provide the reporting. They provide this for free to any public GitHub project. Head over to their site and create an account. After setting navigate to &apos;Add Repos&apos; and toggle it to on for your project.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now hit &apos;details&apos; to get the token for this repo. You can ignore the instructions shown here. You just want to token. They don&apos;t apply to a project built with &lt;code&gt;react-create-app&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Setting up TravisCI&lt;/h1&gt;
&lt;p&gt;Your token is private and you shouldn&apos;t check it into a public repo. So we will use TravisCI&apos;s build settings to pass it into our build system. This way the token remains secure.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Adding Coveralls to your project&lt;/h1&gt;
&lt;p&gt;You&apos;ll need to bring in the &lt;code&gt;coveralls&lt;/code&gt; package.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add --dev coveralls # yarn users
npm install --dev coveralls # npm users

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Project configuration changes&lt;/h1&gt;
&lt;p&gt;Now we need to make a few tweaks to the project files. First, let&apos;s make sure coverage gets ran with each build and that we upload the report to TravisCI.&lt;/p&gt;
&lt;p&gt;Add a new target to packages.json that defines a coveralls task to parse the coverage files.&lt;/p&gt;
&lt;p&gt;This is done in &lt;code&gt;.travis.yml&lt;/code&gt;. We will make two changes. The first tells the tests to include coverage reporting, the second uploads the coverage report after each build.&lt;/p&gt;
&lt;p&gt;Now commit and push these changes to GitHub. Once your build has completed you should see your first coverage report in Coveralls.io!&lt;/p&gt;
&lt;p&gt;There you have it, now every new build includes a coverage report. You can even take the badge generated in coveralls and add it to your projects &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can see this setup in action here: https://github.com/brookslyrette/react-sudoku-solver&lt;/p&gt;
&lt;p&gt;If you have any questions please leave them in the comments below. Happy Coding!&lt;/p&gt;</content:encoded><category>travis-ci</category><category>continuous-integration</category><category>coveralls-io</category><category>javascript</category><category>react</category></item><item><title>Building and Deploying a React Create App to GitHub Pages Using TravisCI</title><link>https://rants.broonix.ca/blog/building-and-deploying-a-react-create-app-to-github-pages-using-travisci/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/building-and-deploying-a-react-create-app-to-github-pages-using-travisci/</guid><pubDate>Fri, 13 Oct 2017 01:33:00 GMT</pubDate><content:encoded>&lt;p&gt;It is surprisingly simple to deploy a public &lt;code&gt;react-create-app&lt;/code&gt; built project on GitHub. In this post, we will look at how to set up a continuous integration pipeline using &lt;a href=&quot;http://travis-ci.org&quot;&gt;TravisCI&lt;/a&gt; and GitHub Pages.&lt;/p&gt;
&lt;h1&gt;Setting up Continuous Integration Builds&lt;/h1&gt;
&lt;p&gt;Continuous integration (&lt;strong&gt;CI&lt;/strong&gt;) makes deploying an updated app so simple you don&apos;t even have to think about it. After your commits are pushed to GitHub, a new build will get kicked off.&lt;/p&gt;
&lt;p&gt;You will need to create a TravisCI account. After creating your account. You&apos;ll be able to select which of your GitHub projects.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ll be setting up this CI pipeline for my &lt;a href=&quot;https://github.com/brookslyrette/react-sudoku-solver&quot;&gt;sudoku solver app&lt;/a&gt;. The first step is to enable builds for this project.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now that we have configured the project to be built. We have to tell TravisCI how to build our project. We will define how our project is built in a file called &lt;code&gt;.travis.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Commit this file and push your changes to GitHub. In a few minutes, you&apos;ll see an automatic build kicked off.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Setting up auto deployment to GitHub Pages&lt;/h1&gt;
&lt;p&gt;Now that we have the app building on every commit, let&apos;s auto deploy it to GitHub Pages. You will need to create a GitHub access token with the &lt;strong&gt;repo&lt;/strong&gt; security level in order for this to work. To create this token, login to GitHub and create a token here: https://github.com/settings/tokens/new&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Copy the value of this token. &lt;em&gt;This is a private token that gives access to your GitHub repos.&lt;/em&gt; &lt;strong&gt;Never check this token into a public repo.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we can tell TravisCI about this token. You can do so from the build screen. Navigate to settings and define a token called &lt;code&gt;github_token&lt;/code&gt; setting the value to the token you created.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now we can add the deploy step to our &lt;code&gt;.travis.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We also need to let our app know what it&apos;s URL will be. This is done in the projects &lt;code&gt;packages.json&lt;/code&gt; file. For my project this gets set to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;name&quot;: &quot;sudoku-solver&quot;,
  &quot;version&quot;: &quot;0.1.0&quot;,
  &quot;homepage&quot;: &quot;https://brookslyrette.github.io/react-sudoku-solver/&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^16.0.0&quot;,
    &quot;react-dom&quot;: &quot;^16.0.0&quot;,
    &quot;react-redux&quot;: &quot;^5.0.6&quot;,
    &quot;react-scripts&quot;: &quot;1.0.14&quot;,
    &quot;redux&quot;: &quot;^3.7.2&quot;
  },
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The format is &lt;code&gt;https://&amp;lt;usename&amp;gt;.github.io/&amp;lt;repository_name&amp;gt;/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Commit and push these changes to your repository. Once the build is complete your application will be deployed.&lt;/p&gt;
&lt;p&gt;You can see this in action here: https://github.com/brookslyrette/react-sudoku-solver&lt;/p&gt;
&lt;p&gt;If you have any questions please leave them in the comments below. Happy Coding!&lt;/p&gt;</content:encoded><category>react</category><category>travis-ci</category><category>github</category><category>continuous-integration</category></item><item><title>Getting started with React Navigation</title><link>https://rants.broonix.ca/blog/getting-started-with-react-navigation/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/getting-started-with-react-navigation/</guid><pubDate>Sun, 20 Aug 2017 19:51:40 GMT</pubDate><content:encoded>&lt;p&gt;I recently had some time to try &lt;a href=&quot;https://reactnavigation.org/&quot;&gt;React Navigation&lt;/a&gt;. It&apos;s a very nice routing solution to use in your &lt;a href=&quot;https://facebook.github.io/react-native/&quot;&gt;React Native&lt;/a&gt; applications. Let&apos;s walkthrough a simple example of using React Navigation in a project.&lt;/p&gt;
&lt;h3&gt;Project Setup&lt;/h3&gt;
&lt;p&gt;We will use &lt;code&gt;create-react-native-app&lt;/code&gt; for this demo. Ensure you have it installed via &lt;code&gt;npm install -g create-react-native-app&lt;/code&gt;. We will start by creating a new project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ create-react-native-app ReactNavigationDemo
# cd ReactNavigationDemo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run &lt;code&gt;yarn run ios&lt;/code&gt; you&apos;ll see a simple single page demo application.&lt;/p&gt;
&lt;h3&gt;Adding React Navigation&lt;/h3&gt;
&lt;p&gt;We will install React Navigation using yarkpkg:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ yarnpkg add react-navigation
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Adding Screens&lt;/h3&gt;
&lt;p&gt;React Navigation lets us easily route from one screen to an other. So let&apos;s start by creating a few simple screens to start.&lt;/p&gt;
&lt;p&gt;In the example above we create three components that render a view. Each of these will become a screen in our app that we can navigate to.&lt;/p&gt;
&lt;h2&gt;Creating a navigator&lt;/h2&gt;
&lt;p&gt;We will use a StackNavigator for this example.&lt;/p&gt;
&lt;p&gt;Above we create a StackNavigator with three screens. We provide it some coloring and a default title. I&apos;ll elaborate a bit more on styling the navigation bar later in this article.&lt;/p&gt;
&lt;p&gt;If you load up the app you should now see &lt;code&gt;Screen1&lt;/code&gt; with a navigation bar.&lt;/p&gt;
&lt;p&gt;Well, we are 1/2 there already. We now need to add some navigation so we can move between screens.&lt;/p&gt;
&lt;h2&gt;Navigating&lt;/h2&gt;
&lt;p&gt;Now we will expand Screen 1 so we can click a button to navigate to Screen2 or Screen2.&lt;/p&gt;
&lt;p&gt;As you can see we are binding the &lt;code&gt;onPress&lt;/code&gt; event to navigate to the other screens in our app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;this.props.navigation&lt;/code&gt; is not available to the child components on a given screen. You&apos;ll need to pass it down to the child components if you want to navigate from them.&lt;/p&gt;
&lt;h2&gt;Styling React Navigation&lt;/h2&gt;
&lt;p&gt;Here are what all style related properties I passed into the StackNavigator do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;navigationOptions: {
  headerTitle: &apos;Default App Title&apos;, // Title shown when a screen does not have a title
  headerTitleStyle: {
    color: &apos;#FFF&apos; // Color of the text in the header
  },
  headerStyle: {
    backgroundColor: &apos;#f57c00&apos; // Background color for bar
  },
  headerBackTitle: null, // Text for &apos;back&apos; button. `null` sets this to an empty string
  headerTintColor: &apos;#FFF&apos;, // Color of the text used in the back button/icons
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Completed Demo&lt;/h2&gt;
&lt;p&gt;And here you have it. A rather simple example of how to get started using React Navigation in a React Native project.&lt;/p&gt;
&lt;p&gt;If you want to check out this code and run it locally, I&apos;ve also uploaded this demo to GitHub: https://github.com/brookslyrette/ReactNavigationDemo&lt;/p&gt;</content:encoded><category>react</category><category>react-native</category><category>react-navigation</category><category>coding</category></item><item><title>AppleTV 4 Netflix UI Review</title><link>https://rants.broonix.ca/blog/appletv-4-netflix-ui-review/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/appletv-4-netflix-ui-review/</guid><pubDate>Sat, 08 Jul 2017 11:46:50 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve &lt;a href=&quot;http://rants.broonix.ca/apple-tv-4-review/&quot;&gt;posted before&lt;/a&gt; about how much I like the new AppleTV. Now that I have used it for a few months I wanted to expand on my review of the Netflix app.&lt;/p&gt;
&lt;h2&gt;The Good&lt;/h2&gt;
&lt;p&gt;The Netflix app has to be one of the most polished apps on the platform. The attention to details is wonderful. Navigating you can see the little details are obsessed over. Sound effects pan based on the direction you are scrolling. Highlighting a shows poster and using the Siri remote&apos;s touchpad and you can see the lighting move based on your input. Scrolling feels smoother and more natural that any other app on the platform.&lt;/p&gt;
&lt;h2&gt;The Bad&lt;/h2&gt;
&lt;p&gt;There are two bugs in this UI that drive me &lt;strong&gt;BONKERS&lt;/strong&gt;. With such a polished experience these US issues seem like odd stand outs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Issue #1&lt;/strong&gt;: After watching which you started playing on the &apos;show details&apos; screen. The UI returns to that same screen on the same episode. I get why this seems like the correct UI, but it is not. If I just finished that episode why show me a screen to play the same episode? This should be loaded with the &lt;em&gt;next&lt;/em&gt; episode ready to play.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Issue #2&lt;/strong&gt;: When returning to the &apos;home listing&apos; screen, the UI redraws your &apos;recently&apos; watched list. It might just be a slow connection but this seems off to me. There is a good 2-3 second pause after navigating back to this UI before it attempts to redraw. This leads me to try to navigate before the UI is ready.&lt;/p&gt;
&lt;h2&gt;Overall&lt;/h2&gt;
&lt;p&gt;Overall this is still the most polished app on AppleTV. It even outshines Apple&apos;s apps in overall UI and UX.&lt;/p&gt;</content:encoded><category>review</category><category>apple</category><category>tv</category></item><item><title>Apple TV 4 Review</title><link>https://rants.broonix.ca/blog/apple-tv-4-review/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/apple-tv-4-review/</guid><pubDate>Sat, 20 May 2017 11:38:25 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been waiting for ages to update my Apple TV 3 to the latest version. I recently finally found an excuse to upgrade! So here is my one week review of Apple&apos;s latest TV device.&lt;/p&gt;
&lt;p&gt;The new Apple TV really shows off its speed and power with faster start-up times. V3 took 10-15 seconds to start up at times, while V4 is on in a fraction of that time.&lt;/p&gt;
&lt;p&gt;The setup is ridiculously smooth. Hold your iPhone or device near your Apple TV and it downloads the configuration to your WiFi and other needed settings. All I had to do is enter my iTunes password and my new device was setup!&lt;/p&gt;
&lt;p&gt;The Siri remote is a cute addition. Voice to text makes input and searching very seamless on the new device. Siri at times can still get confused but that might be my Canadian accent.&lt;/p&gt;
&lt;p&gt;App, Apps and more Apps! Having an App store is a great addition. No more waiting for updates to maybe bring a new app or two. Most of the games are novel at best, but I do think kids will really enjoy them. I&apos;ll update my review after my nieces visit with their take on a few games.&lt;/p&gt;
&lt;p&gt;My only complaints so far are not Apple related. I find the V4 Netflix app a bit lacking compared to V3. No more ratings (0 - 5 stars), shows no longer show a list of related items, and you can no longer see &apos;My List&apos; from your Apple TV.&lt;/p&gt;
&lt;p&gt;All in all, I&apos;d recommend the upgrade if you are a heavy Apple TV user.&lt;/p&gt;</content:encoded><category>apple</category><category>tv</category><category>review</category></item><item><title>Git status in your BASH prompt</title><link>https://rants.broonix.ca/blog/git-status-in-your-bash-prompt/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/git-status-in-your-bash-prompt/</guid><pubDate>Wed, 22 Mar 2017 21:56:15 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;m a command line git user. I do 99% of my git related stuff without a GUI. I know the world is split about 50/50 between GUI users and command line users. But if you are like me, you&apos;ll love this post.&lt;/p&gt;
&lt;p&gt;Anytime you go to do any command with git, you&apos;ll start with &lt;code&gt;git status&lt;/code&gt;. You have to know what branch you are one and what the local state of your repo is. I&apos;ve been using &lt;code&gt;bash-git-prompt&lt;/code&gt; for a while to provide this info without needing to type anything. You can check out the project here: https://github.com/magicmonty/bash-git-prompt&lt;/p&gt;
&lt;h2&gt;Installing&lt;/h2&gt;
&lt;p&gt;Installation is dead simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~
git clone https://github.com/magicmonty/bash-git-prompt.git .bash-git-prompt --depth=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add this to your &lt;code&gt;~/.bash_profile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GIT_PROMPT_ONLY_IN_REPO=1
source ~/.bash-git-prompt/gitprompt.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;My customizations&lt;/h2&gt;
&lt;p&gt;I&apos;m not a huge fan of the default settings. So I added a few customizations to my setup. I disabled remote fetching to speed up the prompt. I prefer a single line for my prompt. I use stashes for work in progress code so I don&apos;t want my stash count included in the prompt.&lt;/p&gt;
&lt;p&gt;Customizations added in &lt;code&gt;~/.bash_profile&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GIT_PROMPT_FETCH_REMOTE_STATUS=0   #avoid fetching remote status
GIT_PROMPT_THEME=Single_line_Ubuntu #better theme
GIT_PROMPT_IGNORE_STASH=1 #ignore any stashes. 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This leaves my prompt looking like:
&lt;/p&gt;</content:encoded><category>coding</category><category>git</category><category>automation</category></item><item><title>Upgrading to react-router v4</title><link>https://rants.broonix.ca/blog/updated-upgrading-to-react-router-v4-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/updated-upgrading-to-react-router-v4-2/</guid><pubDate>Sun, 19 Mar 2017 14:09:00 GMT</pubDate><content:encoded>&lt;p&gt;My previous article on upgrading to react-router alpha4 is still getting a lot of traffic. Below is an updated version of that post for the release version of react-router v4.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I decided to take a little bit of time and migrate my &lt;a href=&quot;https://github.com/brookslyrette/reactit&quot;&gt;reactit&lt;/a&gt; demo app to v4 of react-router. I&apos;ll outline what changes I had to make for v4 as well as my thoughts on the new approach.&lt;/p&gt;
&lt;h4&gt;Migrating&lt;/h4&gt;
&lt;p&gt;Well, first things first. I installed the new version of react-router using npm.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save react-router
npm install --save react-router-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The big reason for this rewrite was &apos;Declarative Composability&apos;. This means that routes are now just components. You might want to re-read that last sentence. This works really well in my demo app. The components themselves now define the composition of the UI.&lt;/p&gt;
&lt;p&gt;Before all my routing was done in &lt;code&gt;index.js&lt;/code&gt;. This is the same routing you see in most v2 apps.&lt;/p&gt;
&lt;p&gt;The first thing I realised is that I can get rid of all this routing in &lt;code&gt;index.js&lt;/code&gt;. We can just render the app and compose the UI based on the route. After removing all the routing &lt;code&gt;index.js&lt;/code&gt; now looks like this:&lt;/p&gt;
&lt;p&gt;When using v2, it was the route in &lt;code&gt;index.js&lt;/code&gt; that injected the correct children to be rendered based on the route.&lt;/p&gt;
&lt;p&gt;This is where v4 starts to shine. Now I can compose the UI based on the path as part of the &lt;code&gt;App&lt;/code&gt; component. This is a much more concise way to define what is happening in the application.&lt;/p&gt;
&lt;p&gt;While this is a very simple example with only two routes, you can quickly see the amount of forward thinking that has been put into v4. I&apos;m already seeing some great use-cases that will be simpler (ex: side menus). It also feels more &apos;react&apos; to have your routes be components.&lt;/p&gt;
&lt;h4&gt;Testing&lt;/h4&gt;
&lt;p&gt;In order to give routes to my components during testing, I simply wrapped them in &lt;code&gt;&amp;lt;MemoryRouter/&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As always this code is available on GitHub. You can see the commit that updates &apos;reactit&apos; to react-router v4 right here: https://github.com/brookslyrette/reactit/commit/12c03798710b2e47eeb24a4b743d5cf5e7d2a86a&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category><category>router</category></item><item><title>Getting Started with React Native and Redux</title><link>https://rants.broonix.ca/blog/getting-started-with-react-native-and-redux/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/getting-started-with-react-native-and-redux/</guid><pubDate>Sun, 26 Feb 2017 13:38:42 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s been far too long that I&apos;ve been saying to myself: &lt;em&gt;&quot;Brooks you really should give React Native a try&quot;&lt;/em&gt;. So I&apos;ve whipped up a quick &apos;how-to&apos; article from my first foray into React Native. In this tutorial, we will create a new React Native project, integrate Redux and deploy on Android and iOS. To keep this first project simple we&apos;ll be remaking the Redux counter example.&lt;/p&gt;
&lt;h1&gt;Getting Started&lt;/h1&gt;
&lt;p&gt;You will have to install and configure React Native. You can find that documentation here: http://facebook.github.io/react-native/docs/getting-started.html&lt;/p&gt;
&lt;p&gt;I recommend setting up both Android and iOS if you want to see the final product on both platforms.&lt;/p&gt;
&lt;p&gt;We will call our project &apos;ReactNativeCounter&apos;. To create a new project use this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~$ react-native init ReactNativeCounter
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Integrating Redux&lt;/h1&gt;
&lt;p&gt;We will need Redux and React Redux. Redux integrates smoothly with React Native so you can pull these in via &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~$ cd ReactNativeCounter
~$ npm install redux
~$ npm install react-redux
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Design&lt;/h1&gt;
&lt;p&gt;We will cover 5 basic React/Redux concepts. In three sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Actions&lt;/li&gt;
&lt;li&gt;Stores/Reducers&lt;/li&gt;
&lt;li&gt;Containers and Components&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Actions&lt;/h2&gt;
&lt;p&gt;An action is sent when something has happened that will change our applications state. In this example, we will have 3 actions: &apos;&lt;em&gt;INCREMENT&lt;/em&gt;&apos;, &apos;&lt;em&gt;DEINCREMENT&lt;/em&gt;&apos; and &apos;&lt;em&gt;RESET&lt;/em&gt;&apos;.&lt;/p&gt;
&lt;h2&gt;Stores and Reducers&lt;/h2&gt;
&lt;p&gt;A store is an object that represents the state of your application. In our case, the store for our application is simple a &lt;code&gt;Number&lt;/code&gt;. This is the current counter value shown in the application.&lt;/p&gt;
&lt;p&gt;Reducers handle actions. They are responsible for updating the applications state based on the action being handled. Reducer functions &lt;em&gt;must be &lt;a href=&quot;https://en.wikipedia.org/wiki/Pure_function&quot;&gt;pure functions&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Containers and Components&lt;/h2&gt;
&lt;p&gt;To increase testability and ensure we properly separate concerns, I recommend using containers and components when creating renderable parts of a React application.&lt;/p&gt;
&lt;h3&gt;Containers&lt;/h3&gt;
&lt;p&gt;Containers are responsible for fetching data and rendering their components.&lt;/p&gt;
&lt;h3&gt;Components&lt;/h3&gt;
&lt;p&gt;Components are responsible for rendering data to the screen.&lt;/p&gt;
&lt;p&gt;While our application is simple this is a React best practice so I&apos;ve included it anyways.&lt;/p&gt;
&lt;h2&gt;App Layout&lt;/h2&gt;
&lt;p&gt;We&apos;ll add all our app related code in a new folder called &apos;App&apos; in the root of our newly created project. Create theses folders as you follow along with the code below.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;The code!&lt;/h1&gt;
&lt;p&gt;First, we will look at our reducer and the store it creates:&lt;/p&gt;
&lt;p&gt;The reducer handles 3 simple actions. We should also write some tests for our reducing function.&lt;/p&gt;
&lt;p&gt;Our reducer isn&apos;t that useful unless something generates actions. We will bind our actions and app state to our components via their containers.&lt;/p&gt;
&lt;p&gt;Now our component only needs to render its view and bind the action generating functions we passed into it.&lt;/p&gt;
&lt;p&gt;Now that we have all the moving pieces, we can wire them together. In &lt;code&gt;App.js&lt;/code&gt; we connect our application to the Redux store and render a &lt;code&gt;CounterContainer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now all that remains is configuring React Native to render this application for iOS and Android. There are two index files representing what each operating system will render. In our case we want both to render the application we designed above. This will be the code for BOTH &lt;code&gt;index.ios.js&lt;/code&gt; and &lt;code&gt;index.android.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have already configured Xcode and Android Studio you can see the resulting by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~$ react-native run-ios      #Launch iOS
~$ react-native run-android  #Launch Android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clicking the up and down buttons will change the counter, clicking the counter will reset it to zero.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If you want to check out the code it&apos;s here on GitHub: https://github.com/brookslyrette/ReactNativeCounter&lt;/p&gt;
&lt;p&gt;Happy Coding!&lt;/p&gt;</content:encoded><category>react</category><category>react-native</category><category>redux</category></item><item><title>An Another New Year</title><link>https://rants.broonix.ca/blog/a-new-year-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/a-new-year-2/</guid><pubDate>Mon, 02 Jan 2017 13:30:10 GMT</pubDate><content:encoded>&lt;p&gt;So 2016 was a year for change for me. I started blogging weekly, quit my job at Dotsub to join Amazon and achieved some of my personal finance goals.&lt;/p&gt;
&lt;p&gt;Looking back at 2016, I think I accomplished what I set out to do, be happier and enjoy life more. Blogging was once a chore but now I enjoy typing out a post with a morning coffee. Leaving Dotsub was a hard decision but so far it has been for the best.&lt;/p&gt;
&lt;p&gt;This year I&apos;m setting a few more goals than last. I&apos;m also blogging them to have a better record to compare to next new year. So onward to 2017!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I want to find time to exercise 4-5 times a week.&lt;/strong&gt; This use to be part of my routine last year. I could make excuses, but I simply need to get back to this!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Improve my guitar playing.&lt;/strong&gt; I&apos;ve been playing the guitar since I was a teenager. My skill level plateaued a long time ago. This is only because I don&apos;t use a structured practice routine. I just pick up the guitar and noodle. This year I&apos;ll set out to learn a few solos from my guitar heroes. This should improve my guitar chops.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I want to try using daily meditation.&lt;/strong&gt; Maybe I&apos;ve been listening too much Tim Ferriss, but I think meditation might be right for me. I&apos;m addicted to this always on always connected existence we call life. Learning how to disconnect, even temporarily, will be good for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ensure I stay in touch with old friends.&lt;/strong&gt; I&apos;m the worst at this, I&apos;m such an introvert that I often forget to cultivate my existing friendships. I&apos;m making it a goal to change this habit! In 2017 I&apos;ll be sure to stay in touch with those people I enjoy talking to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take more time to enjoy the little things.&lt;/strong&gt; Be it a walk in the woods with my dog, spending time with family or any other little joy. I need to more actively acknowledge these small moments that I enjoy. What is life if it is not a small series of joys?&lt;/p&gt;
&lt;p&gt;I look forward to a great 2017. I&apos;ll revisit this post next year to see how I did. Happy New Year!&lt;/p&gt;</content:encoded><category>me</category><category>blog</category></item><item><title>Git commands everyone should know</title><link>https://rants.broonix.ca/blog/git-commands-everyone-should-know/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/git-commands-everyone-should-know/</guid><pubDate>Fri, 30 Dec 2016 13:45:43 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Git&lt;/strong&gt; is a phenomenal source control system. Having used some of its predecessors, &lt;strong&gt;SVN&lt;/strong&gt;, &lt;strong&gt;CVS&lt;/strong&gt; and &lt;strong&gt;SourceSafe&lt;/strong&gt;, I&apos;m always impressed at how much better things have gotten. If you have not seen Linus Torvalds talk about why he created git you should take an hour and watch it.&lt;/p&gt;
&lt;p&gt;I do think too many developers just gloss over Git. They learn the basic commands they need day to day but miss out on some of the robust features Git has.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;So here is my list of my most used and helpful Git commands.&lt;/p&gt;
&lt;h2&gt;Most used&lt;/h2&gt;
&lt;p&gt;I checked my bash history and the list is exactly what you&apos;d expect. These are the commands everyone knows and uses day to day:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git status  #show the status of your working copy

git checkout &amp;lt;branch_name&amp;gt; #switch to another branch

git checkout -b &amp;lt;new_branch_name&amp;gt; #create a new branch

git add . #add everything from your working copy to be committed

git commit -m &quot;&amp;lt;commit message&amp;gt;&quot; #commit changes 

git pull #push your copy to the remote repository 

git push #pull any changes from the remote repository
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Most useful&lt;/h2&gt;
&lt;p&gt;Here is my list of commands I use much less often but are very useful in a pinch.&lt;/p&gt;
&lt;h3&gt;Merging branches&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git merge --squash &amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should be using a branching model during development. This how to merge one branch into another. &lt;code&gt;--squash&lt;/code&gt; is optional. Using it will squash all the commits into a single commit on the target branch.&lt;/p&gt;
&lt;h3&gt;Merging remote branch&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git merge origin/&amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not need to switch to and update a branch before merging it. Just &lt;code&gt;git pull&lt;/code&gt; and merge the remote URL.&lt;/p&gt;
&lt;h3&gt;Squashing Commits&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git rebase -i HEAD~&amp;lt;number_of_commits&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Forgot a file in the last commit? Did you do a lot of commits to track your work but want to roll them into just one commit? Use rebase to squash them together!&lt;/p&gt;
&lt;h3&gt;Cherry picking a commit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git cherry-pick &amp;lt;commit_hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Want to bring over one commit from another branch into yours? Well, that&apos;s what cherry pick is for! It&apos;ll just bring over the selected commit to your branch.&lt;/p&gt;
&lt;h3&gt;Resetting your local copy&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git reset --(hard/soft) (&amp;lt;commit_hash&amp;gt;/&amp;lt;branch_name&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;ve all completely fucked up our local copy at least once. It&apos;s always fixable with git. Resetting hard will drop your changes, while soft will keep them as staged files. You can also use remote URL&apos;s here to reset to the server&apos;s working copy.&lt;/p&gt;
&lt;h3&gt;Create a patch file&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git diff &amp;gt; a_file.patch 
git diff --cached &amp;gt; a_file.patch # if staged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Working remotely and want to have someone look at some code for you? Send them a patch file.&lt;/p&gt;
&lt;h3&gt;Apply a patch file&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git apply --stat a_file.patch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply a patch to your local copy.&lt;/p&gt;
&lt;h2&gt;Stashing&lt;/h2&gt;
&lt;p&gt;This is my most used Git feature. I&apos;d always surprised when I see people not using stashes. Stashes gives you a way to save local changes then restore them later.&lt;/p&gt;
&lt;p&gt;This makes a few git tasks much easier. Need to switch tasks to work on something else? &lt;em&gt;Stash your changes!&lt;/em&gt; Need to merge a branch into your local copy but you have active changes? &lt;em&gt;Stash your changes!&lt;/em&gt; Tried an idea that didn&apos;t work out but what to revisit the code later? &lt;em&gt;Stash your changes!&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Saving a stash&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git stash save &quot;Optional Description&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Listing stashes&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git stash list
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Apply a stash&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git stash apply &amp;lt;stash_index&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you have it, a few commands to add to your git toolkit.&lt;/p&gt;</content:encoded><category>git</category><category>coding</category><category>tips</category></item><item><title>Netflix and it&apos;s missing content</title><link>https://rants.broonix.ca/blog/netflix-and-its-missing-content/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/netflix-and-its-missing-content/</guid><pubDate>Fri, 23 Dec 2016 12:32:58 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been a long-time Netflix user. I signed up back in 2010 when they first launched their Canadian service. It&apos;s always been a product I&apos;ve told people they should be using, but as we approach 2017 I&apos;m really worried about their future offering.&lt;/p&gt;
&lt;p&gt;First some background. When I signed up back in 2010 there was very little content in the Canadian market. I nearly cancelled after the first three free months. I really only stayed signed up because I discovered how to access the US library. From 2011 to 2015 I was a border hopper. Accessing the huge content library Netflix offered in the US.&lt;/p&gt;
&lt;p&gt;My viewing habits might not be the norm. I always have a show that the wife and I watch together. But late at night, I love to watch random documentaries, off-beat shows and even a cheesy action movie or comedy once and a while.&lt;/p&gt;
&lt;p&gt;This was simple and easy on US Netflix. It was workable on Canadian Netflix, but it&apos;s getting harder to do. Why you might ask. It&apos;s simple Netflix has bet huge on their own produced content.&lt;/p&gt;
&lt;p&gt;Netflix has invested billions of dollars to produce over 100&apos;s shows this year. They have bet big on making their own content. In order to do this, they have had to cut spending elsewhere.&lt;/p&gt;
&lt;p&gt;The amount of quality content on Netflix that is not owned by Netflix is slowly dwindling. Right now only a few are noticing it, but if this trend continues it&apos;ll be interesting to see what reaction the user base has.&lt;/p&gt;
&lt;p&gt;My fear is that Netflix is becoming what we all tried to get away from with Cable. Large media companies (ABC, NBC, CBS) were once able to ram whatever crap down our throat they decided to put in a timeslot. There was no real alternative. Now you see Netflix, CraveTV, Shomi and most competitors in this market setting themselves up to be the new gatekeepers of media.&lt;/p&gt;</content:encoded><category>netflix</category></item><item><title>New URL!</title><link>https://rants.broonix.ca/blog/new-url/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/new-url/</guid><pubDate>Sun, 18 Dec 2016 13:06:05 GMT</pubDate><content:encoded>&lt;p&gt;I promised myself that if I blogged for an entire year, I&apos;d get a proper domain for my blog. Well, that new URL should be active now! Welcome to &lt;strong&gt;rants.broonix.ca&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;My posts are likely going to become monthly. While I still love blogging, I have a bit less free time with my new job.&lt;/p&gt;</content:encoded><category>me</category><category>blog</category></item><item><title>One Week Post Remote</title><link>https://rants.broonix.ca/blog/one-week-post-remote/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/one-week-post-remote/</guid><pubDate>Sat, 26 Nov 2016 12:32:56 GMT</pubDate><content:encoded>&lt;p&gt;So I did it, just like the &lt;em&gt;normal&lt;/em&gt; people. I got up a drove to an office 5 days this week. After so long working remotely it is quite an adjustment. I figured it&apos;d be good to follow up my &lt;a href=&quot;http://broonix-rants.ghost.io/remote-working-6-years-later/&quot;&gt;Remote Working&lt;/a&gt; post with an update after working at an office again.&lt;/p&gt;
&lt;h2&gt;The Bad&lt;/h2&gt;
&lt;p&gt;Commuting still sucks. This has to be the worst part. I do know this is my own fault, I knew when accepting the offer that the office would be the complete opposite side of town. I have been trying to make my commute time useful by listening to podcasts. I&apos;m quickly running out of podcasts!&lt;/p&gt;
&lt;h2&gt;The Good&lt;/h2&gt;
&lt;p&gt;There so far is a lot more good than bad. I love the work thus far. Our team is a great group of people.&lt;/p&gt;
&lt;p&gt;It&apos;s great to have a reason to leave the house. I know that sounds silly, but every person working remotely has had a week whereby Wednesday they are not sure if that have or have not left the house. Sometimes I&apos;d even go work at a coffee shop just to make sure to get out.&lt;/p&gt;
&lt;p&gt;Getting up to speed is also much easier. It&apos;s great to just have to walk a few feet or have someone be able to come over to your desk. No more fumbling with screen shares or posting screenshots to slack.&lt;/p&gt;
&lt;p&gt;So far I think I made the right decision. It&apos;s going to take a while to adjust. I do have the bonus of a job that lets you work from home. So if I really miss being remote, I can just work from home.&lt;/p&gt;</content:encoded><category>remote</category><category>work</category></item><item><title>No longer working Remotely</title><link>https://rants.broonix.ca/blog/no-longer-working-remotely/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/no-longer-working-remotely/</guid><pubDate>Sat, 12 Nov 2016 13:37:31 GMT</pubDate><content:encoded>&lt;p&gt;So after 7+ years I&apos;ll be going into an office to work. I know that is surprising since everyone wants to work remotely these days. While I&apos;ve enjoyed the experience, I think it&apos;s time to switch back. I know commuting across the city will suck, but there will be some nice changes.&lt;/p&gt;
&lt;p&gt;Leaving the house is something most people do every day. When you work remotely you often don&apos;t. Sure you might work a few hours from a coffee shop, but you can&apos;t stay there 4+ hours. You get a little bit of cabin fever after a while. My wife is, of course, the opposite. Having to leave home to go to work every day, she looks forward to getting back home after work. I&apos;m the complete opposite, having been in the house all day, the first thing I want to do after work is to get out of the house.&lt;/p&gt;
&lt;p&gt;Collaboration is almost never real-time when you work remotely. Sure you can schedule calls, use slack, and screen share, but it is just not the same as being in the same room collaborating on a whiteboard. I didn&apos;t realize how much I missed this until I started having to whiteboard answers in interviews.&lt;/p&gt;
&lt;p&gt;The same goes for having off tasks. It&apos;s a lot more complicated to hand off work remotely. This is compounded when there are also timezone differences. You might have a 2-3 hour window at most to interact with some of your coworkers. You also might have to wait until the next day to get a reply for even a simple question.&lt;/p&gt;
&lt;p&gt;The lines between work and home get blurred no matter how hard you try. I &lt;a href=&quot;http://broonix-rants.ghost.io/remote-working-6-years-later/&quot;&gt;did lots&lt;/a&gt; to help keep the line between work and home well defined, but there is always some cross-over. My office never gets used on the weekend, simply because when I&apos;m in there I feel like I&apos;m working. Even if I&apos;m coding up something fun, it feels like work. There is even some research that shows this increases &lt;a href=&quot;http://fusion.net/story/212971/working-from-home-telecommuting-sucks/&quot;&gt;work related stress&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Maybe this is all a case of &lt;em&gt;&apos;The grass is always greener on the other side&apos;&lt;/em&gt;. I&apos;ll let you know if it is in a month or two!&lt;/p&gt;</content:encoded><category>remote</category><category>work</category><category>me</category></item><item><title>My thoughts on the new MacBook Pros</title><link>https://rants.broonix.ca/blog/my-thoughts-on-the-new-macbook-pros/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/my-thoughts-on-the-new-macbook-pros/</guid><pubDate>Sun, 06 Nov 2016 13:21:00 GMT</pubDate><content:encoded>&lt;p&gt;Apple announced new MacBook Pros last week. The new models are caused quite a stir in the tech community. While Pros feel abandoned by the new MacBook, Apple is saying they have record pre-orders.&lt;/p&gt;
&lt;p&gt;The must-read review, in my opinion, is by Owen Williams: &quot;&lt;a href=&quot;https://medium.com/charged-tech/apple-just-told-the-world-it-has-no-idea-who-the-mac-is-for-722a2438389b#.t2q06nao1&quot;&gt;Apple just told the world it has no idea who the Mac is for&lt;/a&gt;&quot;. Most of my complaints fall right in line with Owens.&lt;/p&gt;
&lt;h4&gt;Touchbar&lt;/h4&gt;
&lt;p&gt;Yea, I get that it&apos;s a &apos;cute&apos; addition, but did you see the UX of using it? Here let me hunch over my computer staring at a little strip of light below the screen. It&apos;s 2016 and the best Apple can do is a small strip of screen interaction? There have been touchscreen Windows models for a few years and this is all Apple add to &apos;revolutionize&apos; the new MacBook?&lt;/p&gt;
&lt;p&gt;How is it possible to ship something like that without a few Apple developers mentioning how shitty it is to lose the hardware escape key?&lt;/p&gt;
&lt;p&gt;They did keep a model that retains the hardware escape key and function keys. So at least there is that. Most Pro&apos;s I know will likely go with that option. So the revolutionary &apos;touchbar&apos; is just not in line with what MacBook Pro users really need.&lt;/p&gt;
&lt;h4&gt;Cables, Adapters, and Ports&lt;/h4&gt;
&lt;p&gt;Apple is right, USB-C is likely the future. The only problem is, the future isn&apos;t now. No, people do not want to shell out $300 bucks for a pile of adapters they have to carry around!&lt;/p&gt;
&lt;p&gt;I&apos;ll use myself as an example, when working I have the following plugged into my MacBook: 2 Display Port displays, MagSafe Charger, USB Display, USB iPhone connection and my headphone jack is plugged into my office speakers. Let&apos;s price these adapters out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2 x Thunderbolt 3 (USB-C) to Thunderbolt 2 Adapter &lt;strong&gt;$70.00&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;USB-C to Lightning Cable (1 m) &lt;strong&gt;$25.00&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;USB-C to USB Adapter
&lt;strong&gt;$10.00&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;USB-C Digital AV Multiport Adapter
&lt;strong&gt;$59.00&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That gives me the 5 adapters I need for my office, and an AV Multiport adapter to carry around. Since adding a goddamn HDMI port wasn&apos;t in Apple&apos;s design plans.&lt;/p&gt;
&lt;p&gt;Don&apos;t even get me started on how the hell the MacBook Pro got a headphone jack, while the device &lt;strong&gt;designed to play&lt;/strong&gt; music is without one! I&apos;m glad they included one. I&apos;m only angry since no headphone jack is one of the number one reasons I&apos;ve skipped the iPhone seven. I guess if I&apos;m already trucking around my 5 MacBook adapters one more for any iPhone isn&apos;t that much extra.&lt;/p&gt;
&lt;h4&gt;Why?&lt;/h4&gt;
&lt;p&gt;Why this is happening is pretty obvious if you ask me. The Mac product line use to fall into 3 categories for 3 distinct types of users.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MacBook Air&lt;/strong&gt;: Light and sexy with enough power for a basic user&apos;s needs. This is what your mom or that guy in marketing needs. Enough to check your emails and update spreadsheets and documents.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MacBook&lt;/strong&gt;: The laptop for the &apos;normals&apos;. The amateur hobbyists. They might write a little code, they might do photography or video on the side. They need a bit more power than the Air, but since they don&apos;t do their day to day work on this computer they don&apos;t need to upgrade to the Pro.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MacBook Pro&lt;/strong&gt;: The machine for the makers. The coders, the photographers, the video editors. Those who use their machine to create and make new things. They need high powered machines to get shit done.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the new MacBook Apple has one and only one idea in mind. &lt;em&gt;&quot;How can we get people who use to buy our $1,500 laptop to buy a $2,000 laptop?&quot;&lt;/em&gt;. This is why sales numbers are up. However, in doing so they have abandoned their previous core audience.&lt;/p&gt;</content:encoded><category>apple</category><category>macbook</category></item><item><title>Gource: Visualize a Repositories Commits</title><link>https://rants.broonix.ca/blog/gource-visualize-a-repositories-commits/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/gource-visualize-a-repositories-commits/</guid><pubDate>Thu, 03 Nov 2016 20:31:08 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Who doesn&apos;t love interesting data visualizations? One of the neatest looking ones is visualizing commits to a git repository. &lt;a href=&quot;http://gource.io/&quot;&gt;Gource&lt;/a&gt; is my go-to tool to create these types of visualizations.&lt;/p&gt;
&lt;p&gt;Here is a step by step on how to generate a video on MacOS.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;First, you&apos;ll need to install gource and ffmpeg. I use &lt;a href=&quot;http://brew.sh/&quot;&gt;brew&lt;/a&gt; as my package manager. Installing gource and ffmpeg is as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew install ffmpeg
$ brew install gource
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might take a long time depending on what other packages you have installed.&lt;/p&gt;
&lt;h3&gt;Using Gource&lt;/h3&gt;
&lt;p&gt;Gource is very simple to use. Just type &lt;code&gt;gource&lt;/code&gt; in the root of any git project.&lt;/p&gt;
&lt;p&gt;There are also a ton of options you can enable and disable. I recommend setting a better resolution, disabling the bloom effect and hiding filenames. The bloom effect is horrible when you create a video. It gets very &lt;a href=&quot;https://www.youtube.com/watch?v=r6Rp-uo6HmI&quot;&gt;pixelated when the video is compressed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You also might want to tweak &lt;code&gt;seconds-per-day&lt;/code&gt; and &lt;code&gt;auto-skip-seconds&lt;/code&gt; depending on how old the repo is.&lt;/p&gt;
&lt;h3&gt;Making a video&lt;/h3&gt;
&lt;p&gt;You can just pipe the output from gource into ffmpeg by doing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gource -1280x720 -o - | ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i - -vcodec libx264 -preset ultrafast -pix_fmt yuv420p -crf 1 -threads 0 -bf 0 &amp;lt;filename&amp;gt;.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is one of the main repositories I committed to from 2007 - 2016. I used these settings to highlight my name and make it go a bit faster.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gource --hide bloom,filenames,dirnames --follow-user &quot;Brooks Lyrette&quot; --file-idle-time 15 --seconds-per-day 0.075 --auto-skip-seconds 1  --highlight-users --stop-at-end -1280x720 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is it just me or is everything a bit more dramatic when you set it to a classical waltz?&lt;/p&gt;</content:encoded><category>coding</category><category>git</category><category>gource</category></item><item><title>Dotsub: Any Video any Language</title><link>https://rants.broonix.ca/blog/dotsub-any-video-any-language/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/dotsub-any-video-any-language/</guid><pubDate>Wed, 02 Nov 2016 17:43:21 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;So after 9+ years at Dotsub, I figured it would be fitting to write a small post about my time there.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I can&apos;t believe it was that long ago that I joined a small team here in Ottawa to build the second version of &lt;a href=&quot;http://dotsub.com&quot;&gt;Dotsub.com&lt;/a&gt;. Dotsub&apos; founder Michael Smolens knew someone here in Ottawa that I had worked with on a consulting project. A few phone conversations later, I was ready to take the plunge and join a small startup.&lt;/p&gt;
&lt;p&gt;There was a PHP prototype site in place, our plan was simple. Rewrite the site to be scalable and more extensible. Here is what Dotsub looked like in 2007.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In a few months, we hacked together a great foundation to build on. Two of us hacked away at the user interface and backend code, while one other hacked at the flash player. People don&apos;t remember how much more complex web video was in 2008. There was no &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag, CDNs were insanely expensive, there were very few decent flash players. Accessibility on the web seemed like an afterthought at best. Most of the video players on the market did event support subtitles/captions.&lt;/p&gt;
&lt;p&gt;All our effort paid off in our maiden release. Just before we planned to release the first version of our implementation of Dotsub, a video went viral. Now the PHP version of the site wasn&apos;t that bad, but it was completely unable to handle any type of load. We had to quickly release our version before the PHP site melted. One of the other developers did some brilliant tweaks to the PHP site to buy us a week or two to release.&lt;/p&gt;
&lt;p&gt;Then a few weeks later, we released our version of Dotsub.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;We stayed up all night to migrate the data and direct traffic to the new site. On release, the site endlessly crashed. After a few hours of cursing and coffee, we had isolated the issue. Turns out we had a single line of code that was getting needlessly executed and eating up all the CPU cycles. It just goes to show you, no first release is ever smooth.&lt;/p&gt;
&lt;p&gt;While a lot has changed since that initial release, it was this version that was the foundation for so many of the amazing things we accomplished at Dotsub.&lt;/p&gt;
&lt;p&gt;There are too many exciting things to list them all. So I&apos;ll pick out a few of my personal highlights.&lt;/p&gt;
&lt;p&gt;Most people don&apos;t know this, but a Dotsub video titled &lt;em&gt;&apos;Twitter in Plain English&apos;&lt;/em&gt; was on Twitters homepage.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This was before Oprah joined Twitter. The &apos;Watch a video!&apos; button played this video:&lt;/p&gt;
&lt;p&gt;This video has over &lt;strong&gt;9 million views&lt;/strong&gt; and &lt;strong&gt;99 different translations&lt;/strong&gt;. All generated by Dotsub users and seen by millions of Twitter visitors.&lt;/p&gt;
&lt;p&gt;We also participated in the TED Talks translation project. Translating thousands of TED Talks into hundreds of languages. This was all done by tens of thousands of TED volunteers. This project truly embodied Dotsub mission. We helped to spread knowledge across the barriers of language and culture. Helping them share their content with tens of millions of non-English speakers around the world.&lt;/p&gt;
&lt;p&gt;Those are just two examples of the global impact  Dotsub and it&apos;s users have made. From my back of the envelope math, we have shown captions almost 1 billion people in the world.&lt;/p&gt;
&lt;p&gt;On top of all the great work completed by our user community, don&apos;t forget we are a business. Dotsub&apos;s platform coupled with our amazing professional services. Have provided captions and translations to some of the largest companies in the world. Amazon, Google, Microsoft, IBM, and many others have used Dotsub to caption and translate their videos.&lt;/p&gt;
&lt;p&gt;While my journey at Dotsub has come to an end, I&apos;ll never forget the amazing things we accomplished.&lt;/p&gt;</content:encoded><category>work</category><category>me</category><category>dotsub</category></item><item><title>Exciting new opportunity</title><link>https://rants.broonix.ca/blog/exciting-new-opportunities-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/exciting-new-opportunities-2/</guid><pubDate>Thu, 27 Oct 2016 20:40:55 GMT</pubDate><content:encoded>&lt;p&gt;I know I haven&apos;t been giving my blog the amount of time it deserves of late! Things have just been crazy busy, I haven&apos;t been able to keep up my previous pace of a post a week.&lt;/p&gt;
&lt;p&gt;Why might you ask? Well, I&apos;m proud to say I&apos;ve taken a new job! I&apos;ll be joining the &lt;em&gt;Amazon Alexa&lt;/em&gt; team. I&apos;ll be starting in a few weeks.&lt;/p&gt;
&lt;p&gt;Working on the Internet of things (IoT) ecosystem will be an exciting new challenge. I can&apos;t wait to see what I get to help create!&lt;/p&gt;
&lt;p&gt;My blog content might change a little in the next few months! Don&apos;t be surprised to see a bit more mobile and IoT content.&lt;/p&gt;</content:encoded><category>work</category><category>me</category></item><item><title>Upgrading to react-router v4</title><link>https://rants.broonix.ca/blog/upgrading-to-react-router-v4/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/upgrading-to-react-router-v4/</guid><pubDate>Sun, 09 Oct 2016 15:59:08 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This article was written when &lt;code&gt;react-router alpha4&lt;/code&gt; was the most recent release.&lt;/strong&gt; If you are using the &lt;code&gt;4.0.0&lt;/code&gt; release or higher please read this post: http://rants.broonix.ca/updated-upgrading-to-react-router-v4-2/&lt;/p&gt;
&lt;p&gt;About a month ago, the react-router team released version 4 of react-router. This version is a complete rewrite of how routing is handled. This new version has been mostly well received, with a few vocal complaints.&lt;/p&gt;
&lt;p&gt;I decided to take a little bit of time and migrate my &lt;a href=&quot;https://github.com/brookslyrette/reactit&quot;&gt;reactit&lt;/a&gt; demo app to v4 of react-router. I&apos;ll outline what changes I had to make for v4 as well as my thoughts on the new approach.&lt;/p&gt;
&lt;p&gt;==&lt;strong&gt;Note:&lt;/strong&gt; At this time react-router v4 is &lt;em&gt;&lt;strong&gt;alpha&lt;/strong&gt;&lt;/em&gt;. You should not be using this in production. If you are using this post as a migration guide, you can&apos;t say I didn&apos;t warn you!==&lt;/p&gt;
&lt;h4&gt;Migrating&lt;/h4&gt;
&lt;p&gt;Well, first things first. I installed the new version of react-router using npm.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save react-router@next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The big reason for this rewrite was &apos;Declarative Composability&apos;. This means that routes are now just components. You might want to re-read that last sentence. This works really well in my demo app. The components themselves now define the composition of the UI.&lt;/p&gt;
&lt;p&gt;Before all my routing was done in &lt;code&gt;index.js&lt;/code&gt;. This is the same routing you see in most v2 apps.&lt;/p&gt;
&lt;p&gt;The first thing I realized is that I can get rid of all this routing in &lt;code&gt;index.js&lt;/code&gt;. We can just render the app and compose the UI based on the route. After removing all the routing &lt;code&gt;index.js&lt;/code&gt; now looks like this:&lt;/p&gt;
&lt;p&gt;When using v2, it was the route in &lt;code&gt;index.js&lt;/code&gt; that injected the correct children to be rendered based on the route.&lt;/p&gt;
&lt;p&gt;This is where v4 starts to shine. Now I can compose the UI based on the path as part of the &lt;code&gt;App&lt;/code&gt; component. This is a much more concise way to define what is happening in the application.&lt;/p&gt;
&lt;p&gt;While this is a very simple example with only two routes, you can quickly see the amount of forward thinking that has been put into v4. I&apos;m already seeing some great use-cases that will be simpler (ex: side menus). It also feels more &apos;react&apos; to have your routes be components.&lt;/p&gt;
&lt;h4&gt;Testing&lt;/h4&gt;
&lt;p&gt;In order to give routes to my components during testing, I simply wrapped them in &lt;code&gt;&amp;lt;MemoryRouter/&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As always this code is available on GitHub. You can see the commit that updates &apos;reactit&apos; to react-router v4 right here: https://github.com/brookslyrette/reactit/commit/9cba5c3ac3205c7e3a36620a809d3c00fdb5ec20&lt;/p&gt;</content:encoded><category>react</category><category>router</category><category>javascript</category></item><item><title>Animations with React</title><link>https://rants.broonix.ca/blog/animations-with-react/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/animations-with-react/</guid><pubDate>Sun, 02 Oct 2016 14:26:38 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been working on a demo app to keep my &apos;react fu&apos; strong. You can check out the app here: https://github.com/brookslyrette/reactit/&lt;/p&gt;
&lt;p&gt;The app is a react front end for Reddit. It uses a standard stack that includes react-router and redux.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This week I&apos;m going to look at how to add some simple animations to this app. React provides a library to use for these types of animations: https://facebook.github.io/react/docs/animation.html&lt;/p&gt;
&lt;p&gt;First, install the package with npm.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install react-addons-css-transition-group
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now lets defined the CSS for this transition. When animating two classes will be added to the control. &lt;code&gt;.{transitionName}-{transitionType}&lt;/code&gt; first, then  &lt;code&gt;.{transitionName}-{transitionType}-active&lt;/code&gt;. The appear transition will start with opacity &lt;code&gt;0.01&lt;/code&gt; and transition to being fully visible.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.items-appear {
  opacity: 0.01;
}

.items-appear.items-appear-active {
  opacity: 1;
  transition: opacity 300ms ease-in;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then to enable this transition we need to add it to the components markup. I&apos;ll be animating the &lt;code&gt;ListingItem.js&lt;/code&gt; component. Now that the transition timeout in the component and CSS should match. Here they are both set to 300ms.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render() {
  return (
    &amp;lt;ReactCSSTransitionGroup transitionName=&quot;items&quot; 
          transitionAppear={true} transitionAppearTimeout={300}&amp;gt;
       {/* old jsx for control */}
    &amp;lt;/ReactCSSTransitionGroup&amp;gt;
  );
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the commit that adds this feature my reactit demo app: https://github.com/brookslyrette/reactit/commit/1a8adb231ad63f5083eb12e39ccd388e107567b3&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category><category>reddit</category></item><item><title>Development Workflow</title><link>https://rants.broonix.ca/blog/development-workflow/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/development-workflow/</guid><pubDate>Sun, 18 Sep 2016 12:25:26 GMT</pubDate><content:encoded>&lt;p&gt;This week GitHub released some great &lt;a href=&quot;https://github.com/blog/2256-a-whole-new-github-universe-announcing-new-tools-forums-and-features&quot;&gt;new features&lt;/a&gt;. The new review feature finally makes code review a first class member of GitHub&apos;s feature set. This is one of the final bits we wanted for our build process at work.&lt;/p&gt;
&lt;p&gt;I&apos;ve blogged about bits and pieces of our workflow, but this week I&apos;m going to outline the entire process end to end.&lt;/p&gt;
&lt;p&gt;All our developers use a &apos;branch and pull request&apos; model. Every change or new feature is developed on its own branch in our git repository. No developer can commit their changes to the main branch. They can only submit a pull request to be merged into the main branch.&lt;/p&gt;
&lt;p&gt;Any pull request can only be merged if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The build was successful on our CI server.&lt;/li&gt;
&lt;li&gt;The build passes PMD, EsLint, CheckStyle and FindBugs checks.&lt;/li&gt;
&lt;li&gt;The code has been reviewed and accepted by one other developer on the team.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This ensures two sets of eyes have looked at an approved every change. PMD and FindBugs provide static analysis for bugs and code quality issues. While EsLint and CheckStyle ensure our code meets a common set of formatting. We also check the code coverage numbers for every build.&lt;/p&gt;
&lt;p&gt;When set up in GitHub. Our branch looks like this.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now any pull request (PR) can only be merged if all the above is met. Here is an example PR where all checks passed but code review found one issue that blocks a PR from being mergeable.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If you are wondering about how these checks or process is implemented see some of my past posts:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://broonix-rants.ghost.io/java-code-coverage-with-gradle-and-jacoco-2/&quot;&gt;Java: Code coverage with Gradle and JaCoCo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://broonix-rants.ghost.io/migrating-from-bamboo-to-travis-ci/&quot;&gt;Migrating to Travis-CI from Bamboo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://broonix-rants.ghost.io/updating-github-status-from-bamboo/&quot;&gt;Updating GitHub Status from Bamboo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://broonix-rants.ghost.io/automated-acceptance-tests/&quot;&gt;Automated Acceptance Testing with Selenium&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://broonix-rants.ghost.io/continuous-deployment-with-spring-boot-and-travis/&quot;&gt;Continuous Deployment with Spring-boot and Travis&lt;/a&gt;&lt;/p&gt;</content:encoded><category>coding</category><category>automation</category><category>gradle</category></item><item><title>Renaming Database Constraints with Liquibase</title><link>https://rants.broonix.ca/blog/renaming-database-constraints-with-liquibase/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/renaming-database-constraints-with-liquibase/</guid><pubDate>Tue, 13 Sep 2016 21:36:32 GMT</pubDate><content:encoded>&lt;p&gt;We used hibernate to set up our initial database schema. Hibernate generates constraint names like: &lt;code&gt;fk_jwy3ip299dvht6bhrf7kxnmap&lt;/code&gt; and &lt;code&gt;fk_bc7w4arbgsiasoufirt1ec0na&lt;/code&gt;. While these are unique and of a length that works on all databases, they are not very descriptive.&lt;/p&gt;
&lt;p&gt;We are using H2 and Postgres for our database (test, production) both of which support longer constraint names. Since we use Liquibase to deploy our database changes, this gave me a great way to rename all these constraints.&lt;/p&gt;
&lt;p&gt;First I went into our Postgres instance and got a list of all the foreign key constraints.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  tc.constraint_name, tc.table_name, kcu.column_name,
  ccu.table_name AS foreign_table_name,
  ccu.column_name AS foreign_column_name
FROM
  information_schema.table_constraints AS tc
  JOIN information_schema.key_column_usage AS kcu
    ON tc.constraint_name = kcu.constraint_name
  JOIN information_schema.constraint_column_usage AS ccu
    ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = &apos;FOREIGN KEY&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I exported this query to CSV. This gave me a file in this format:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;key_name,table,column,join_table,join_table_column
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have over 50 constraints on our tables. There was no way I was planning to type out all of these changes in YAML. I ended up creating two python scripts to output the needed YAML.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Drop constraints&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import csv

template = &quot;&quot;&quot;      - dropForeignKeyConstraint:
          baseTableName: %s
          constraintName: %s&quot;&quot;&quot;

datafile = open(&apos;./constraints_file.csv&apos;, &apos;r&apos;)
datareader = csv.reader(datafile)
data = []
for row in datareader:
    data.append(row)

for row in data:
    print template % (row[1], row[0])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Add back Constraints&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import csv
template = &quot;&quot;&quot;      - addForeignKeyConstraint:
          constraintName: %s
          baseTableName: %s
          baseColumnNames: %s
          referencedTableName: %s
          referencedColumnNames: %s&quot;&quot;&quot;

datafile = open(&apos;./constraints_file.csv&apos;, &apos;r&apos;)
datareader = csv.reader(datafile)
data = []
for row in datareader:
    data.append(row)

for row in data:
    name = &quot;fk_%s_%s_%s_%s&quot; % (row[1], row[2], row[3], row[4])
    print template % (name, row[1], row[2], row[3], row[4])
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>liquibase</category><category>database</category></item><item><title>Selenium Screenshots in Continuous Integration</title><link>https://rants.broonix.ca/blog/selenium-screenshots-in-continuous-integration/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/selenium-screenshots-in-continuous-integration/</guid><pubDate>Thu, 08 Sep 2016 20:56:28 GMT</pubDate><content:encoded>&lt;p&gt;There is nothing worse that when your tests fail only on your continuous integration server. This non-deterministic behaviour can be a pain to debug. This is compounded when testing with Selenium. If you can&apos;t see the state of the browser when a test fails how can you fix the issue?&lt;/p&gt;
&lt;p&gt;This week we had an issue with a test that kept failing only on CI. In order to fix the issue, I ended up writing a jUnit test watcher that will take a screenshot of the failure and email it out to our team.&lt;/p&gt;
&lt;p&gt;I&apos;ve talked about our automated acceptance testing before. http://broonix-rants.ghost.io/automated-acceptance-tests/. Lubos, from our development team, wrote a nice how-to that replicates a minimalistic version of our setup.
https://lkrnac.net/blog/2016/01/run-selenium-tests-on-travisci/. The code below builds on this setup.&lt;/p&gt;
&lt;p&gt;Our setup is pretty standard. Our app (Java/React) is built with Gradle on Travis-ci. While this how to will focus on our setup. It should be adaptable to any other build system.&lt;/p&gt;
&lt;p&gt;I&apos;m going to use Lubos&apos; code from the link above as a starting point. Here is a basic test class that uses Selenium.&lt;/p&gt;
&lt;p&gt;We&apos;ll implement screenshot capture using a jUnit test watcher. Our watcher will look to see if we are running on Travis-ci and if the failure is Selenium related. If it is a screenshot will be taken (if possible) then emailed out to the team.&lt;/p&gt;
&lt;p&gt;So far this has proven a rather useful addition to our test setup.&lt;/p&gt;</content:encoded><category>selenium</category><category>testing</category><category>coding</category><category>travis-ci</category><category>gradle</category><category>automation</category><category>java</category></item><item><title>Great Developers are Passionate but in Different ways</title><link>https://rants.broonix.ca/blog/great-developers-are-passionate-but-in-different-ways/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/great-developers-are-passionate-but-in-different-ways/</guid><pubDate>Thu, 01 Sep 2016 20:31:46 GMT</pubDate><content:encoded>&lt;p&gt;Phil DeJarnett from &lt;a href=&quot;http://Qualified.io&quot;&gt;Qualified.io&lt;/a&gt; wrote a great article this week called: &lt;a href=&quot;http://blog.qualified.io/great-developers-dont-need-to-be-passionate/&quot;&gt;Great Developers Don&apos;t Need To Be &quot;Passionate&quot;&lt;/a&gt;. It really resonated with me. While Phil&apos;s point is 100%, I think a lot of people confuse passion with obsession.&lt;/p&gt;
&lt;p&gt;Like a lot of people, early in my career I lived ate, slept and breathed code. I wanted to prove I could master my craft. Code sessions would run into the wee hours of the night, just to fix a bug or ship that feature on time. Email from a client at 11pm on a Friday? Sure let me handle that! Sunday morning and nothing to do? I&apos;d wander off and work on a new feature or a personal project.&lt;/p&gt;
&lt;p&gt;Great developers have a drive to create, iterate and improve everything they do. It&apos;s that drive that makes them so good at what they do. Always being willing to roll up their sleeves and make things work.&lt;/p&gt;
&lt;p&gt;But this passion can easily become an obsession. Too much of anything is not good for you. I think this is a large contributor to burnout in our industry. Coding sessions start bleeding together in an endless stream. Your day starts looking like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while(true) {
    wake();
    eat(&quot;breakfast&quot;);
    travel(work);
    code(workProject);
    eat(&quot;lunch&quot;);
    code(workProject);
    travel(home);
    eat(&quot;supper&quot;);
    code(personalProject);
    sleep(21600);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your personal projects don&apos;t get touched, you&apos;re too sick at looking at code to code more. Your 9-5 might even suffer. One can easily code so much that the fun goes away. Without the fun of coding, the drive to create is just not the same.&lt;/p&gt;
&lt;p&gt;I learnt the hard way that ==passion can easily become an obsession.== Obsessions are never fun.&lt;/p&gt;
&lt;p&gt;I&apos;m very passionate about writing software, but I channel some of that passion into other hobbies. I feel that this ends up making me a better developer in the end and a more rounded individual.&lt;/p&gt;</content:encoded><category>coding</category><category>work</category><category>me</category></item><item><title>Getting started with create-react-app</title><link>https://rants.broonix.ca/blog/getting-started-with-create-react-app-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/getting-started-with-create-react-app-2/</guid><pubDate>Sat, 13 Aug 2016 13:36:24 GMT</pubDate><content:encoded>&lt;p&gt;Setting up a react app has always been a little complex. I&apos;ve joked about it on this blog and twitter. The react team knows this is a pain point for the community and have been awesome enough to provide us with &lt;a href=&quot;https://github.com/facebookincubator/create-react-app&quot;&gt;create-react-app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Create-react-app differs slightly from most boilerplate react projects. It uses just a single dependency to keep initial configuration as simple as possible.&lt;/p&gt;
&lt;p&gt;Let&apos;s go over the steps to get started on a react app using this great new tool.&lt;/p&gt;
&lt;p&gt;First install create-react-app as a global library.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -g create-react-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now to create an app use &lt;code&gt;create-react-app &amp;lt;app-name&amp;gt;&lt;/code&gt;. I&apos;ll call the app for this blog post &apos;react-app&apos;. So my command looks like.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create-react-app react-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you&apos;ll have to wait a few minutes as npm downloads all the &lt;a href=&quot;https://broonix-rants.ghost.io/dependency-hell/&quot;&gt;dependencies&lt;/a&gt;. Once npm has done it&apos;s magic, all you need to do to start the app is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd react-app
npm start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Provided you followed all these steps you should now see your app.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The app created is rather simple. It hides the setup details really well. Here is &lt;code&gt;App.js&lt;/code&gt;, &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You might be worried that create-react-app locks you into its configuration. Since all the details are hidden how can you edit things like webpack configuration?&lt;/p&gt;
&lt;p&gt;Luckily you can &apos;eject&apos; your app at anytime. Eject is a one way trip, once you have done so you can&apos;t go back. Let&apos;s eject this demo app and see what happens. To eject just do this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run eject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have complete control over all the configuration. You can see how much framework create-react-app provides.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;There you have it a quick primer on create-react-app. It is a great way to get started on your first react app.&lt;/p&gt;</content:encoded><category>javascript</category><category>react</category></item><item><title>Dependency Hell</title><link>https://rants.broonix.ca/blog/dependency-hell/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/dependency-hell/</guid><pubDate>Thu, 04 Aug 2016 20:49:04 GMT</pubDate><content:encoded>&lt;p&gt;It seems like every few years we recreate a new version of &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependency_hell&quot;&gt;dependency hell&lt;/a&gt;. Old school linux users and coders know this pain well. Why do we keep doing this to ourselves?&lt;/p&gt;
&lt;p&gt;First some history; like a lot of developers in their 30&apos;s the first time I saw dependency hell was on a linux box. Dependency hell on linux normally manifests due to conflicting library versions. I&apos;m sure there are a ton of Gentoo, RedHat or Debian users that can relate. Spending hours patching or finding the perfect combination of dependencies to appease the RPM or GCC gods.  Luckily the community did everything it could to fix this: side-by-side installations of versions and smart package managers all but negated the problem.&lt;/p&gt;
&lt;p&gt;It was quite a while before I found another instance of dependency hell. Most of my first programming jobs were on Java systems. For those of you who are not Java programmers we have our own version of dependency hell: Jar Hell. Jar Hell is caused when projects a metric shit ton of dependencies. Package management systems like Gradle and Maven have improved the situation, but every now and again you end up with two version of the same library on the classpath.&lt;/p&gt;
&lt;p&gt;Now I&apos;m living a new dependency hell: NPM/JavaScript. The community has grown so fast in such a short period of time. While the ecosystem is robust, app setup and dependency management is getting a bit crazy. Here is how it feels to setup a new app:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Reacts new &lt;a href=&quot;https://github.com/facebookincubator/create-react-app&quot;&gt;create-react-app&lt;/a&gt; installs 700+ dependancies. 700!! It&apos;s not just react that&apos;s crazy like this, don&apos;t forget the &lt;a href=&quot;http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/&quot;&gt;left-pad&lt;/a&gt; fiasco. Dependency hell seems to be compounded by Javascripts fundamental lack of a decent set of core libraries. So far language updates to Javascript feel more like putting lipstick on a pig than improving the core of the language.&lt;/p&gt;
&lt;p&gt;So the question is how do we fix it? Right now I honestly don&apos;t know. It&apos;s a pain point for tens of thousands of developers. As Javascript matures it will get better. More common libraries will be used, reducing the number of dependencies. Couple that to some much needed improvements in npm and a large pain might become more bearable.&lt;/p&gt;</content:encoded><category>linux</category><category>node</category><category>java</category></item><item><title>Simple HTTP Server for any folder via python</title><link>https://rants.broonix.ca/blog/simple-http-server-for-any-folder-via-python-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/simple-http-server-for-any-folder-via-python-2/</guid><pubDate>Wed, 27 Jul 2016 20:01:07 GMT</pubDate><content:encoded>&lt;p&gt;Ever need to have create a web server for an arbitrary folder on your Mac? Python provides a super nice and quick one liner to do this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python -m SimpleHTTPServer 8080&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Boom! A running server in whatever folder you typed that command in.&lt;/p&gt;</content:encoded><category>python</category><category>tips</category></item><item><title>Continuous Deployment with Spring-boot and Travis</title><link>https://rants.broonix.ca/blog/continuous-deployment-with-spring-boot-and-travis/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/continuous-deployment-with-spring-boot-and-travis/</guid><pubDate>Sat, 16 Jul 2016 12:10:24 GMT</pubDate><content:encoded>&lt;p&gt;One of the more interesting things I&apos;ve setup lately has been continuous deployment for Spring-boot using Travis. We are deploying to Elastic Beanstalk with our database running on Amazon RDS. Our current process creates a database snapshot and updates the app when the master branch is updated.&lt;/p&gt;
&lt;h3&gt;Deploying Spring boot&lt;/h3&gt;
&lt;p&gt;This is pretty simple thanks to &lt;a href=&quot;https://github.com/travis-ci/dpl&quot;&gt;dpl&lt;/a&gt; the deployment library travis uses. Elastic Beanstalk is supported so deployment is as simple as adding some confirmation to your &lt;code&gt;.travis.yml&lt;/code&gt; file. Here is what our war deployment looks like.&lt;/p&gt;
&lt;p&gt;While our current process is deploying master, you can also control what branch gets deployed by adding. This would only deploy a branch called staging to elastic beanstalk.&lt;/p&gt;
&lt;h3&gt;Backing up the database&lt;/h3&gt;
&lt;p&gt;Amazon&apos;s RDS does have a robust backup system, but we want to ensure we have a snapshot backup before deploying. We do this using the &lt;code&gt;before_deploy&lt;/code&gt; hook. We fire a script that creates a snapshot.&lt;/p&gt;
&lt;p&gt;We do have to install the &lt;a href=&quot;https://aws.amazon.com/cli/&quot;&gt;awscli&lt;/a&gt; tools. We use pip to install these then run our script.&lt;/p&gt;
&lt;p&gt;There you have it, continuous deployment with database backups to elastic beanstalk. I do still plan some small additions to our process. I&apos;m hoping to have travis post to Slack that the app was updated and include a change log. Automation FTW.&lt;/p&gt;</content:encoded><category>aws</category><category>spring-boot</category><category>travis-ci</category><category>rds</category><category>automation</category><category>coding</category></item><item><title>Deploying Spring-Boot to Amazon Elastic Beanstalk</title><link>https://rants.broonix.ca/blog/deploying-spring-boot-to-amazon-elastic-beanstalk/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/deploying-spring-boot-to-amazon-elastic-beanstalk/</guid><pubDate>Thu, 07 Jul 2016 20:30:38 GMT</pubDate><content:encoded>&lt;p&gt;While there are a ton of guides on how to use spring boot, I was not able to find anything about how to deploy different configurations on Amazon&apos;s Elastic Beanstalk. Here is a how to on some of the options you have when deploying to Elastic Beanstalk. It does not matter if you are deploying via a jar or war the configuration methods are essentially the same.&lt;/p&gt;
&lt;p&gt;The first thing we had to ensure is that we could override the configuration. This way we can provide settings specific to any one deployment. Spring-boot offers many ways to do this. There are several options for &lt;a href=&quot;http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html&quot;&gt;externalized configuration&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Using Spring Application JSON&lt;/h4&gt;
&lt;p&gt;The first option I looked into was using &lt;code&gt;SPRING_APPLICATION_JSON&lt;/code&gt; to configure the properties for the installation. While this does work, there are several limitations when deploying to Elastic Beanstalk.&lt;/p&gt;
&lt;p&gt;The first lesson I learned is that &lt;strong&gt;you can not set this value in the user interface&lt;/strong&gt;. You can see my report here on stack overflow: http://stackoverflow.com/questions/38208301/spring-boot-amazon-elastic-beanstalk-ignores-spring-application-json/38224046#38224046. You can get this configuration to work using an &lt;code&gt;.ebextensions&lt;/code&gt; folder and configuration in your jar/war.&lt;/p&gt;
&lt;p&gt;Here is an example configuration file.&lt;/p&gt;
&lt;p&gt;Don&apos;t forget to have gradle insert this into your war/jar file!&lt;/p&gt;
&lt;p&gt;We ended up not going with this idea. The configuration JSON would up getting rather complex as we expand the config options with in our application.&lt;/p&gt;
&lt;h4&gt;Using Spring Profiles&lt;/h4&gt;
&lt;p&gt;Enter &lt;a href=&quot;http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html&quot;&gt;spring boot profiles&lt;/a&gt;. This is exactly the feature you should be using for this type of deployment.&lt;/p&gt;
&lt;p&gt;Profiles allow you to provide a set of default settings, then override any settings you need to for a given environment. This is essentially the same as using the JSON config, but lets you have these settings in a config file not a JSON string.&lt;/p&gt;
&lt;p&gt;The profile setting can be provided in Elastic Beanstalk&apos;s UI without any issues. Here is our production profile being loaded into the JVM:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can also load this via a system property called &lt;code&gt;SPRING_PROFILES_ACTIVE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To override any of your default settings just provide the profile specific value in that profile&apos;s configuration file. When using the default settings this is &lt;code&gt;application-${profileName}.properties&lt;/code&gt;.&lt;/p&gt;
&lt;h5&gt;Things to watch for!&lt;/h5&gt;
&lt;p&gt;Be sure your default configuration is not setting &lt;code&gt;spring.profiles.active&lt;/code&gt; this will override your provided profiles. If you want to ensure a given profile is also being loaded use &lt;code&gt;spring.profiles.include&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>java</category><category>spring-boot</category><category>aws</category></item><item><title>Testing with React context</title><link>https://rants.broonix.ca/blog/testing-with-react-context/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/testing-with-react-context/</guid><pubDate>Wed, 29 Jun 2016 19:09:49 GMT</pubDate><content:encoded>&lt;p&gt;Last we I outlined how to use &lt;a href=&quot;https://broonix-rants.ghost.io/react-context/&quot;&gt;react&apos;s context feature&lt;/a&gt; in an application. This week I&apos;ll show you how to ensure you can test components that use react context.&lt;/p&gt;
&lt;p&gt;Our test setup uses jsDom and Chai to render and test our components. You need to wrap any component so that it can access a mock context.&lt;/p&gt;
&lt;p&gt;Our wrapper code ended up looking like:&lt;/p&gt;
&lt;p&gt;This takes any component and lets you wrap it with a context. In our tests we can provide a context and then wrap the control.&lt;/p&gt;
&lt;p&gt;This renders a DOM based on the context. We can then assert that the proper items are rendered.&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category><category>testing</category></item><item><title>React: Context</title><link>https://rants.broonix.ca/blog/react-context/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/react-context/</guid><pubDate>Wed, 22 Jun 2016 20:17:36 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://facebook.github.io/react/docs/context.html&quot;&gt;React context&lt;/a&gt; is one of those features I discovered later that I should have. It&apos;s perfect place to hold your apps user context object. In this post I&apos;ll outline how to place a user object into the context and access it from any component in your app.&lt;/p&gt;
&lt;p&gt;The app context is like a global variable. While global variables are often misused, they do have some useful applications. Like most apps we need to hold on to the user who is currently logged in. We do this by placing this user into the app context.&lt;/p&gt;
&lt;p&gt;Now this user object can be used by any component in the app. If we wanted to render a user edit form, we can pull the user from the context.&lt;/p&gt;
&lt;p&gt;You can also access the app context from stateless functional components. Here is the same control written as a function.&lt;/p&gt;
&lt;p&gt;The main draw back of using context is that data flow becomes harder to follow. The alternative would be to use props to pass this data, this would be a more explicit method. Don&apos;t overuse context, there are only a few use cases where its application is correct!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category></item><item><title>React: Containers and Components</title><link>https://rants.broonix.ca/blog/react-containers-and-components-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/react-containers-and-components-2/</guid><pubDate>Tue, 14 Jun 2016 20:13:23 GMT</pubDate><content:encoded>&lt;p&gt;At work we&apos;ve been using React heavily on one of our new projects. Like most projects we are using redux to load data from the server. In doing so testing becomes a bit more difficult. In order to make our components more testable we are using containers.&lt;/p&gt;
&lt;p&gt;You might have heard of containers before, they are also called &apos;Higher Order Components&apos;. Theses are components that wrap other components. They isolate the redux logic outside of the component doing the rendering. This makes testing the components that render your views much simpler.&lt;/p&gt;
&lt;p&gt;Lets look at a practical example. Let&apos;s say you have a view that component a video&apos;s details. We&apos;ll break this into two parts. The container that loads the video from the server and the component that renders the data.&lt;/p&gt;
&lt;p&gt;First off we use react router to nest our views. Our router configuration would look like this.&lt;/p&gt;
&lt;p&gt;This nests the component within the container. This ensures the container is called, it also also for simple reuse if you have other view that load the same object. Here is an example if you also had an edit view that need to load a video.&lt;/p&gt;
&lt;p&gt;Now lets look at a container. A container loads the object via an action. It connects that object to its children via props.&lt;/p&gt;
&lt;p&gt;As you can see on &lt;code&gt;componentWillMount&lt;/code&gt; an action is dispatched to load the video. I&apos;m ignoring what happens in action creators, middleware and stores for this demo. If you do want to learn more about that, I suggest reading the &lt;a href=&quot;http://redux.js.org/docs/basics/UsageWithReact.html&quot;&gt;redux docs&lt;/a&gt;. The container passes the video to the props of all its children.&lt;/p&gt;
&lt;p&gt;This leaves you to only do the rendering in the component.&lt;/p&gt;
&lt;p&gt;The component does not need to know about loading objects or redux. It&apos;s purely for rendering. This makes testing simpler as well as giving you proper separation of concerns.&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category><category>redux</category></item><item><title>Oculus CV1 Review</title><link>https://rants.broonix.ca/blog/oculus-cv1-review/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/oculus-cv1-review/</guid><pubDate>Sat, 04 Jun 2016 12:30:06 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Well after months of waiting it is finally here! My Oculus CV1 arrived last week while I was in NYC. I pre-ordered on January 6th and my CV1 arrived May 24th. Well, it was worth the wait.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h5&gt;The packaging&lt;/h5&gt;
&lt;p&gt;Compared to the DK2 the packaging is beautiful. The box was sturdy and very functional. The presentation of the device is very Apple like. The CV1 comes with: A headset, position sensor (for head tracking), a Xbox one controller and a small remote.&lt;/p&gt;
&lt;h5&gt;Setup&lt;/h5&gt;
&lt;p&gt;Set up was fairly smooth. The CV1 contains a very nice set of built-in earphones, which freed up a USB port since it replaced my existing razor set. I honestly could have done without the Xbox one controller. I already had the same setup using a Xbox 360 controller, the Xbox one controller adds nothing over its predecessor.&lt;/p&gt;
&lt;p&gt;After getting everything plugged in I did notice one odd issue. My Logitech wireless keyboard no longer works and my wireless mouse is now sporadic. I have not been able to get they keyboard working so I had to revert back to an old wired one. The mouse is rather frustrating to use. From what I&apos;ve read on the oculus forms, there is some driver conflict between the Oculus and Logitech.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h5&gt;First impressions&lt;/h5&gt;
&lt;p&gt;The CV1 feels so much lighter on you head than the DK2. The new strap design feels much better than the ski goggle strap the DK2 used. The earphones built into the headset are surprisingly powerful. The audio is crisp and clear and the design breaths better than my previous headset. I haven&apos;t done any 2-3 hours sessions yet in the CV1, but the feel of the new device makes me think I easily could.&lt;/p&gt;
&lt;h5&gt;Bundled software&lt;/h5&gt;
&lt;p&gt;The CV1 is bundled with Oculus Home, Lucky&apos;s tale, EVE:Valerie and Farlands. I have not tried out Farlands yet, but I can cover everything else.&lt;/p&gt;
&lt;p&gt;Oculus home is a launcher for your VR games as well as a store. Think of it as &apos;Oculus&apos; version of Steam. The library is pretty small but helpfully it will begin growing soon.&lt;/p&gt;
&lt;p&gt;Home does include &apos;Oculus Dreamdeck&apos; which has some great demos that show off VR. If you are introducing someone to VR I suggest starting here. Nothing makes you believe in VR like getting chased by a T-Rex or landing on an alien world.&lt;/p&gt;
&lt;p&gt;I almost didn&apos;t install Lucky&apos;s Tale. It&apos;s a 3rd person platformer (think Mario clone). Not a game I&apos;d ever go out and purchase, but since it came for free I installed it.&lt;/p&gt;
&lt;p&gt;I&apos;m really glad I installed it. The experience is much more compelling that I could have imagined. Hovering over you character and navigating from above feels more natural that I had expected. This game has me craving more like this. &lt;em&gt;Please Nintendo: Mario 64 VR remake! You have to!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;EVE:Valerie is still amazing, they&apos;ve polished it quite a bit since the beta. This game in CV1 fulfills my childhood fantasy of being a spaceship pilot. With the CV1&apos;s better resolution and 90hz refresh rate you are pulled into the experience. In the middle of a dogfight you might forget you are just in VR. Since I had previously played this with the DK2 it make a nice experience to show off the advantages of the new headset.&lt;/p&gt;
&lt;h5&gt;Other software&lt;/h5&gt;
&lt;p&gt;I also tested out Assetto Corsa. If you&apos;ve read my blog you know I&apos;m a huge fan of sim-racing. Assetto Corsa is not my favourite racing game, but when testing its the only one that currently supports the CV1.&lt;/p&gt;
&lt;p&gt;The CV1 again shines over the DK2. In the DK2 the resolution did make it often hard to find decent break markers. The increased resolution makes fine details a bit easier to pick out. The resolution could still be improved but I no longer feel at a disadvantage to the non-VR racers on the track.&lt;/p&gt;
&lt;h5&gt;God rays&lt;/h5&gt;
&lt;p&gt;If you&apos;ve been following the rather messy launch Oculus is having you are wondering about this. Some users are reporting that when UI&apos;s have white on black there is some haze or light tearing in the lenses. My headset does also seem to have this issue. I&apos;m not bothered by it. It only happens in very specific circumstances. It&apos;s a bit disappointing that this issue exists, but all in all I can work with the device as is.&lt;/p&gt;
&lt;h5&gt;Conclusion&lt;/h5&gt;
&lt;p&gt;I recommend the CV1. If you are into VR in any capacity this is a great headset. Be warned: If you start gaming in VR you might not be able to go back!&lt;/p&gt;</content:encoded><category>oculus</category><category>cv1</category></item><item><title>Self Driving Cars vs Air Travel</title><link>https://rants.broonix.ca/blog/self-driving-cars-vs-air-travel/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/self-driving-cars-vs-air-travel/</guid><pubDate>Sun, 29 May 2016 13:12:54 GMT</pubDate><content:encoded>&lt;p&gt;Could self driving cars replace local air travel? It&apos;s a question I was asking myself this week. Having traveled from Ottawa to NYC for our company retreat.&lt;/p&gt;
&lt;p&gt;Air travel is not the glamours as it was once was. Those images from the 60s and 70s of lovely meals and ample legroom are no more.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;We are now packed into a plane as close as possible. Every year they sneak away another 4-5cm of legroom. Hell, I&apos;ve heard some airlines have worked out how to &lt;a href=&quot;http://www.fastcoexist.com/3052290/airbus-finds-new-way-to-squeeze-more-passengers-into-a-plane-stacked-seats&quot;&gt;stack seating&lt;/a&gt;! Sounds like a comfy idea doesn&apos;t it?&lt;/p&gt;
&lt;p&gt;Don&apos;t forget what you have to go through to even get on a plane. Have to arrive &lt;a href=&quot;http://www.nytimes.com/2016/05/28/us/politics/tsas-long-lines-were-avoidable-travelers-and-experts-say.html&quot;&gt;1-2 hours&lt;/a&gt; before your flight to get through security. Security has been getting slower and slower and more and more invasive; Metal detectors, full body scanners and pat downs! There&apos;s nothing more enjoyable that walking through security with no shoes or belt.&lt;/p&gt;
&lt;p&gt;I had to drive 30 minutes to the airport. My flight was about 1h of flying time. I arrived about 2 hours before hand to ensure I got through security. I also spent another 30 minutes at customs waiting in line. So for my 1 hour flight, I spent 3-4 hours total traveling. Since there are a limited number of flights I had to base my schedule of the flights available.&lt;/p&gt;
&lt;p&gt;Now what if I had taken a self driving car? First let us assume that a self-driving car travels at the same speed as current cars. We&apos;ll also assume it takes the same amount of time to pass through customs at a land border. The drive from Ottawa to NYC is about 6.5 hours. So it would be about a 7h drive with a border crossing.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now lets look at the advantages of taking a self driving car. First and foremost you can leave when you want. No more getting up at 4am for a flight or taking a 1am red eye. You can time your travel around your schedule. You&apos;d get to skip the circus that is airport security.&lt;/p&gt;
&lt;p&gt;I think i&apos;d pick the self-driving car. More room, more flexible schedule and no unwanted pat downs. Sure the total travel time is a bit longer, but you can make good use of that time. Get it a few hours of work, or watch a movie. With the extra space you can actually be productive.&lt;/p&gt;</content:encoded><category>self-driving</category><category>airplane</category><category>car</category></item><item><title>Company Retreat</title><link>https://rants.broonix.ca/blog/company-retreat/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/company-retreat/</guid><pubDate>Sat, 21 May 2016 11:53:47 GMT</pubDate><content:encoded>&lt;p&gt;No big post this week. I&apos;m off in Sag Harbor NY for our yearly company retreat.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;When you are a remote company you really have to make the most of the times you all are together. Our team has flown in from Argentina, Canada, Italy, Slovakia and Great Britain.&lt;/p&gt;
&lt;p&gt;We spend a week here discussing the previous year, making plans for next year.&lt;/p&gt;</content:encoded><category>work</category></item><item><title>Creating a videojs plugin</title><link>https://rants.broonix.ca/blog/creating-a-videojs-plugin/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/creating-a-videojs-plugin/</guid><pubDate>Sat, 14 May 2016 13:53:10 GMT</pubDate><content:encoded>&lt;p&gt;At Dotsub we work with a lot of video. For the last couple years we have been using &lt;a href=&quot;http://videojs.com&quot;&gt;videojs&lt;/a&gt; as our sites main video player. This week I&apos;ve been updating some of our plugins to the latest videojs version (5.9.2) and also open sourcing them!&lt;/p&gt;
&lt;p&gt;This week lets walk through how to create a videojs plugin. We&apos;ll make a simple watermark plugin that displays the company logo on a video.&lt;/p&gt;
&lt;h3&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;The folks at videojs do provide a yeoman generator as an initial starting point. You can check out that project here: https://github.com/videojs/generator-videojs-plugin&lt;/p&gt;
&lt;p&gt;To create the initial framework just install and run the generator.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npm install -g yo generator-videojs-plugin
$ yo videojs-plugin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I enabled the following for the watermark plugin.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;generator-videojs-plugin&quot;: {
    &quot;bcov&quot;: false,
    &quot;scope&quot;: &quot;&quot;,
    &quot;name&quot;: &quot;watermark&quot;,
    &quot;description&quot;: &quot;Adds a watermark image the video player&quot;,
    &quot;author&quot;: &quot;Brooks Lyrette &amp;lt;hidden&amp;gt;&quot;,
    &quot;license&quot;: &quot;apache2&quot;,
    &quot;changelog&quot;: true,
    &quot;sass&quot;: true,
    &quot;docs&quot;: false,
    &quot;lang&quot;: false,
    &quot;bower&quot;: false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have a ready to go project that uses &lt;code&gt;grunt&lt;/code&gt; to build, &lt;code&gt;karma&lt;/code&gt; and &lt;code&gt;qunit&lt;/code&gt; to test. The plugin code is under &lt;code&gt;src/plugin.js&lt;/code&gt; and &lt;code&gt;src/plugin.scss&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can make sure everything is working fine by typing &lt;code&gt;npm test&lt;/code&gt; in your console.&lt;/p&gt;
&lt;p&gt;**Note: **&lt;em&gt;If you need to be able to use ES6 imports check this out&lt;/em&gt;: https://github.com/videojs/generator-videojs-plugin/pull/69#discussion_r62665092&lt;/p&gt;
&lt;h3&gt;Making the plugin do something&lt;/h3&gt;
&lt;p&gt;Now comes the fun part, making the plugin do something useful. The requirements are pretty simple for this plugin:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show a configured image in the video player.&lt;/li&gt;
&lt;li&gt;When the video is played for the first time wait the pre configured number of seconds to fade the logo out.&lt;/li&gt;
&lt;li&gt;After the initial fade out, the logo should be shown when the player&apos;s controls are shown.&lt;/li&gt;
&lt;li&gt;The logo can be a clickable link.&lt;/li&gt;
&lt;li&gt;The logo position should be configurable (the four corners).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Defaults, Options and configuration.&lt;/h3&gt;
&lt;p&gt;Videojs provides a nice way deal with configuration. As the plugin developer you can provide a set of &lt;code&gt;defaults&lt;/code&gt;, the user will input their settings using &lt;code&gt;options&lt;/code&gt; and you can merge the two using a function the provide called: &lt;code&gt;videojs.mergeOptions(defaults, options);&lt;/code&gt;. Based on the requirements our plugin will use the following settings with these defaults:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const defaults = {
  position: &apos;top-right&apos;,
  fadeTime: 3000,
  url: undefined,
  image: undefined
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;image&lt;/strong&gt;: The URL to the image to be used as the watermark.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;position&lt;/strong&gt;: The location to place the watermark (top-left, top-right, bottom-left, bottom-right). Defaults to &apos;top-right&apos;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fadeTime&lt;/strong&gt;: The amount of time in milliseconds for the initial watermark fade. Defaults to 3000.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;url&lt;/strong&gt;: A url to be linked to from the watermark. If the user clicks the watermark the video will be paused and the link will open in a new window.&lt;/p&gt;
&lt;h3&gt;Coding the plugin&lt;/h3&gt;
&lt;p&gt;We&apos;ll need some DOM elements added to the video player. If you look at the generated code in &lt;code&gt;src/plugin.js&lt;/code&gt; you&apos;ll see there is already a function listing for &lt;code&gt;onPlayerReady&lt;/code&gt;. This is where we will add the needed DOM elements. &lt;code&gt;onPlayerReady&lt;/code&gt; is an event videojs throws when the player is setup and ready to go.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const watermark = function(options) {
  this.ready(() =&amp;gt; {
    onPlayerReady(this, videojs.mergeOptions(defaults, options));
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s add a call to a new function &lt;code&gt;setupWatermark(player, options);&lt;/code&gt;. In that function lets setup the required DOM.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const onPlayerReady = (player, options) =&amp;gt; {
  player.addClass(&apos;vjs-watermark&apos;);

  // if there is no image set just exit
  if (!options.image) {
    return;
  }
  setupWatermark(player, options);
  player.on(&apos;play&apos;, () =&amp;gt; fadeWatermark(options));
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you see above we exit if there is not image configured. If there is one we continue on and set up the DOM. The setup ends up looking like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const setupWatermark = (player, options) =&amp;gt; {
  // Add a div and img tag
  const videoEl = player.el();
  const div = document.createElement(&apos;div&apos;);
  const img = document.createElement(&apos;img&apos;);

  div.id = &apos;vjs-watermark&apos;;
  div.classList.add(&apos;vjs-watermark-content&apos;);
  div.classList.add(`vjs-watermark-${options.position}`);
  img.src = options.image;

  // if a url is provided make the image link to that URL.
  if (options.url) {
    const a = document.createElement(&apos;a&apos;);

    a.href = options.url;
    // if the user clicks the link pause and open a new window
    a.onclick = (e) =&amp;gt; {
      e.preventDefault();
      player.pause();
      window.open(options.url);
    };
    a.appendChild(img);
    div.appendChild(a);
  } else {
    div.appendChild(img);
  }
  videoEl.appendChild(div);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We only add a &lt;code&gt;a&lt;/code&gt; tag if there was a url provided in the configuration. To handle the logo positions we have a CSS class for each location. Let&apos;s write the SCSS for the plugin. We will us absolute positioning for the image container.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Sass for videojs-watermark
.video-js {
  &amp;amp;.vjs-watermark {
    display: block;
  }
  .vjs-watermark-content {
    opacity: 0.99;
    position: absolute;
    padding: 5px;
  }

  // pre-defined positions
  .vjs-watermark-top-right {
    right: 0;
  }
  .vjs-watermark-top-left {
    left: 0;
  }
  .vjs-watermark-bottom-right {
    right: 0;
    bottom: 30px;
  }
  .vjs-watermark-bottom-left {
    left: 0;
    bottom: 30px;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the bottom logo positions the &lt;code&gt;30px&lt;/code&gt; of padding is to account for the height of the controlbar.&lt;/p&gt;
&lt;p&gt;We&apos;ve covered almost all of the requirements, you can configure the image, its location and an optional URL to link to.&lt;/p&gt;
&lt;h3&gt;Implementing fade out&lt;/h3&gt;
&lt;p&gt;We will want the logo to fade in and out. This will look smoother than having it just disappear. We will do this using CSS transitions. To do this we will animate the opacity from 1 to 0 over 1 second.&lt;/p&gt;
&lt;p&gt;Since one of the requirements is that the logo remains visible on the initial play we can use a timeout to wait the correct amount of time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const onPlayerReady = (player, options) =&amp;gt; {
  ...
  player.on(&apos;play&apos;, () =&amp;gt; fadeWatermark(options));
};

const fadeWatermark = (options) =&amp;gt; {
  setTimeout(
    () =&amp;gt; document.getElementById(&apos;vjs-watermark&apos;).classList.add(&apos;vjs-watermark-fade&apos;),
  options.fadeTime);
};

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the &lt;code&gt;vjs-watermark-fade&lt;/code&gt; fade class is attached to the element it will start fading in and out. For the SCSS I recommend using a mixin that covers all the browser prefixes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// cross browser mixin
@mixin transition($args...) {
  -webkit-transition: $args;
  -moz-transition: $args;
  -ms-transition: $args;
  -o-transition: $args;
  transition: $args;
}

// Sass for videojs-watermark
.video-js {
  &amp;amp;.vjs-watermark {
    display: block;
  }
  .vjs-watermark-content {
    opacity: 0.99;
    position: absolute;
    padding: 5px;
    @include transition(visibility 1.0s, opacity 1.0s);
  }
  ...
  //fade out when the user is not active and the video is playing.
  &amp;amp;.vjs-user-inactive.vjs-playing .vjs-watermark-fade {
   opacity: 0;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you have it. A working simple videojs watermark plugin. You can check out the completed project here: https://github.com/dotsub/videojs-watermark (Apache 2.0 licenced)&lt;/p&gt;
&lt;p&gt;The final version includes Qunit tests and ES6 support.&lt;/p&gt;</content:encoded><category>videojs</category><category>javascript</category><category>coding</category></item><item><title>Testing a NPM package locally</title><link>https://rants.broonix.ca/blog/testing-a-npm-package-locally/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/testing-a-npm-package-locally/</guid><pubDate>Mon, 09 May 2016 14:45:40 GMT</pubDate><content:encoded>&lt;p&gt;I ran into this today. I had a pain of a time figuring out how to install a local NPM package for testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here is a quick how to.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From the local package you want to install, package the module. You can do this via &lt;code&gt;npm pack&lt;/code&gt;. This will create a tgz file of the package.&lt;/p&gt;
&lt;p&gt;Change directories to where you want to install your test package. You can install the package via &lt;code&gt;npm install /path/to/packaged-file.tgz&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>npm</category></item><item><title>Star Wars intro in CSS</title><link>https://rants.broonix.ca/blog/star-wars-into-in-css/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/star-wars-into-in-css/</guid><pubDate>Sun, 08 May 2016 12:54:27 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In celebration of &lt;a href=&quot;https://en.wikipedia.org/wiki/Star_Wars_Day&quot;&gt;Star Wars&lt;/a&gt; day, I thought it&apos;d be fun do walk-through how to recreate the movies intro with CSS animations. May the force be with you on this journey of learning.&lt;/p&gt;
&lt;h3&gt;Parts of the intro&lt;/h3&gt;
&lt;p&gt;We will focus on the three main parts of the into animation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The blue &apos;A long time ago..&apos;.&lt;/li&gt;
&lt;li&gt;A shrinking Star Wars logo.&lt;/li&gt;
&lt;li&gt;The scrolling story text.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;A long time ago...&lt;/h4&gt;
&lt;p&gt;The first part of the intro fades in and out the epic starting line of ever Star Wars movie.&lt;/p&gt;
&lt;p&gt;Here is the starting HTML. It&apos;s just a simple div for the intro text.&lt;/p&gt;
&lt;p&gt;Now we have to style it. I took a guess at the font color and found a similar font for the text.&lt;/p&gt;
&lt;p&gt;Now to animate! There are two animations for this part of the intro. The text fades in on a black background, then fades out ~5 seconds later. We can achieve this by animating an opacity change. Fade in is done by animating opacity from 0 to 1 and fade out is the inverse.&lt;/p&gt;
&lt;p&gt;Now these css classes by themselves do nothing. We do need to attach them to the intro div. We&apos;ll do this via Javascript.&lt;/p&gt;
&lt;h4&gt;Shrinking Logo&lt;/h4&gt;
&lt;p&gt;The next part of the intro displays a huge Star Wars logo and has it slowly shrink to nothing. I grabbed a nice SVG copy of the logo from &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Star_Wars_Yellow_Logo.svg&quot;&gt;Wikimedia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s add the logo to the HTML. We will start it off as hidden.&lt;/p&gt;
&lt;p&gt;Now we will add some small styling to add margins and ensure the logo is with width of the screen. The shrink animation is created using the scale CSS property. By scaling from 1.0 to 0 the logo will start huge and shrink into nothing.&lt;/p&gt;
&lt;p&gt;We also have to add this to the Javascript to trigger it after the blue intro text.&lt;/p&gt;
&lt;h4&gt;Scrolling story text&lt;/h4&gt;
&lt;p&gt;This is the most complicated part of the intro. The original was done without &lt;a href=&quot;https://en.wikipedia.org/wiki/Star_Wars_opening_crawl#Episode_IV_opening_crawl&quot;&gt;computer animations&lt;/a&gt;. Lucky for us we don&apos;t have to resort to the type of tricks they needed in 1977 to animate this.&lt;/p&gt;
&lt;p&gt;I&apos;ll add three divs to the HTML for the story text. The first to allow me to change the CSS perspective, the second to tilt backwards into the view and the third to allow me to scroll the text.&lt;/p&gt;
&lt;p&gt;Here is what the styling looks like. I again guessed the text color and found a similar font.&lt;/p&gt;
&lt;p&gt;The tilt effect is achieved by changing the &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/CSS/perspective&quot;&gt;perspective&lt;/a&gt; on the first div by 185px. Then to have the text scroll into the view, I use a rotate transform on the X axis of the tilt div. This gives a similar perspective to what we see in the movie intro. To get the text to scroll, the inner most div is positioned relatively. We can animate the scroll of it&apos;s top position. By changing the top value from 50 to -2000 over 60 seconds, we get a decent recreation of the intro scroll.&lt;/p&gt;
&lt;p&gt;All that&apos;s left to do is add this to the javascript. I start the scroll just before the logo has fully shrunk as we see in the original movie intro.&lt;/p&gt;
&lt;p&gt;I&apos;ve checked the completed demo into github if you want to check it out:&lt;/p&gt;
&lt;p&gt;https://github.com/brookslyrette/starwars-intro&lt;/p&gt;</content:encoded><category>css</category><category>coding</category><category>javascript</category></item><item><title>What is planned for ES7?</title><link>https://rants.broonix.ca/blog/what-planned-for-es7-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/what-planned-for-es7-2/</guid><pubDate>Sat, 30 Apr 2016 13:15:24 GMT</pubDate><content:encoded>&lt;p&gt;Like all things javascript, things move fast. While a lot of us are just starting to get ES6 into production, &lt;a href=&quot;https://github.com/tc39/ecma262&quot;&gt;ES7&apos;s draft&lt;/a&gt; is almost complete. ES6 was a small update, but ES7 looks to be a huge step forward. Here are some highlights on the goodness ES7 will bring.&lt;/p&gt;
&lt;h3&gt;Object Rest/Spread Properties&lt;/h3&gt;
&lt;p&gt;After introducing the spread operator for arrays in ES6, this operator will also support Object in ES7.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Rest Example
let { a, b, ...c } = { a: &apos;foo&apos;, b: &apos;bar&apos;, z: 1, y: 2 };
console.log(a); // &apos;foo&apos;
console.log(b); // &apos;bar&apos;
console.log(c); // { z: 1, y: 2 }

// Spread Example
let example = { a, b, ...c };
console.log(example); // { a: &apos;foo&apos;, b: &apos;bar&apos;, z: 1, y: 2 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Object.values/Object.entries&lt;/h3&gt;
&lt;p&gt;We already have &lt;code&gt;Object.keys&lt;/code&gt; but we all turn to lodash, jQuery or some other 3rd party library to get access to an object&apos;s values or entry set.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const example = { a: &apos;foo&apos;, b: &apos;bar&apos;, z: 1, y: 2 };

// Values
console.log(example.values); // [&apos;foo&apos;, &apos;bar&apos;, 1, 2];

// Entries 
console.log(example.values); // [[a: &apos;foo&apos;], [a: &apos;bar&apos;], [z: 1], [y: 2]];
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;String.prototype.padStart/String.prototype.padEnd&lt;/h3&gt;
&lt;p&gt;Javascript Strings are a pain to deal with. This is mostly due to some missing string manipulation features like padding. I&apos;ll stay away from the &lt;a href=&quot;http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/&quot;&gt;left-pad fiasco&lt;/a&gt; we had just a few months back. I&apos;ll only say that this is a much needed core language feature.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// End
&apos;As if JS lacks this&apos;.padEnd(25); // &apos;As if JS lacks this      &apos;
&apos;As if JS lacks this&apos;.padEnd(2); // &apos;As if JS lacks this&apos;

// Start
&apos;As if JS lacks this&apos;.padEnd(25); // &apos;      As if JS lacks this&apos;
&apos;As if JS lacks this&apos;.padEnd(2); // &apos;As if JS lacks this&apos;

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Async Functions&lt;/h3&gt;
&lt;p&gt;Async function promise to clean up all the &lt;code&gt;promise&lt;/code&gt; code. While promises where a vast improvement over callbacks, Async will remove even more boiler plate.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function asyncExample(elem, animations) {
  let ret = null;
    try {
      for(const anim of animations) {
        ret = await anim(elem);
      }
    } catch(e) { 
      // handle error here if need be
    }
  return ret;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Array.prototype.includes&lt;/h3&gt;
&lt;p&gt;Sick of writing &lt;code&gt;foo.indexOf(x) != -1&lt;/code&gt;? Good, so are the rest of us! ES7 will add a &lt;code&gt;includes&lt;/code&gt; function. You can finally write &lt;code&gt;foo.includes(x)&lt;/code&gt; to properly express what you are trying to do.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This only scratches the surface of what is being proposed for ES7. These will be welcome additions to the language. Love it or hate it, the web runs on Javascript. Only time will tell if ES7&apos;s changes make writing Javascript better. Sometimes I do wonder, are only putting lipstick on a pig?&lt;/p&gt;
&lt;p&gt;Further reading:
&lt;strong&gt;ES7 Spec:&lt;/strong&gt; https://tc39.github.io/ecma262/&lt;br /&gt;
&lt;strong&gt;ES7 Status:&lt;/strong&gt; https://github.com/tc39/ecma262&lt;br /&gt;
&lt;strong&gt;ES7 Examples:&lt;/strong&gt; https://github.com/hemanth/es7-features#exponentiation-operator (a bit out of date but useful)&lt;/p&gt;</content:encoded><category>javascript</category><category>es7</category></item><item><title>Advertisers: It&apos;s not me, it&apos;s you</title><link>https://rants.broonix.ca/blog/advertisers-its-not-me-its-you-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/advertisers-its-not-me-its-you-2/</guid><pubDate>Sat, 23 Apr 2016 12:35:21 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Almost &lt;a href=&quot;http://www.statista.com/statistics/435252/adblock-users-worldwide/&quot;&gt;200,000,000 internet&lt;/a&gt; users are blocking ads. This number has been growing year over year and will likely continue doing so. I am one of these users. I&apos;ve been blocking ads for years. The reason is not that I hate seeing advertisements, It&apos;s that I&apos;m sick of being followed around the internet.&lt;/p&gt;
&lt;p&gt;It&apos;s a wonder some people don&apos;t realize this. Have you ever google something and found you started seeing the same ads over and over again? Well that is because you are being followed by all the major ad networks. Think of them as a creepy dude following you around, looking over your shoulder as much as possible. Trying to get every little tiny bit of data about you.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;#####But why? How did this happen?&lt;/p&gt;
&lt;p&gt;Google was one of the first companies to perfect this tracking into a business model. Google was the greatest search engine of its time, but that does not make a penny for the company. What does create revenue are ads. Google realized that if you know what a person searches for, you can show them paid ads for what they are looking for. This does not stop at search results. Google&apos;s ad network is large and is in use almost everywhere.&lt;/p&gt;
&lt;p&gt;A friend of mine learned this the hard way. When he was shopping for engagement rings he googled for local retailers, designs, etc. At the time he and girlfriend shared a computer. The ads shown on the computer quickly changed: &lt;em&gt;engagement rings, wedding planning ads, diamond vendors&lt;/em&gt;. Luckily she never noticed, but he was noticing and praying the surprise would not be ruined!&lt;/p&gt;
&lt;p&gt;Google has taken this much further. Google scans every bit of data about you it can get it&apos;s hands on. Got a gmail account? They scan that. Watch videos on YouTube? That&apos;s in the data. Use Google+?.... just kidding no one uses Google+. Google packs every little bit they can about you into your profile. Even data about the pages they already showed ads to you on, even if those are not google sites!&lt;/p&gt;
&lt;p&gt;While Google was the first to perfect user tracking into a business model, Facebook is the new king.&lt;/p&gt;
&lt;p&gt;The crazy thing about Facebook is that we willingly give them tons of data. Age, sex, likes, friends, posts. They pair this will tracking similar to google. This builds the perfect profiles to advertise too. Want to target 20 something males who are single and like cars? How about 30 something females with no children who like romantic comedies and jazz? Facebook has the data to target like this, and it is us who gave it to them willingly.&lt;/p&gt;
&lt;p&gt;#####So what can we do?&lt;/p&gt;
&lt;p&gt;Not much sadly. There is no good way to block only targeted ads without blocking everything. If you keep using Facebook, Google and other services you should be aware on what they are doing with your data. You can install &lt;a href=&quot;https://adblockplus.org/&quot;&gt;ad blockers&lt;/a&gt; or &lt;a href=&quot;https://www.ghostery.com/&quot;&gt;tracker blockers&lt;/a&gt;. Maybe if enough of us refuse to be tracked we can force these companies to stop following us.&lt;/p&gt;
&lt;p&gt;You stop following me and I&apos;ll turn off my ad blocker.&lt;/p&gt;</content:encoded><category>ads</category><category>privacy</category></item><item><title>Linting in ES6</title><link>https://rants.broonix.ca/blog/linting-in-es6/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/linting-in-es6/</guid><pubDate>Sat, 16 Apr 2016 12:23:24 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve talked about &lt;a href=&quot;http://broonix-rants.ghost.io/are-you-linting-yet/&quot;&gt;linting before&lt;/a&gt; and it&apos;s benefits. After updating all our &lt;a href=&quot;http://broonix-rants.ghost.io/porting-react-components-to-es6/&quot;&gt;code to ES6&lt;/a&gt; we had to update our linting rules.&lt;/p&gt;
&lt;p&gt;We are using the &lt;a href=&quot;https://github.com/airbnb/javascript&quot;&gt;AirBnB style guide&lt;/a&gt;, which has also been updated with some nice ES6 defaults. There was a ton of updates to make, but it&apos;s worth it to have clean consistent code.&lt;/p&gt;
&lt;p&gt;Here were our most common linting errors and why they should be corrected.&lt;/p&gt;
&lt;h4&gt;Template Literals&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot;&gt;Template literals&lt;/a&gt; are a cleaner way to combine strings. You should never be writing &lt;code&gt;&apos;foo&apos; + x&lt;/code&gt; in ES6. Templates make string concatenation a breeze &lt;code&gt;`foo${x}`&lt;/code&gt;. Our linting rules enforce us to use literals when concatenating strings, but also for us to not lazily use string templates when there is not any concatenation taking place. &lt;code&gt;`Some string`&lt;/code&gt; is an error!&lt;/p&gt;
&lt;h4&gt;Object Literal Shorthand&lt;/h4&gt;
&lt;p&gt;There is a lot of enhancements in ES6 to let you write shorter and more concise code. This includes &lt;a href=&quot;http://eslint.org/docs/rules/object-shorthand&quot;&gt;object literal shorthand&lt;/a&gt;. How often do you see code that looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const name = &apos;Some Name&apos;;
const title = &apos;A title&apos;;

this.setState({ name: name, title: title});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Shorthand makes this a bit more concise:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const name = &apos;Some Name&apos;;
const title = &apos;A title&apos;;

this.setState({ name, title});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Arrow Functions&lt;/h4&gt;
&lt;p&gt;One of the best additions to ES6 is &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot;&gt;arrow functions&lt;/a&gt;. Inheriting the local &lt;code&gt;this&lt;/code&gt; of the code surrounding them make arrow functions very useful. Enforcing that we use arrow functions cleans up a ton of code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The old&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let foo = function() {
  return this.bar;
}.bind(this);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Becomes&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let foo = () =&amp;gt; this.bar;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Other changes&lt;/h4&gt;
&lt;p&gt;We did have a multitude of other linting changes. The rest however are cosmetic. We turned on &lt;a href=&quot;https://medium.com/@nikgraf/why-you-should-enforce-dangling-commas-for-multiline-statements-d034c98e36f8#.3l8v4b1pn&quot;&gt;comma dangle&lt;/a&gt;, added spacing around &lt;code&gt;{ object }&lt;/code&gt; brackets and enforced a line length. These changes are purely visual and just help our code look consistent.&lt;/p&gt;
&lt;p&gt;If you are not using a linter on your Javascript code I suggest you start now. The longer you wait the more refactoring you&apos;ll have to do!&lt;/p&gt;</content:encoded><category>javascript</category><category>es6</category><category>eslint</category></item><item><title>Porting React Components to ES6</title><link>https://rants.broonix.ca/blog/porting-react-components-to-es6/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/porting-react-components-to-es6/</guid><pubDate>Sat, 09 Apr 2016 12:11:19 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been meaning to do this for a while, but we finally ported all our React code to ES6 classes. The migration was pretty simple, but I figured sharing my notes might help someone out there.&lt;/p&gt;
&lt;h3&gt;Class creation&lt;/h3&gt;
&lt;p&gt;We had been using &lt;code&gt;React.createClass&lt;/code&gt;, but with ES6 this changes to &lt;code&gt;extends React.Component&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Old&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Widget = React.createClass({
   // code is here
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Widget = extends React.Component {
   // code is here
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;PropTypes&lt;/h3&gt;
&lt;p&gt;PropTypes are no longer part of the code within the class. They are defined afterwards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Old&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Widget = React.createClass({

  propTypes: {
    fn: React.PropTypes.func,
    thing: React.PropTypes.object
  }

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Widget = extends React.Component {
   // code is here
}

Widget.propTypes: {
  fn: React.PropTypes.func,
  thing: React.PropTypes.object
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Initial State&lt;/h3&gt;
&lt;p&gt;Initial state is now set in the constructor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Old&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Widget = React.createClass({

  getInitialState() {
    return {
      foo: &apos;bar&apos;
    };
  }

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Widget = extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      foo: &apos;bar&apos;
    };
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Function Binding&lt;/h3&gt;
&lt;p&gt;You might not know, but React does some magic internally when you use &lt;code&gt;createClass&lt;/code&gt;. This includes auto-binding &lt;code&gt;this&lt;/code&gt; to your functions. If you have a function that needs access to the component, you now need to bind it in the constructor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Old&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Widget = React.createClass({

  doSomeWork() {
     return this.props.foo;
  }

});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Widget = extends React.Component {

  constructor(props) {
    super(props);
    this.doSomeWork = () =&amp;gt; this._doSomeWork();
  }

  _doSomeWork() {
     return this.props.foo;
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Other issues&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Mixins&lt;/code&gt; are not supported in ES6. Most libraries are already moving away from them. We did have to update &lt;code&gt;react-router&lt;/code&gt; and &lt;code&gt;react-intl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Happy porting and welcome to ES6!&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category><category>es6</category></item><item><title>The Threat</title><link>https://rants.broonix.ca/blog/the-threat/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/the-threat/</guid><pubDate>Sat, 02 Apr 2016 12:26:40 GMT</pubDate><content:encoded>&lt;p&gt;I honestly can&apos;t believe it happened. My company got a &lt;a href=&quot;https://en.wikipedia.org/wiki/Denial-of-service_attack#Distributed_attack&quot;&gt;Distributed Denial of Services attack  (DDoS)&lt;/a&gt; ransom note.&lt;/p&gt;
&lt;h3&gt;The note&lt;/h3&gt;
&lt;p&gt;The note itself was short and to the point. Send us X Bitcoins or we attack you. If you do not comply by Z date the price will go up every day by Y while we attack. The message was sent via an encrypted anonymous email service.&lt;/p&gt;
&lt;h3&gt;The Ransom&lt;/h3&gt;
&lt;p&gt;They are rather smart about the ransom amount. It&apos;s not a crazy impossible sum of money. It&apos;s right at the point where if you didn&apos;t know better you might think about paying it.&lt;/p&gt;
&lt;h3&gt;Is it real?&lt;/h3&gt;
&lt;p&gt;The first question we had to ask ourselves was &lt;em&gt;is this a legitimate threat?&lt;/em&gt; This answer is impossible to know conclusively.&lt;/p&gt;
&lt;p&gt;It could just as easily be a copycat. Using the same email without the intent to ever attack. It could be a script kiddie with a few rudimentary tools at their disposal. It could also be the group the email claims it is.&lt;/p&gt;
&lt;p&gt;After comparing the ransom note to others. It seems probable that it may be legitimate.&lt;/p&gt;
&lt;p&gt;Even if there is a 5% chance this is real we have to act on it. Like most small companies, a day of outage would not be acceptable. It is just not a chance you want to take.&lt;/p&gt;
&lt;h3&gt;What to do?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;First things first:&lt;/strong&gt; What ever you do, do not pay the ransom. Why? Even it did stop the attack, what&apos;s to say they won&apos;t be back asking for more? You&apos;ll just be added to a list of people who will pay up. The threats will never stop. If you give a bully your lunch money, they will come back for it every day.&lt;/p&gt;
&lt;p&gt;You should get yourself protected. There are a ton of companies that provide &lt;a href=&quot;https://www.google.ca/search?q=ddos+protection&amp;amp;oq=DDoS+protection&quot;&gt;DDoS protection&lt;/a&gt;. While this does cost a bit of money, it is worth the peace of mind.&lt;/p&gt;</content:encoded><category>attack</category><category>ddos</category></item><item><title>Happy Easter</title><link>https://rants.broonix.ca/blog/21-posts-in/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/21-posts-in/</guid><pubDate>Sat, 26 Mar 2016 11:58:05 GMT</pubDate><content:encoded>&lt;p&gt;No real post this week. My goal is still to blog about something (technical or nontechnical) once a week for a year. But with the holidays this week I just never found the time.&lt;/p&gt;
&lt;p&gt;I&apos;ll be back next week with something interesting. I&apos;m pretty happy with my posts so far 21/52 weeks completed!&lt;/p&gt;</content:encoded></item><item><title>Machines will take your job</title><link>https://rants.broonix.ca/blog/computers-will-take-your-job/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/computers-will-take-your-job/</guid><pubDate>Sat, 19 Mar 2016 13:05:27 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s not even a bold or futuristic thing to say. Machines will slowly replace all of us. It&apos;s who is next that might surprise you most.&lt;/p&gt;
&lt;p&gt;How soon and how fast is still up for debate. NPR has a great interactive article where you can see an estimate on how long you it might take to &lt;a href=&quot;http://www.npr.org/sections/money/2015/05/21/408234543/will-your-job-be-done-by-a-machine&quot;&gt;automate your job&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So who&apos;s surprisingly next?&lt;/p&gt;
&lt;h5&gt;Long Distance Trucking&lt;/h5&gt;
&lt;p&gt; Sadly these new truckers will not be as amazing as the transformers. All the advances we are seeing in self-driving cars has amazing commercial application in the transportation field. There are approximately 3.5 million truck drivers on the road today.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://www.newsmax.com/TheWire/army-tests-self-driving-trucks/2016/03/14/id/718988/&quot;&gt;US&lt;/a&gt; and &lt;a href=&quot;http://www.engadget.com/2016/03/07/uk-self-driving-lorries-government-budget/&quot;&gt;UK&lt;/a&gt; are already experimenting with this idea. A robot truck would never need to stop to rest. It&apos;s only reason to stop working is to fuel. Advances in the next 5-10 years should eliminate most of the long haul trucking jobs between cities. Leaving the human drives small &apos;city&apos; or last mile drop offs.&lt;/p&gt;
&lt;h5&gt;Wall Street Traders&lt;/h5&gt;
&lt;p&gt;
If you compare the number of humans on the floor of the New York stock exchange now and fifteen years ago, you&apos;ll see a shocking decline in humans on the floor. The people you see on the floor now are just supervising the computers that are now doing the majority of the trading.&lt;/p&gt;
&lt;p&gt;The next casuality on Wall Street will surprise you: &lt;strong&gt;Analysts&lt;/strong&gt;. That&apos;s right, the big guys, college educated and making $300-$500k a year.&lt;/p&gt;
&lt;p&gt;Everyone on Wall Street has heavily invested in artificial intelligence and machine learning. They are starting to get the returns on their investments. An AI can easily crunch a company&apos;s financials, parse thousands of news articles all while monitoring social media and other trade data and instantly act on financial moves. We humans just can&apos;t keep up.&lt;/p&gt;
&lt;p&gt;There will still be a small number of analysts remaining at most firms, but you will see a drastic shrinking in their numbers on Wall Street.&lt;/p&gt;
&lt;h5&gt;What will the future look like?&lt;/h5&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;That&apos;s just a small sample of what jobs might be replaced in the next 5-10 years. Some estimates say 50%-80% of the existing jobs might be replaced. The retail and services industries will be the hardest hit.&lt;/p&gt;
&lt;p&gt;What this all means for society is up in the air. Distopian future? Creative utopia? Only we, not the machines, can decide that part.&lt;/p&gt;</content:encoded><category>work</category><category>machines</category><category>automation</category></item><item><title>I&apos;m still bullish on Twitter</title><link>https://rants.broonix.ca/blog/im-still-bullish-on-twitter/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/im-still-bullish-on-twitter/</guid><pubDate>Sat, 12 Mar 2016 13:37:20 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;It seems like every few weeks someone writes an article about the demise of twitter. User growth has started to taper off but revenue has continued to increase. I&apos;m not saying they are in the best position they could be in, but they are far from failing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Twitter needs to start owning what they do well and ignoring all the crap people think they should be doing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;Twitter is not Facebook&lt;/h5&gt;
&lt;p&gt;Investors keep trying to compare Facebook and Twitter as if they are the same thing.&lt;/p&gt;
&lt;p&gt;I believe this is why you keep seeing rumors of Twitter replacing the timeline with algorithmically ranked content. People seem to think that if you made Twitter look more like Facebook, you&apos;d see the same growth and return on investment.&lt;/p&gt;
&lt;p&gt;Going head to head with Facebook on their turf would be like invading Russia in the winter. You just can&apos;t win that fight.&lt;/p&gt;
&lt;h5&gt;So what does Twitter do well?&lt;/h5&gt;
&lt;p&gt;Twitter is a great app for more than it gets credit for. Here is why enjoy using twitter.&lt;/p&gt;
&lt;h6&gt;The here and now.&lt;/h6&gt;
&lt;p&gt;Twitter is the ultimate network for keeping up with that is happening &lt;em&gt;&lt;strong&gt;now&lt;/strong&gt;&lt;/em&gt;. It&apos;s not just about your social network either, your Twitter feed keeps you in touch with the world.&lt;/p&gt;
&lt;p&gt;I&apos;m amazed how many important world events I found out about from my Twitter feed. From Steve Jobs death to terrorist attacks, I opened Twitter and found out about these things. Then and only then did I flick on the news. &lt;strong&gt;Twitter is the ultimate discovery tool.&lt;/strong&gt;&lt;/p&gt;
&lt;h6&gt;Technical Support&lt;/h6&gt;
&lt;p&gt;It might seems a little funny on this list but it&apos;s something more people should be aware of.&lt;/p&gt;
&lt;p&gt;If you tweet at most of these small startups they almost always reply. They are about their social footprint and Twitter public nature seems to ensure they act faster.&lt;/p&gt;
&lt;h6&gt;Sports and Events&lt;/h6&gt;
&lt;p&gt;If you&apos;ve ever been to a hockey, football or baseball game, you know there is one thing fans love doing: &lt;em&gt;&lt;strong&gt;Yelling&lt;/strong&gt;&lt;/em&gt;. It doesn&apos;t even matter at what sometimes. &lt;em&gt;The ref is blind!&lt;/em&gt;, &lt;em&gt;Your team sucks!&lt;/em&gt;, &lt;em&gt;My team rules!&lt;/em&gt;, &lt;em&gt;Did you see that play!?!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This experience moves almost seamlessly to Twitter. During the 2015 Stanley Cup playoffs I loved the interaction I had with other fans.&lt;/p&gt;
&lt;p&gt;Sports stars have really embraced Twitter. A Nascar driver even got fined for &lt;em&gt;tweeting during a race&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We&apos;ve seen sports stars hash out differences over Twitter as well as congratulate others on their accomplishments. As a fan it&apos;s amazing to see these interactions and to feel like a part of the conversation.&lt;/p&gt;
&lt;h6&gt;So what is next for Twitter?&lt;/h6&gt;
&lt;p&gt;They need to embrace and monetize what they do well. Focus on their core competencies of connecting people to the events that are happening around them in real time. With users inside and outside of their social circle.&lt;/p&gt;</content:encoded><category>facebook</category><category>twitter</category></item><item><title>Sim-Racing: My favorite hobby</title><link>https://rants.broonix.ca/blog/sim-racing-my-favorite-hobby/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/sim-racing-my-favorite-hobby/</guid><pubDate>Sat, 05 Mar 2016 13:37:26 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve talked before about how much &lt;a href=&quot;http://broonix-rants.ghost.io/virtual-reality-the-future-of-gaming/&quot;&gt;I enjoy VR&lt;/a&gt;, but never have I talked about why I almost exclusively play racing games. Here is the history and the how and why I love racing.&lt;/p&gt;
&lt;h5&gt;How I got started&lt;/h5&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The first racing games I remember playing were on the NES. Games like &lt;a href=&quot;https://en.wikipedia.org/wiki/Rad_Racer&quot;&gt;Rad Racer&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/R.C._Pro-Am&quot;&gt;RC-Pro Am&lt;/a&gt; instantly come to mind. It wasn&apos;t until I was a bit older that I realized I had an obsession for racing. It came from the oddest of games: &lt;a href=&quot;https://en.wikipedia.org/wiki/Super_Mario_Kart&quot;&gt;Super Mario Kart&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;How did I get obsessed? It&apos;s pretty simple. I&apos;d set a fast time trial lap on Mario GP or Ghost Valley and my father would beat it. I&apos;d set another fast lap and he&apos;d beat it again. It finally got to the point where we were eking out laps that were tenths or thousands of a second better. I was hooked, no matter the game I wanted to be the fastest.&lt;/p&gt;
&lt;p&gt;I graduated to much more complicated games. The series by Papyrus Design Group were always some of my favorites. From Indycar Racing to Grand Prix Legends to Nascar 2003 and now &lt;a href=&quot;http://iracing.com&quot;&gt;iRacing&lt;/a&gt;. I even found all these classics in a drawer at my parents place.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h5&gt;Why not race a real car?&lt;/h5&gt;
&lt;p&gt;There is an old saying in racing:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What is the Best Way to Make a Small Fortune in Racing? Start with a large one, and work down from there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&apos;d love too, but racing makes a drug addiction look cheap. Maybe one day, but for now real racing is a hobby beyond my means.&lt;/p&gt;
&lt;h5&gt;But why? What is it about sim-racing?&lt;/h5&gt;
&lt;p&gt;It&apos;s hard to put it into words, but I&apos;ll give it a try.&lt;/p&gt;
&lt;p&gt;There is a moment when racing where every thing else in the universe disappears. There is no work, stress or anything else to think about.  It is zen. All you mind can think about is: &lt;em&gt;brake, turn-in, apex, accelerate&lt;/em&gt;.&lt;/p&gt;</content:encoded><category>simracing</category><category>racing</category><category>iracing</category></item><item><title>Facebook is getting Emotional</title><link>https://rants.broonix.ca/blog/facebook-is-getting-a-little-emotional/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/facebook-is-getting-a-little-emotional/</guid><pubDate>Sat, 27 Feb 2016 13:36:17 GMT</pubDate><content:encoded>&lt;p&gt;Well there you have it, after 12 years Facebook changed its secret sauce. Gone are the days of a simple &lt;strong&gt;Like&lt;/strong&gt; replaced with the wide variety of: &lt;strong&gt;Like, Love, Haha, Wow, Sad and Angry&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Call me old fashioned, but I really enjoyed the simplicity of only being able to Like. It was simple, streamlined and looked a bit less cluttered. Now every post has a small rainbow of colors.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This new model really brings in 4 new emotions: Laughter, Shock, Sorrow and Anger. Love is really just hitting the &apos;Like&apos; button harder. You can see why Facebook has added these. No one wants to like a post about a lost family member or a bad day, Anger and Sorrow work better in these situations.&lt;/p&gt;
&lt;p&gt;People have been asking for a &apos;Dislike&apos; button for years. I can easily see why Facebook did not include this interaction. Facebook has always been a place for positive interactions. Can you only imagine the trolling that could be done with a &apos;Dislike&apos; button? If you can&apos;t, I suggest joining Reddit. Getting downvoted to oblivion can invoke a negative emotional reaction. Facebook would never want this type negative connotation with their product.&lt;/p&gt;
&lt;p&gt;I do wonder what Facebook&apos;s long term plan is with this feature. While you might think this feature is for you the user. I&apos;d be really surprised if it was. People often forget Facebook is free because you are the product.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Facebook has already admitted to intentionally experimenting with it&apos;s &lt;a href=&quot;http://www.theguardian.com/technology/2014/dec/09/facebook-emotional-experiment-most-shared-academic-research&quot;&gt;users emotions&lt;/a&gt;. They have even &lt;a href=&quot;http://www.popularmechanics.com/technology/apps/news/a18837/facebook-has-been-intentionally-crashing-its-android-app-on-users/&quot;&gt;repeatedly crashed their app&lt;/a&gt; to see what your patience level is. Now they have added a new data point for users to openly share with them.&lt;/p&gt;
&lt;p&gt;How you emotionally react to something is gold to advertisers. Facebook is adding another data point to be packaged up a sold back to advertisers. Who knows what other ideas they might dream up with this data.&lt;/p&gt;</content:encoded><category>facebook</category></item><item><title>I stand with Apple</title><link>https://rants.broonix.ca/blog/encr/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/encr/</guid><pubDate>Sat, 20 Feb 2016 14:05:09 GMT</pubDate><content:encoded>&lt;p&gt;So it happened, the US government is trying to compel &lt;a href=&quot;http://www.apple.com/customer-letter/&quot;&gt;Apple into creating a backdoor into iOS&lt;/a&gt;. This is a dangerous precedent. Sure they&apos;ll pass it off as an &lt;em&gt;&apos;issue of national security&apos;&lt;/em&gt; but it&apos;s just not that simple.&lt;/p&gt;
&lt;p&gt;iOS is designed to be secure. This means there is no way for even Apple to access the encrypted data on the device. This is a practice we call &apos;&lt;a href=&quot;https://en.wikipedia.org/wiki/Secure_by_design&quot;&gt;secure by design&lt;/a&gt;&apos;. Apple has done their best to ensure there is no way into the data on the device.&lt;/p&gt;
&lt;p&gt;The US government asked for an IPSW file to access the phone. This file would contain a security hole that would give them access to a phone. The mere existence of such a piece of software makes iOS no longer secure by design.&lt;/p&gt;
&lt;p&gt;What would stop this software from falling into the wrong hands? Do you honestly think such a tool would not be used in spycraft? After all that has come to light on warrantless spying using &apos;&lt;a href=&quot;https://en.wikipedia.org/wiki/Stingray_phone_tracker&quot;&gt;Stingray devices&lt;/a&gt;&apos; do you even trust them with such a tool?&lt;/p&gt;</content:encoded><category>encryption</category><category>ios</category></item><item><title>On Uber...</title><link>https://rants.broonix.ca/blog/on-uber/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/on-uber/</guid><pubDate>Sat, 13 Feb 2016 16:43:50 GMT</pubDate><content:encoded>&lt;p&gt;Uber is in the news a lot here in Canada of late. It&apos;s one of those topics I&apos;m torn on.&lt;/p&gt;
&lt;p&gt;On one hand Uber is a good thing. It directly connects drivers and riders. It streamlines payments and is so convenient.&lt;/p&gt;
&lt;p&gt;On the other hand, the drivers who are licenced taxi drivers are taking a huge hit. They were already being screwed by a semi-corrupt licensing system. As always it&apos;s the last person in the economic chain that gets squeezed the most. The fact that it was a tech startup that implemented this idea give you idea on how stagnant the taxi business had become.&lt;/p&gt;
&lt;p&gt;Taxi licenses have become a business. Since there are finite number of licenses in most cities, investors would buy them up. Since new licenses are rarely issued, they could see these as an investment. These people do not own taxis, do not drive taxis, hell they do not even &lt;strong&gt;own taxi companies&lt;/strong&gt;. This is what drove up the prices of these licenses upwards of millions in some place. They just milk the value of their holdings.&lt;/p&gt;
&lt;p&gt;Here is why I feel bad for the real taxi drivers. In most cities here in Canada UberX is illegal. You can not be a driver for hire without the proper business insurance and license. Drivers who are doing everything right in the shitty system they have are being squeezed out by UberX thieves. Of course not having insurance lowers the prices you charge! It&apos;s an unfair advantage. Same goes of all the other costs a licensed cab driver must endure.&lt;/p&gt;
&lt;p&gt;What we really need is a middle ground between what Uber has become and what traditional cabs are. There has to be a small amount of oversight, insurance and regulation. I think removing cab licenses as a investment can really affect some change. It would also allow a company like Uber to purchase cab licences and join a more level playing field.&lt;/p&gt;</content:encoded><category>taxi</category><category>uber</category><category>gigs</category></item><item><title>Virtual Reality is the Future</title><link>https://rants.broonix.ca/blog/virtual-reality-the-future-of-gaming/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/virtual-reality-the-future-of-gaming/</guid><pubDate>Sat, 06 Feb 2016 13:03:07 GMT</pubDate><content:encoded>&lt;p&gt;If you get a chance to try an &lt;a href=&quot;https://www.oculus.com/&quot;&gt;Oculus Rift&lt;/a&gt;, &lt;a href=&quot;/&quot;&gt;HTC Vive&lt;/a&gt; or any VR headset for that matter, &lt;strong&gt;DO IT&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;I was lucky enough to have my friend &lt;a href=&quot;http://www.davidorban.com/&quot;&gt;David Orban&lt;/a&gt; convinced me that I should get a DK2. My DK2 arrived in September 2014. From the first moment I put that headset &lt;strong&gt;I was sold on VR&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve &lt;a href=&quot;http://www.shovsoft.com/lunarflight/&quot;&gt;landed on the moon&lt;/a&gt;. Raced an &lt;a href=&quot;http://www.iracing.com/&quot;&gt;F1 car at Monza&lt;/a&gt;. Had &lt;a href=&quot;http://evevalkyrie.com&quot;&gt;spaceship fights in space&lt;/a&gt;. Had my mind blow with demos like &lt;a href=&quot;http://sightlinevr.com/&quot;&gt;SightLineVR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem with telling others about VR is that no matter how well you try to explain it, you can&apos;t properly convey what it&apos;s like. VR is an experience. Given the right game the immersion can be picture perfect. I&apos;ve raised my hands to protect my face or braced my body for an impact only seconds later to realize that I&apos;m just gaming.&lt;/p&gt;
&lt;p&gt;I&apos;ve always been an avid sim-racer. I&apos;ve used a &lt;em&gt;triple monitor&lt;/em&gt; set up for quite some time. Lots of racers use setups like this to maximize the immersion and give you a better field of view for racing. This is what my setup looked like:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I prefer the Rift so much that I got rid of my triples. All those screens got replaced with this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;That&apos;s my father trying out my DK2 in iRacing.&lt;/p&gt;
&lt;p&gt;VR really nails the &apos;cockpit&apos; style gaming experience. Racecar, airplane, spaceship it&apos;s a perfect match. I recommend spending a little on a wheel for driving games or a flight joystick. It really adds to the experience.&lt;/p&gt;
&lt;p&gt;Non-cockpit games like FPS&apos;s are fun but using a keyboard/mouse or joypad steals away a bit of the experience. There are a lot of companies trying to improve on this. I&apos;m sure this year we will see a lot of innovation in regards to FPS controls when using a VR headset. &lt;a href=&quot;https://www.oculus.com/en-us/touch/&quot;&gt;Oculus Touch&lt;/a&gt; looks promising as well as &lt;a href=&quot;https://www.leapmotion.com&quot;&gt;Leap Motion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The technology is not perfect at the moment. Resolution is just a little  lower than you&apos;d like. The CV1, The first consumer version of the Riff, will improve upon this. I already have my order in for a CV1 and I hope to tell you the resolution change has really improved the product. I think for near perfect immersion a 4k resolution will be required. At the rate  this technology is progressing that can&apos;t be more than a couple years aways.&lt;/p&gt;
&lt;p&gt;We are just scratching the surface of what VR can and will do. VR will be a must have item before you know it.&lt;/p&gt;</content:encoded><category>oculus</category><category>virtual-reality</category><category>gaming</category></item><item><title>Automated Acceptance Testing with Selenium</title><link>https://rants.broonix.ca/blog/automated-acceptance-tests/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/automated-acceptance-tests/</guid><pubDate>Sat, 30 Jan 2016 13:34:06 GMT</pubDate><content:encoded>&lt;p&gt;More testing is never a bad thing. &lt;em&gt;Automating that testing is even better.&lt;/em&gt; We recently added automated acceptance testing to a spring-boot application.&lt;/p&gt;
&lt;p&gt;There were a few small hiccups to getting it implemented. Which I&apos;d like to share the solutions to.&lt;/p&gt;
&lt;h5&gt;Multiple Application Contexts&lt;/h5&gt;
&lt;p&gt;The first issue we ran into was defining the application context for our testing. We wanted to ensure our Integration and Unit tests were not interfering with our Acceptance tests. At first this was causing all kinds of problems. Spring&apos;s test runner was merging the two contexts we defined into one.&lt;/p&gt;
&lt;p&gt;This can all be fixed if you name the context for your acceptance test class. Just provide a &lt;code&gt;name = value&lt;/code&gt; to the &lt;code&gt;@SpringApplicationConfiguration&lt;/code&gt; annotation.&lt;/p&gt;
&lt;p&gt;This made sure the two contexts were not getting merged.&lt;/p&gt;
&lt;h5&gt;H2 not Resetting&lt;/h5&gt;
&lt;p&gt;Even if you add the &lt;code&gt;@Transational&lt;/code&gt; annotation to the tests executed by selenium changes will not be rolled back. They are not being done with in the transaction of the test case. Luckily H2 is simple to backup and restore.&lt;/p&gt;
&lt;p&gt;That&apos;s all you need to backup and restore. The overhead is minimal so we can do this before and after each test.&lt;/p&gt;
&lt;p&gt;If you want to persist date for the whole test class use &lt;code&gt;@BeforeClass&lt;/code&gt; and &lt;code&gt;@AfterClass&lt;/code&gt;. I do not recommend this since your tests might become order dependent.&lt;/p&gt;
&lt;h5&gt;Continuous Integration (Travis-CI)&lt;/h5&gt;
&lt;p&gt;We use travis to automate our builds. Travis already has built in support for Firefox. All we had to do is add a few lines to our travis config.&lt;/p&gt;</content:encoded><category>spring-boot</category><category>java</category><category>selenium</category></item><item><title>Java: Code coverage with Gradle and JaCoCo</title><link>https://rants.broonix.ca/blog/java-code-coverage-with-gradle-and-jacoco-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/java-code-coverage-with-gradle-and-jacoco-2/</guid><pubDate>Fri, 22 Jan 2016 21:22:04 GMT</pubDate><content:encoded>&lt;p&gt;There are so many great metrics to help you improve a project&apos;s quality. One of these metrics is &lt;strong&gt;code coverage&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this post we will go over why this measurement is important, how to measure coverage using &lt;a href=&quot;http://eclemma.org/jacoco/&quot;&gt;JaCoCo&lt;/a&gt;. I&apos;ll also wrap up with some info on &lt;a href=&quot;coveralls.io&quot;&gt;coveralls.io&lt;/a&gt; which allows you to mesure this metric between builds.&lt;/p&gt;
&lt;h5&gt;What is code coverage?&lt;/h5&gt;
&lt;p&gt;Code coverage is a measure of how much for your application&apos;s code has been executed in testing. This covers not only seeing which lines of code have been executed, but also checking that all branches have been covered.&lt;/p&gt;
&lt;p&gt;The a coverage tool will ensure your tests sent at least 3 cases:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = true
b = true
a = false, b = false
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Why measure code coverage?&lt;/h5&gt;
&lt;p&gt;The more code that you have covered the lower the probability of potential bugs. Coverage is not a cure all for bug free code, but does force you to think about writing more tests.&lt;/p&gt;
&lt;p&gt;After adding coverage reports to several work projects, I can tell you all of a sudden every developer on the team seems to commit a few more tests. No one wants to be the one that commits a large amount of untested code.&lt;/p&gt;
&lt;h5&gt;Setting up JaCoCo&lt;/h5&gt;
&lt;p&gt;I&apos;ll add coverage to a simple spring-boot web application. I have a pre-made simple boot app on git hub: https://github.com/brookslyrette/spring-boot-demo-starter. You can use this as a starting point. First download the code or clone the git repo &lt;code&gt;git clone https://github.com/brookslyrette/spring-boot-demo-starter.git&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You do not have to to add much to &lt;code&gt;build.gradle&lt;/code&gt; to generate a JaCoCo report.&lt;/p&gt;
&lt;p&gt;All you need to do to run your tests and generate a coverage report is &lt;code&gt;gradle test jacocoTestReport&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see I don&apos;t have proper coverage! I better add some more tests. Here is a small test for the &lt;code&gt;DemoController&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now if we run our coverage results again, we get a nice improvement:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see the final product here in GitHub: https://github.com/brookslyrette/gradle-jacoco-coverage&lt;/p&gt;
&lt;h5&gt;Coveralls.io&lt;/h5&gt;
&lt;p&gt;While having coverage reports for the current code is great, it&apos;s even more useful to track changes in coverage over time. I stumbled onto a great tool for this http://coveralls.io.&lt;/p&gt;
&lt;p&gt;It&apos;s free for any open source projects on GitHub or BitBucket and reasonably priced for private projects.&lt;/p&gt;
&lt;p&gt;It keeps track of how code coverage changes over each build.
&lt;/p&gt;
&lt;p&gt;Configuring this is as simple as adding this plugin to your build: https://github.com/kt3k/coveralls-gradle-plugin&lt;/p&gt;</content:encoded><category>coverage</category><category>gradle</category><category>java</category><category>coveralls-io</category></item><item><title>Remote working: 6+ Years Later!</title><link>https://rants.broonix.ca/blog/remote-working-6-years-later/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/remote-working-6-years-later/</guid><pubDate>Thu, 14 Jan 2016 21:58:00 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s hard to believe it, but I&apos;ve been working remotely for 6+ years now. Back in 2009 Dotsub closed its Ottawa office and I started working remotely. It&apos;s been an amazing journey. Now Dotsub has remote employees in Canada, The United States, Argentina, The United Kingdom, Italy, and Ireland.&lt;/p&gt;
&lt;p&gt;Remote working is becoming more and more prevalent in the tech sector. I figured it might be useful to share some of the lessons I&apos;ve learned over the years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You still should &apos;commute&apos; to work.&lt;/strong&gt; When you don&apos;t have to physically travel to an office to work it becomes easy to pick up some bad habits. You can literally roll out of bed, grab your laptop and start working. I&apos;ve found that over the long term this can adversely affect my mood and productivity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have a routine for your time before work.&lt;/strong&gt; Get dressed and make yourself look presentable. Have some breakfast and watch the news. In the nice summer months I&apos;ll even take the dog for a morning walk. I&apos;ve talked to other people who will drive to get their morning coffee.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take a real lunch.&lt;/strong&gt; You should not sit computer and eat everyday. Get out of the space you are working in and eat a good meal. Breaks a good for your mental health and an important part of any work day. I made this mistake for far too long. It was only after I started walking our dog on lunch that I learn how important taking a lunch break is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have a start and end time for your work day.&lt;/strong&gt; I love to code and sometimes I forget to stop. While it is great to be passionate about what you do, you can easily &lt;a href=&quot;https://en.wikipedia.org/wiki/Occupational_burnout&quot;&gt;burn&lt;/a&gt; yourself out. Make sure you know when to call it a day.&lt;/p&gt;
&lt;p&gt;If possible &lt;strong&gt;Have a designated work area.&lt;/strong&gt; I&apos;ve found it very helpful to have a space just for work. It makes it a bit easier to keep a work-life balance if you are not working and playing in the same space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use remote communication tools.&lt;/strong&gt; It is pretty easy to feel isolated when working remotely. Email is a pretty poor tool to bring a remote team together. We by using &lt;a href=&quot;https://campfirenow.com/&quot;&gt;Campfire&lt;/a&gt; in 2012  and later switched to &lt;a href=&quot;slack.com&quot;&gt;Slack&lt;/a&gt;. These tools help by not only creating a spot for meetings and work related chats, but a fun space to get to know everyone on your team. Our #General and #Random channels are our companies watercooler.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enjoy working remotely.&lt;/strong&gt; No more being stuck in traffic before and after work. You&apos;d be amazed how much time you win back by not commuting. I still smile a little when I hear the morning traffic report while sitting in my home office.&lt;/p&gt;</content:encoded><category>work</category><category>remote</category></item><item><title>XKCD Substitutions [Chrome Extension]</title><link>https://rants.broonix.ca/blog/xkcd-substitutions-chrome-extension/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/xkcd-substitutions-chrome-extension/</guid><pubDate>Fri, 08 Jan 2016 21:03:25 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://xkcd.com/&quot;&gt;XKCD&lt;/a&gt; was at it again with another funny substitutions comic.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I figured it would be fun to create a Chrome extension that does this. Truth be told there are already a ton of extenstions that do this in the &lt;a href=&quot;https://chrome.google.com/webstore/search/xkcd%20substitutions?hl=en&amp;amp;_category=extensions&quot;&gt;Chrome Store&lt;/a&gt;. XKCD even links to the most popular one: &lt;a href=&quot;https://chrome.google.com/webstore/detail/xkcd-substitutions/jkgogmboalmaijfgfhfepckdgjeopfhk?hl=en&quot;&gt;XKCD substitutions &lt;em&gt;offered by alec.posney&lt;/em&gt;&lt;/a&gt;. Since there are already so many competing plugins I decided to do this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve never written a Chrome extension before so here we go!&lt;/p&gt;
&lt;p&gt;After reading over the &lt;a href=&quot;https://developer.chrome.com/extensions/getstarted&quot;&gt;Getting Started&lt;/a&gt; guide and a little of the &lt;a href=&quot;https://developer.chrome.com/extensions/devguide&quot;&gt;documentation&lt;/a&gt;. It seemed logical to break this into two parts.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;background file&lt;/strong&gt; to keep track of the plugins state and a &lt;strong&gt;content script&lt;/strong&gt; to do the substitutions. I also made a few icons to denote the plugins state and of course a manifest file that defines the plugin.&lt;/p&gt;
&lt;p&gt;First let&apos;s dive into &lt;code&gt;manifest.json&lt;/code&gt;:
&lt;/p&gt;
&lt;p&gt;You can see this defines the name of and default icon of the extension. It also loads up &lt;code&gt;background.js&lt;/code&gt; and &lt;code&gt;substitutions.js&lt;/code&gt;, where all the work is done.&lt;/p&gt;
&lt;p&gt;It also defines what privileges we will need when running. This plugin needs access to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The active Chrome tab.&lt;/li&gt;
&lt;li&gt;The ability to run a background script.&lt;/li&gt;
&lt;li&gt;Access to storage to save the plugins state.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first thing I got working was the ability to store the plugins state and show the correct contextual status icon. This is all handled in &lt;code&gt;background.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The code is pretty self explanatory, when the user clicks the icon we toggle the plugins state. We save the new state to local storage and display the correct on or off icon. We also reload the webpage, this is to allow the content script to run if it needs to.&lt;/p&gt;
&lt;p&gt;Now for the fun part: &lt;code&gt;substitutions.js&lt;/code&gt;. This the content script that does all the on page work. Content scripts do have a limited amount of permissions so I do recommend reading the &lt;a href=&quot;https://developer.chrome.com/extensions/content_scripts&quot;&gt;documentation&lt;/a&gt;. In &lt;code&gt;manifest.json&lt;/code&gt; we set our content script to run on &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt; on the &lt;code&gt;document_idle&lt;/code&gt; event. This happens after page content is loaded and javascript has run.&lt;/p&gt;
&lt;p&gt;If the plugins state is enabled the &lt;code&gt;makeItBetter&lt;/code&gt; function is ran to do all of XKCD&apos;s replacements. It walks all the text nodes in the DOM and does a two step replace. I had started with a one step replace but it caused some issues due to &quot;Years ➜ Minutes&quot; and &quot;Minutes ➜ Years&quot;.&lt;/p&gt;
&lt;p&gt;The results are pretty amusing!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;My version is here on GitHub: https://github.com/brookslyrette/chrome-xkcd-substitutions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;All credit for this being interesting or funny goes to &lt;a href=&quot;http://xkcd.com/&quot;&gt;XKCD&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><category>xkcd</category><category>javascript</category><category>chrome</category></item><item><title>Building a Universal Translator</title><link>https://rants.broonix.ca/blog/building-a-universal-transaltor/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/building-a-universal-transaltor/</guid><pubDate>Fri, 01 Jan 2016 14:02:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this year, some of us around the office were rather impressed with &lt;a href=&quot;http://www.skype.com/en/translator-preview/&quot;&gt;Skype Translator&lt;/a&gt;. It&apos;s a very cool feature that shows just how far realtime translation has come in the last few years.&lt;/p&gt;
&lt;p&gt;When I first saw this cool feature, I told my co-workers: &lt;em&gt;&quot;I&apos;m pretty sure we could build something similar to that in a few hours&quot;&lt;/em&gt;. I didn&apos;t want to look like I was just bullshitting them, so I set aside a few hours and built it.&lt;/p&gt;
&lt;p&gt;This post outlines how you can build your own universal translator with nothing but Javascript. It&apos;s not nearly as feature rich as what Skype is doing. It goes give you an idea of what can be done with existing Javascript API&apos;s in Chrome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt;&lt;br /&gt;
Demo &lt;em&gt;[Chrome only]&lt;/em&gt;: https://universal-translator.dotsub.com/
Code: https://github.com/dotsub/universal-translator&lt;/p&gt;
&lt;p&gt;First let&apos;s decompose the steps required. The universal translator needs to do three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Recognise what the user is saying.&lt;/li&gt;
&lt;li&gt;Translate the spoken phrase.&lt;/li&gt;
&lt;li&gt;Speak the result.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Speech Recognition&lt;/h4&gt;
&lt;p&gt;Google Chrome has a built in &lt;a href=&quot;https://developers.google.com/web/updates/2013/01/Voice-Driven-Web-Apps-Introduction-to-the-Web-Speech-API?hl=en&quot;&gt;speech recognition engine&lt;/a&gt;. Their implementation is based on the w3 speech-api &lt;a href=&quot;https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html&quot;&gt;community group specification&lt;/a&gt;. Using this engine is very simple.&lt;/p&gt;
&lt;p&gt;It is important to properly set &lt;code&gt;recognition.lang&lt;/code&gt;. To the language the user is speaking. Now we have the spoken input from the user. This is all we need to complete step one.&lt;/p&gt;
&lt;h4&gt;Machine Translation&lt;/h4&gt;
&lt;p&gt;We will use Google&apos;s &lt;a href=&quot;https://cloud.google.com/translate/docs&quot;&gt;Translation API&lt;/a&gt; to translate our text. The code is a bit ugly, I stole it out of one of my old projects.&lt;/p&gt;
&lt;h4&gt;Speech Synthesis&lt;/h4&gt;
&lt;p&gt;The w3 speech-api also includes a speech synthesis engine. It only takes a few lines to get the browser to speak any line of text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var msg = new SpeechSynthesisUtterance();
msg.text = &quot;Hello World&quot;;
window.speechSynthesis.speak(msg);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the full synthesis part of my universal translator. It takes input from the user translates it and speaks the result.
&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;There you have it, less than 150 lines of Javascript that makes a universal translator. The finished demo here: https://universal-translator.dotsub.com/. You can look over the code here: https://github.com/dotsub/universal-translator&lt;/p&gt;</content:encoded><category>javascript</category><category>translation</category><category>coding</category></item><item><title>Happy Holidays!</title><link>https://rants.broonix.ca/blog/happy-holidays-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/happy-holidays-2/</guid><pubDate>Sat, 26 Dec 2015 14:46:44 GMT</pubDate><content:encoded>&lt;p&gt;Due to traveling and spending time with family, there will not be an in depth post this week.&lt;/p&gt;
&lt;p&gt;Happy holidays to everyone!&lt;/p&gt;</content:encoded></item><item><title>Migrating from Refluxjs to Redux</title><link>https://rants.broonix.ca/blog/moving-from-refluxjs-to-redux/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/moving-from-refluxjs-to-redux/</guid><pubDate>Fri, 18 Dec 2015 20:03:00 GMT</pubDate><content:encoded>&lt;p&gt;Unless you have been living under a rock. I&apos;m sure you are aware of all the buzz behind &lt;a href=&quot;https://facebook.github.io/react/&quot;&gt;React&lt;/a&gt;. Our latest project at work is React based. It&apos;s been so much fun to get our hands on a new UI framework.&lt;/p&gt;
&lt;p&gt;We built out the initial proof of concept using React, &lt;a href=&quot;https://github.com/rackt/react-router&quot;&gt;React-Router&lt;/a&gt; and &lt;a href=&quot;https://github.com/reflux/refluxjs&quot;&gt;Refluxjs&lt;/a&gt;. Refluxjs was a great way to get the project started quickly. But we quickly started noticing some things that got us worried.&lt;/p&gt;
&lt;p&gt;It seems like development on Refluxjs &lt;a href=&quot;https://github.com/reflux/refluxjs/issues/436&quot;&gt;is stalled&lt;/a&gt;. Refluxjs lacks es6 support. It does not currently support universal apps. While it does have simplicity over other Flux implementations, these all come at a cost. By eliminating the global dispatcher you lose out on the ability to apply middleware. It promotes the use of &lt;code&gt;this.state&lt;/code&gt; in components.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rackt/redux&quot;&gt;Redux&lt;/a&gt; on the other hand seems to have a huge community around it. It&apos;s development is &lt;a href=&quot;https://github.com/rackt/redux/commits/master&quot;&gt;very active&lt;/a&gt;. It has support for universal apps. It&apos;s creator &lt;a href=&quot;https://twitter.com/dan_abramov&quot;&gt;Dan Abramov&lt;/a&gt; was recently hired by facebook.&lt;/p&gt;
&lt;p&gt;Since we hope to have our current project around for a long time, we decided it would be best to migrate to Redux.&lt;/p&gt;
&lt;h5&gt;How it worked in Refluxjs&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;╔═════════╗       ╔════════╗       ╔═════════════════╗
║ Actions ║------&amp;gt;║ Stores ║------&amp;gt;║ View Components ║
╚═════════╝       ╚════════╝       ╚═════════════════╝
     ^                                      │
     └--------------------------------------┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;How it works in Redux&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;╔════════════════╗       ╔════════════╗       ╔════════════╗
║ Action Creator ║------&amp;gt;║ Dispatcher ║------&amp;gt;║   Reducer  ║
╚════════════════╝       ╚════════════╝       ╚════════════╝
         ^                                           |
         |                                           |
         |                                           v
╔═════════════════╗                              ╔════════╗ 
║ View Components ║ &amp;lt;--------------------------  ║ Store  ║
╚═════════════════╝                              ╚════════╝ 
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;Notes on the migration&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;There is now just one store. It does seem like a good idea to be able to represent the state of your app in one object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is only one reducer. You can however logically separate your code using &lt;code&gt;combineReducers&lt;/code&gt; and creating a root reducer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reflux maps properties from the stores as &lt;code&gt;this.state&lt;/code&gt;. This was convenient, but React advises keeping your components stateless.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you are dealing with data from a server you are going to need some middleware. Right now we have been using &lt;a href=&quot;https://github.com/gaearon/redux-thunk&quot;&gt;redux-thunk&lt;/a&gt; to add data to the server before dispatching the events to update the state.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>react</category><category>redux</category><category>refluxjs</category></item><item><title>Migrating to Travis-CI from Bamboo</title><link>https://rants.broonix.ca/blog/migrating-from-bamboo-to-travis-ci/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/migrating-from-bamboo-to-travis-ci/</guid><pubDate>Sat, 12 Dec 2015 13:25:02 GMT</pubDate><content:encoded>&lt;p&gt;At work we have been using &lt;a href=&quot;http://broonix-rants.ghost.io/updating-github-status-from-bamboo/&quot;&gt;Bamboo&lt;/a&gt; for a little over a year. It has served us well in the past, but for our newest project it has just not be working well. The &lt;em&gt;cloud&lt;/em&gt; version seems to be severely limited.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Want to &lt;a href=&quot;https://marketplace.atlassian.com/plugins/net.utoolity.atlassian.bamboo.tasks-for-aws/server/overview&quot;&gt;upload your app to AWS&lt;/a&gt; after it&apos;s built&lt;/em&gt;? Yes, there is a plugin for that but it does not support Bamboo cloud. &lt;em&gt;Headless browser testing?&lt;/em&gt;  Well it is possible, but you have to configure your own EC2 instances.&lt;/p&gt;
&lt;p&gt;Bamboo just does not seem to be moving forward. As a tool it was starting to get in our way. We decided it was time for our team to try something new.&lt;/p&gt;
&lt;p&gt;I&apos;ve been looking for a opportunity to give &lt;a href=&quot;travis-ci.com&quot;&gt;Travis-CI&lt;/a&gt; a try. Travis-CI builds over 200,000 project and has over 100,000 users. They must be onto something. It almost feels like every GitHub project is building with Travis-CI.&lt;/p&gt;
&lt;p&gt;In less than a few hours we migrated to Travis-CI. It was quick and painless.&lt;/p&gt;
&lt;h3&gt;How we migrated&lt;/h3&gt;
&lt;p&gt;Unlike Bamboo, Travis-CI keeps the build configuration in your project. &lt;code&gt;.travis.yml&lt;/code&gt; is where all the magic happens. This file controls everything.&lt;/p&gt;
&lt;h4&gt;Step 1: Create an account&lt;/h4&gt;
&lt;p&gt;You of course have to set an account with Travis-CI. They use your GitHub credentials to login. Then just enable your repository in Travis-CI: https://travis-ci.com/getting_started&lt;/p&gt;
&lt;h4&gt;Step 2: Building&lt;/h4&gt;
&lt;p&gt;To have your project build you first need to add a &lt;code&gt;.travis.yml&lt;/code&gt;. Our project is Java 8/Gradle so the initial file looked like this:&lt;/p&gt;
&lt;h4&gt;Step 3: Migrating status scripts&lt;/h4&gt;
&lt;p&gt;I wrote some status scripts that I covered in a previous &lt;a href=&quot;http://broonix-rants.ghost.io/updating-github-status-from-bamboo/&quot;&gt;blog post&lt;/a&gt;.  I won&apos;t go into their functional details here, but I will cover the migration changes.&lt;/p&gt;
&lt;p&gt;Travis-CI also lets you run scripts. These scripts have to be part of the repository. This is a really good idea, build scripts are code and all code should be in your repository. I added the script to our project under &lt;code&gt;src/main/scripts/post-build.sh&lt;/code&gt;. I had to replace the Bamboo variables with their Travis-CI equivalents.&lt;/p&gt;
&lt;p&gt;All we have to do now is tell Travis-CI to call this script after the build. This is done with the &lt;code&gt;after_success&lt;/code&gt; setting.&lt;/p&gt;
&lt;p&gt;That&apos;s all it took. Six lines of configuration and moving our post build scripts.&lt;/p&gt;
&lt;h3&gt;Future topics&lt;/h3&gt;
&lt;p&gt;Setting up the new build took so little time that I ended up setting up a few extras:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://coveralls.io/&quot;&gt;Coveralls&lt;/a&gt; coverage reporting.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://slack.com/&quot;&gt;Slack&lt;/a&gt; Notifications for Travis builds.&lt;/li&gt;
&lt;li&gt;Automatic deployment of our builds to &lt;a href=&quot;http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/Welcome.html&quot;&gt;Amazon Elastic Beanstalk&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;ll cover those three and headless browser testing in a future post!&lt;/p&gt;</content:encoded><category>travis-ci</category><category>java</category><category>spring-boot</category><category>github</category></item><item><title>Are you linting yet?</title><link>https://rants.broonix.ca/blog/are-you-linting-yet/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/are-you-linting-yet/</guid><pubDate>Thu, 03 Dec 2015 21:27:00 GMT</pubDate><content:encoded>&lt;p&gt;If you aren&apos;t you should be! If you are like me, you have a love and hate relationship with Javascript. It&apos;s used to do so much, but it&apos;s loose rule set makes it a potential nightmare.&lt;/p&gt;
&lt;p&gt;At work we already were using &lt;a href=&quot;http://checkstyle.sourceforge.net/&quot;&gt;Checkstyle&lt;/a&gt; and &lt;a href=&quot;http://findbugs.sourceforge.net/&quot;&gt;FindBugs&lt;/a&gt; for our Java code, so why not apply the same idea to our Javascript and React code?&lt;/p&gt;
&lt;p&gt;There a few options, but we chose to go with &lt;a href=&quot;http://eslint.org/&quot;&gt;eslint&lt;/a&gt;. It has a large number of plugins and support for React.&lt;/p&gt;
&lt;p&gt;Setup was dead simple. Create a &lt;code&gt;.eslintrc&lt;/code&gt; file in the project root. Then add &lt;code&gt;gulp-eslint&lt;/code&gt; to the gulp build chain.&lt;/p&gt;
&lt;p&gt;We decided to follow the &lt;a href=&quot;https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb&quot;&gt;airbnb&lt;/a&gt; style. We did make some small alterations.&lt;/p&gt;
&lt;p&gt;Now what happened next was not as simple:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Clean up was pretty easy. Most of those errors and warnings were the use of &lt;code&gt;var&lt;/code&gt; or small style violations. After a couple of hours we now have a much cleaner, consistent and more maintainable Javascript and React code.&lt;/p&gt;</content:encoded><category>react</category><category>javascript</category><category>lint</category><category>eslint</category></item><item><title>Practice Practicing</title><link>https://rants.broonix.ca/blog/practice-practicing/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/practice-practicing/</guid><pubDate>Sun, 29 Nov 2015 14:34:16 GMT</pubDate><content:encoded>&lt;p&gt;We heard it all so many times in our lives. &lt;em&gt;Practice, Practice, Practice&lt;/em&gt; or &lt;em&gt;Practice makes perfect&lt;/em&gt;. But sometimes you have to remember it&apos;s not just about the act of practicing but you ned to learn to practice properly.&lt;/p&gt;
&lt;p&gt;This is something I&apos;ve always been poor at. I&apos;ll pick up a guitar to &apos;practice&apos; but will just end up &lt;a href=&quot;http://www.guitarworld.com/beyond-fretboard-why-guitar-noodling-might-be-underrated&quot;&gt;noodling&lt;/a&gt; for an hour or so. I&apos;ll do the same sim-racing, I&apos;ll just load up a track and blast out a few laps and consider it as a practice. Neither of these are really practice.&lt;/p&gt;
&lt;p&gt;I&apos;ve been trying to improve how I practice, I&apos;ve done this by adding a few things to my practice routines.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Goals&lt;/li&gt;
&lt;li&gt;Plan&lt;/li&gt;
&lt;li&gt;Analysis&lt;/li&gt;
&lt;li&gt;Back to #1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Never sit down and practice mindlessly. You should have a goal before you sit down to practice. It could be something simple like learning a small riff in a song you like or in racing it can be something like improving your lap times or consistency.&lt;/p&gt;
&lt;p&gt;From these goals you can define a plan on how to practice. Find the tabs you need to learn, the scales you need to practice. Load up the right car/track combo to get rolling.&lt;/p&gt;
&lt;p&gt;Now comes the most important part, you need to be able to analyse your practice session vs your goal. For my racing hobby this is very easy. There are a ton of online tools to analyse every turn and lap you do in a session. You can even compare your laps against some of the fastest in the world. This gives you a great way to set your goals for the next practice.&lt;/p&gt;
&lt;p&gt;For my guitar playing, I&apos;ve started recording all my practice sessions. I can go back and listen to everything to see what is and is not working. From there you can figure out your next goals.&lt;/p&gt;
&lt;p&gt;So far it&apos;s been paying off.&lt;/p&gt;</content:encoded></item><item><title>Spring-Boot: Building Bootstrap with Gulp and Gradle</title><link>https://rants.broonix.ca/blog/spring-boot-building-bootstrap-with-gulp-2/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/spring-boot-building-bootstrap-with-gulp-2/</guid><pubDate>Sat, 21 Nov 2015 14:03:46 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://getbootstrap.com&quot;&gt;Bootstrap&lt;/a&gt; is one of my favorite CSS frameworks. I&apos;ve used it in many of my Java projects. One problem I&apos;ve often ran into is how to include Bootstrap in your project.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen it done in quite a few ways. The most popular are. Added as a static library or importing it via webjars.&lt;/p&gt;
&lt;p&gt;To me both these options miss out on the best part of bootstrap. It&apos;s not about the CSS but about the LESS/SASS that compiles into bootstrap&apos;s CSS. This makes it so simple and quick to customize.&lt;/p&gt;
&lt;p&gt;##Getting Started&lt;/p&gt;
&lt;p&gt;We&apos;ll start with a simple spring-boot web application. I have a pre-made simple boot app on git hub: https://github.com/brookslyrette/spring-boot-demo-starter. You can use this as a starting point. First download the code or clone the git repo &lt;code&gt;git clone https://github.com/brookslyrette/spring-boot-demo-starter.git&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;##How it all works&lt;/p&gt;
&lt;p&gt;We are going to make bootstrap a first class member of our spring-boot projects build. We will use &lt;a href=&quot;http://gulpjs.com/&quot;&gt;gulp&lt;/a&gt; to build a customized version of bootstrap.&lt;/p&gt;
&lt;p&gt;##Building
First we will add node and gulp to our gradle build. This will give us the tools we need to build bootstrap. There is already a great plugin for this: https://github.com/srs/gradle-gulp-plugin . Configuration is pretty simple. Just add the plugin and configure when it runs in the build. You can see my comments on what does what in the gist below.&lt;/p&gt;
&lt;p&gt;You how have to to add a &lt;code&gt;packages.json&lt;/code&gt; file. This will install all the node packages our gulp build chain will use. You can use npm to create this file via &lt;code&gt;npm init&lt;/code&gt;. You can just hit return or enter values for all the questions npm will ask. Now you should have a &lt;code&gt;packages.json&lt;/code&gt; file that looks something like this.&lt;/p&gt;
&lt;p&gt;We will be building from the SASS bootstrap sources. We can install this using: &lt;code&gt;npm install --save bootstrap-sass&lt;/code&gt;. You&apos;ll see &lt;code&gt;packages.json&lt;/code&gt; now contains this dependency. We will also install gulp-sass with &lt;code&gt;npm install --save gulp-sass&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;####The SASS file&lt;/p&gt;
&lt;p&gt;We should now add a SASS file that will contain our own styles and bootstrap customizations. We will add this file under &lt;code&gt;src/main/sass&lt;/code&gt; and call it &lt;code&gt;global.scss&lt;/code&gt;. We can leave this blank for now.&lt;/p&gt;
&lt;p&gt;####gulpfile.js&lt;/p&gt;
&lt;p&gt;Our gulpfile will let us do two things. First it will have a &lt;code&gt;build&lt;/code&gt; task that gets called by gradle. Second a watch task that lets us run gulp and have any changes we make instantly compiled into our CSS files.&lt;/p&gt;
&lt;p&gt;Now we have everything in place! You can run this &lt;em&gt;empty&lt;/em&gt; build with &lt;code&gt;gradle bootRun&lt;/code&gt;. You&apos;ll see the project now creates and copies css into &lt;code&gt;src/main/resources/static&lt;/code&gt; to be used in your project.&lt;/p&gt;
&lt;p&gt;##Using and Customizing
Bootstrap has a great &lt;a href=&quot;https://getbootstrap.com/examples/theme/&quot;&gt;theme demo page&lt;/a&gt;. I&apos;ve made it thymeleaf compatible so we can test our customized theme. You can just cut and paste this into &lt;code&gt;home.html&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Lets launch with &lt;code&gt;grable bootRun&lt;/code&gt; and see how it all looks.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Well that&apos;s not too pretty. How about we open up &lt;code&gt;global.scss&lt;/code&gt; and pull in bootstrap. All we need to do is add the import into the file.
&lt;/p&gt;
&lt;p&gt;Now when we rerun the app we see a much prettier picture.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now we can customize some of the styles. Bootstrap has a variable for everything. Lets tweak the navbar&apos;s color and some of the buttons.&lt;/p&gt;
&lt;p&gt;Be sure to add all customizations before the bootstrap import. Here is my custom theme for this post.&lt;/p&gt;
&lt;p&gt;Now the demo page looks like.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Any customization is now just a line or two of SASS away.&lt;/p&gt;
&lt;p&gt;You can download the completed project here: https://github.com/brookslyrette/sass-gradle-gulp&lt;/p&gt;</content:encoded><category>spring-boot</category><category>css</category><category>bootstrap</category><category>less</category><category>sass</category><category>java</category></item><item><title>Updating GitHub Status from Bamboo</title><link>https://rants.broonix.ca/blog/updating-github-status-from-bamboo/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/updating-github-status-from-bamboo/</guid><pubDate>Sat, 14 Nov 2015 16:15:34 GMT</pubDate><content:encoded>&lt;p&gt;GitHub has a great &lt;a href=&quot;http://https://developer.github.com/v3/repos/statuses/&quot;&gt;Statuses API&lt;/a&gt;. This gives you a great way to update the information from your automated build process. Giving you a quick overview of the status of a branch.&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;http://dotsub.com&quot;&gt;Dotsub&lt;/a&gt; we use &lt;a href=&quot;https://www.atlassian.com/software/bamboo/&quot;&gt;Bamboo&lt;/a&gt; to build our applications. Bamboo does not have a native GitHub integration so we had to create our own.&lt;/p&gt;
&lt;p&gt;We wanted to ensure that our unit tests, &lt;a href=&quot;http://findbugs.sourceforge.net&quot;&gt;findbugs&lt;/a&gt; report and &lt;a href=&quot;http://checkstyle.sourceforge.net&quot;&gt;checkstyle&lt;/a&gt; reports were reflected in the GitHub branch status.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is pretty simple to set up. We&apos;ll use two small scripts and cURL. First let&apos;s add a Script task at the start of our build. This will update the branch status so we know it&apos;s building.&lt;/p&gt;
&lt;p&gt;Add this task at the very start of the build.
&lt;/p&gt;
&lt;p&gt;The script looks like this:
&lt;/p&gt;
&lt;p&gt;This updates GitHub and shows that branches status as &apos;building&apos;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now all we have to do is update the build at the end as well. We will use grep to count the errors in the reports. This was we can include this on the branch details.&lt;/p&gt;
&lt;p&gt;This updates GitHub will the status and the number of each type of error. You instantly know the status/mergeability of any branch in your repository.
&lt;/p&gt;
&lt;p&gt;There you have it. A quick and simple way to set up Bamboo to update GitHub with build statuses. Your team will always know the status of every branch in your repo.&lt;/p&gt;</content:encoded><category>github</category><category>bamboo</category></item><item><title>Why a blog?</title><link>https://rants.broonix.ca/blog/testing-the-new-blog/</link><guid isPermaLink="true">https://rants.broonix.ca/blog/testing-the-new-blog/</guid><pubDate>Wed, 11 Nov 2015 01:20:34 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s been ages since I told myself &lt;em&gt;&quot;You should really start a blog&quot;&lt;/em&gt;. I&apos;m finally making good on that idea today.&lt;/p&gt;
&lt;p&gt;First a little about me. I&apos;m a software engineer from Ottawa Canada. I am the CTO at &lt;a href=&quot;https://dotsub.com&quot;&gt;Dotsub&lt;/a&gt; a very amateur &lt;a href=&quot;https://www.youtube.com/user/Broonix&quot;&gt;guitarist&lt;/a&gt; and avid &lt;a href=&quot;https://www.youtube.com/channel/UCK_orOmsbx7gfi6OoS_vn2g&quot;&gt;sim-racer&lt;/a&gt;. Like most Canadians I enjoy hockey and beer.&lt;/p&gt;
&lt;p&gt;On this blog I hope to share a few random thoughts. Maybe a few how to articles.&lt;/p&gt;
&lt;p&gt;You&apos;ll have to excuse my spelling and grammar. You know the old joke:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This idea may or may not pan out, but for now I&apos;m hoping to blog at least once a week.&lt;/p&gt;
&lt;p&gt;Let&apos;s see how this goes!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I&apos;m pretty sure no one will read this.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>intro</category><category>me</category></item></channel></rss>