Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://edm00se.io/rss.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  3.    <channel>
  4.        <title>Eric McCormick's Dev Blog</title>
  5.        <link>https://edm00se.io/</link>
  6.        <description>software development, operations, tooling, and tech musings</description>
  7.        <lastBuildDate>Thu, 28 Mar 2024 21:55:31 GMT</lastBuildDate>
  8.        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
  9.        <generator>Gridsome Feed Plugin</generator>
  10.        <image>
  11.            <title>Eric McCormick's Dev Blog</title>
  12.            <url>https://edm00se.io/logo_600.png</url>
  13.            <link>https://edm00se.io/</link>
  14.        </image>
  15.        <copyright>Copyright 2014–2024 Eric McCormick</copyright>
  16.        <atom:link href="https://edm00se.io/rss.xml" rel="self" type="application/rss+xml"/>
  17.        <item>
  18.            <title><![CDATA[Putting The Bots To Work]]></title>
  19.            <link>https://edm00se.io/putting-the-bots-to-work/</link>
  20.            <guid>https://edm00se.io/putting-the-bots-to-work/</guid>
  21.            <pubDate>Fri, 10 Nov 2023 21:58:45 GMT</pubDate>
  22.            <description><![CDATA[when you can't beat 'em, sic the bots on 'em]]></description>
  23.            <content:encoded><![CDATA[Happy November everybody! I made it through October and in spite of some of the doom and gloom to come in this one, I still had _some_ fun in moderating my usual contributing open source repository to Hacktoberfest.
  24.  
  25. ### Hacktoberfest 2023 and Lessons Learned
  26.  
  27. Since 2018, I've been not only participating, but opening up an open source repository of mine for contributions with [Hacktoberfest](https://hacktoberfest.com/). This was Hacktoberfest's 10th year and I've contributed for most of them, but the desire to contribute a repository was born from a desire to share something fun and help others gain in the ways of open source contribution. This year didn't go perfectly, they never do, and I had to learn a few things, which I usually do. Primarily of note this year was that the amount of traffic my humble repo got was significantly larger than any previous year. How much more? One eager individual submitted 17 PRs immediately and all together, which is more than I had a couple of years. Here's some data charted out for those that like such things. There's also a curious uptick in this year's number of un-merged PRs marked in the chart as "gap", some were duplicates that could have been easily found (a requirement in the checks specified in my repo's Pull Request template btw), others were low effort and not the sort of contributions requested, and some were even abandoned as soon as there was any feedback.
  28.  
  29. ![spreadsheet screenshot of by year values of opened vs merged PRs and expressed as a bar chart](./images/pr_data_and_graph_updated.png)
  30.  
  31. If you're looking to get some Pull Request data, my search filter to limit by creation within a given month of a year and remove dependabot looks like this:
  32.  
  33. ```
  34. is:pr created:2023-10-01..2023-10-31
  35. ```
  36.  
  37. To filter out dependabot, which showed up under two names in my repo, add `-author:app/dependabot -author:app/dependabot-preview`.
  38.  
  39. If you want to see merged, tack on `state:merged` to that query. [Here's the merged search for this October for reference](https://github.com/edm00se/awesome-board-games/pulls?q=is%3Apr+created%3A2018-10-01..2018-10-31+-author%3Aapp%2Fdependabot+-author%3Aapp%2Fdependabot-preview+state%3Amerged+).
  40.  
  41. ### When Do The Bots Come In?
  42.  
  43. A little over a week into October, I had realized I had a problem. I knew I had to do something as nearly a hundred PRs had been opened within the first two weeks. So I did what any seasoned developer would do, I looked for a "cheat" to speed up my efforts. Enter the bots. I decided to see what responses I would get from some of the LLM chat "AI" interfaces in an attempt to compare them and see how close to a working output I could get without needing to do the work myself; the adage of "a good developer is a lazy developer" comes to mind.
  44.  
  45. ### The Showdown
  46.  
  47. I began by asking for a [GitHub Action](https://github.com/features/actions) that would suit my needs, specifically one to detect other open PRs by the author and fail the check should it be over a specified threshold. I then asked for the bot to update should I apply the "hacktoberfest-accepted" label, allowing me as the maintainer to effectively override this, since I did expect a few log jams that could need clearing. I repeated this two part request for each of those I tried out. I also only tested those I could use for free, because this was a one-off and I'm a bit of a cheap skate with disposable technology.
  48.  
  49. #### [OpenAI ChatGPT](https://chat.openai.com) (3.5, aka the free one)
  50.  
  51. I began with the one that is frequently talked about most. I was a bit surprised by this one as it had my highest expectations, in spite of low expectations in general. On the first ask, I was given a mild surprise as I was returned code in two parts, not one. The first was a node script using `@actions/core` and `@actions/github` to do the heavy lifting. It reads about how you would expect.
  52.  
  53. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#1a_openai_script.js
  54.  
  55. The wiring up of the GitHub Action via its workflow YAML was also quite straight forward. It checks out the code, installs the node dependencies, then executes the script.
  56.  
  57. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#1b_openai_config.yml
  58.  
  59. After my follow up to allow for the label to override the failing check when present.
  60.  
  61. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#1c_openai_updated_script_snippet.js
  62.  
  63. ##### The Other Thing
  64.  
  65. The Eagle eyed among you may have noticed something which dates the response and the built LLM a bit. While everything seemed functional, the node version specified was node 14. Node 14 was in active LTS for a while, then moved to maintenance for security updates, and then was End Of Life'd in April of this year (2023) in accordance with the [node release schedule](https://nodejs.org/en/about/previous-releases). I asked for it to be updated to latest LTS and was basically told to figure it out myself.
  66.  
  67. ![a reply from ChatGPT telling me to just figure it out myself](./images/ChatGPT_dated_node_version_knowledge.png)
  68.  
  69. I know many like to joke about the volatility and pace of web development compared to other development circles, but I found it a little remarkable that ChatGPT couldn't just do a basic query to at least tell me what to update to for the latest LTS. It's not that I needed it, but that it would be incredibly simple and highlights the limits of LLMs being trained as they were when that one was built.
  70.  
  71. ##### Verdict
  72.  
  73. This wasn't a bad place to start, but I decided to try the others once this split the files into two. This combined with the dated knowledge of node made me wonder if that was what I should persue as I was really looking for a drop-in solution that required no other files.
  74.  
  75. #### [Google Bard](https://bard.google.com)
  76.  
  77. Bard started strong, rolling up all the work into a single step and making good use of the `actions/github-script` to both attain the needed dependency functionality and also keep it inside the one file. Note: in no cases did I specify the desire for a single file, but nearly all returned with a single file. Here's what it returned after my update to request the label to escape the condition.
  78.  
  79. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#2b_bard_action_updated.yml
  80.  
  81. A nice feature I noticed only for Bard was the citation of sources. That's right, they call it out with a numbered marker on the side, clicking on it gives the hyperlink, as does it show in the footer of the response. This was an immensely useful feature and it didn't matter that the cited link was in Japanese, using Google Translate I was then able to read and understand the majority of the source. That the source was cited and that it was done with specificity as to what component it was generating gave me some level of assurance either to vet the information myself or at least know who to blame 🙃.
  82.  
  83. ![Bard cites specific sources under some circumstances, footer note with hyperlink shown](./images/bard_citing_source.png)
  84.  
  85. ##### Verdict
  86.  
  87. This looked promising, so promising that I attempted to implement the action, in a test repository at first. Until it was deployed in my actual repository with other people submitting PRs I didn't realize that I was apparently the noted "author" and had to make more changes. So this one was my starting point, but it was no out of the box working experience. That said, knowing the source it used to help come up with the answer was good, in that I could read up on what the human being that it pulled from was talking about.
  88.  
  89. #### [Claude](https://claude.ai)
  90.  
  91. For any who aren't aware of it, Claude is made [by Anthropic and at least claims](https://www.anthropic.com/index/introducing-claude) to train its models to be "helpful, honest, and harmless". What that functionally means, I'm not entirely certain of, but with any luck it means they aren't stealing some [poor artist's exposure bucks](https://theoatmeal.com/comics/exposure). On first request, it gave me something that wouldn't work, then on calling it out it gave me back the exact same script, except that it had capitalized the word "count". Maybe this sort of question isn't the sort Claude is good for as I've had a couple decent interactions with other tests I've run. See if you can spot the issue.
  92.  
  93. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#3b_claude_aciton_updated.yml
  94.  
  95. Right out the gate, wherever Claude pulled this from was attempting to execute the action only on `workflow_dispatch`, which being familiar with GitHub Actions I can tell you is only [for manual invocations](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch), meaning someone would have to go to the Actions tab in the repository and click the "Run Workflow" button. It looked like the assumption from the response was that I'd be providing both the author and repo name and it would spit back the number count of `state: open` PRs, as detected via `octokit/request-action`.
  96.  
  97. ##### Verdict
  98.  
  99. This wasn't for my need, so I moved on.
  100.  
  101. #### Bing Chat
  102.  
  103. Officially, Microsoft's Bing team acts like Bing Chat is only available from MS Edge. In reality, if you have [the relevant URL](https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx), you can get to it from presumably any modern web browser. Also, I didn't add this one until after I solved my problems. It was a bit of code that I needed to implement with some urgency. That said, I'm somewhat disappointed I didn't think to check Bing Chat's response at the time, as I rather enjoy that in spite of being written primarily as a BASH script, it makes use of the GitHub API directly to get the data needed and does so with an apparently decent amount of knowledge as to what I was looking for. Here it is after being updated to allow for the label escape.
  104.  
  105. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#4_bing_chat_action_updated.yml
  106.  
  107. ##### Verdict
  108.  
  109. I was intrigued, in spite of having already moved on. Maybe I'll consider checking against Bing in the future, as this seemed the most cohesive and likely to work out of the box. Then again it carries a downside of bing a BASH script at its heart, not a problem per se, but I do spend most of my time in JS/TS/Node and something in that flavor makes it instantly more easy to maintain.
  110.  
  111. ### How I Concluded Things
  112.  
  113. Ultimately, I was pressed for time and had to go with the one I felt most comfortable with, the Bard solution. As I mentioned already I had to change things after I realized it didn't do _exactly_ what I needed to, after having implemented it live; there's nothing like testing in prod as they say. In any case, here's what I came up with in the end. I tried to lean hard into the using the steps as the logic, had to forcibly filter the open PRs so it wouldn't confuse all of those in my repo with the PR author's, set that as output from the step, which I could use to drive the message and fail or succeed steps accordingly. It's not perfect, but I can easily maintain it and it helped a fair amount for the remainder of the month since it went online over [`240 workflow runs`](https://github.com/edm00se/awesome-board-games/actions/workflows/limit-prs.yml). Here's a copy, otherwise [you can see it where it lives](https://github.com/edm00se/awesome-board-games/blob/65a2f528c076f9d66179d59a7fa152d918a35c6d/.github/workflows/limit-prs.yml).
  114.  
  115. https://gist.github.com/edm00se/7e03ec794c947f58ec376b38920632ab#my_final.yml
  116.  
  117. ### Take Aways
  118.  
  119. Ultimately, I still had to write some of it at the end of the day. I was almost disappointed, but my expectations going into it weren't for anything truly magical. For many it may have been a help to have a starting point, but as someone who's both familiar with writing GitHub Actions and other scripts for automation, I'd call it a bit of an draw since I spent more time debugging what had gone wrong in the example I was given, rather than just writing it myself. In short, my day job is secure, at least for a while.
  120.  
  121. Until next time, cheers. ☕️
  122. ]]></content:encoded>
  123.        </item>
  124.        <item>
  125.            <title><![CDATA[JSON and Number Formats]]></title>
  126.            <link>https://edm00se.io/json-and-number-formats/</link>
  127.            <guid>https://edm00se.io/json-and-number-formats/</guid>
  128.            <pubDate>Fri, 06 Oct 2023 00:00:00 GMT</pubDate>
  129.            <description><![CDATA[not the fun time I was looking for]]></description>
  130.            <content:encoded><![CDATA[I know it's been a while, figured I'd pop up write a quick blog.
  131.  
  132. ![a kraken lifting a sailing ship above water, cracking it in half, and dumping the people into the sea](./images/kraken.gif){.skinny}
  133.  
  134. Numbers came up as a topic today, you know, those primitive entities in JavaScript. Yeah, I was surprised as well.
  135.  
  136. ### The Problem
  137.  
  138. Whether this is truly a problem is debatable. Working in an almost exclusively node and TS/JS context these days, my code lives and dies based on what my runtime supports. Usually this is defined by [node and its implementation of the v8 engine](https://nodejs.dev/en/learn/the-v8-javascript-engine/). Today this means specifically its implementation of [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) and its [`parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) and [`stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) methods. I thought I knew about everything there was to know about JSON, short of [JSON 5](https://json5.org/) which sadly looks like [it won't be supported by node anytime soon](https://github.com/nodejs/node/issues/40714), but while unsurprised, I learned something today.
  139.  
  140. The short version is that numeric values in JSON strings need to be valid `float`s or `int`s, this shouldn't be a surprise to anyone experienced with JavaScript. Where the rubber meets the road is that in the process of `JSON.stringify`ing or `JSON.parse`ing, any trailing `.0` on a numeric value will be stripped. Of course it still is of the correlating value, and if decimal places are needed, you _could_ pass the value as a string, but that's silly. What's also silly is the inevitable kickback one could get when matching an existing system's output when migrating to a new one.
  141.  
  142. ### Why Is This a "Problem"?
  143.  
  144. As far as I can tell, the previously generated output was coming from a C based runtime that was generating the output with complete abandon for the ultimate JSON output itself. It succeeded in creating a numeric value that was parsable as either a `float` or `int`, making it technically correct, but mildly unhelpful and an annoyance for me, here _in the future_. JavaScript, JSON parse, and JSON stringify will each strip the `.0` specificity as its unnecessary for it as a numeric value, either as an `int` or `float`; it's just not needed and any need to _display_ the value differently would likely require using [`.toFixed`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed), because that's a function of display, not numeric value.
  145.  
  146. ![Futurama GIF showing a lawyer character stating "you are technically correct, the best kind of correcet"](./images/technically-correct.gif){.skinny}
  147.  
  148. ### An Example
  149.  
  150. https://codesandbox.io/embed/json-and-number-precision-wz8hg2?fontsize=14&hidenavigation=1&theme=dark
  151.  
  152. ### Summary
  153.  
  154. When in doubt, check what you know. We'll see whether I need to do some custom string processing or not; preferably the latter. Still, I've gone down [the custom JSON serialization path before](/java/custom-json-serilization/), but that was on another platform with different tooling and reasons. Until next time.
  155. ]]></content:encoded>
  156.        </item>
  157.        <item>
  158.            <title><![CDATA[Proxying APIs With Parcel v2!]]></title>
  159.            <link>https://edm00se.io/web/proxying-apis-with-parcel-v2/</link>
  160.            <guid>https://edm00se.io/web/proxying-apis-with-parcel-v2/</guid>
  161.            <pubDate>Thu, 05 Aug 2021 00:00:00 GMT</pubDate>
  162.            <description><![CDATA[parcel as a local dev server with back-end/api call proxying has been updated with the Parcel v2 Release Candidate]]></description>
  163.            <content:encoded><![CDATA[It's no secret I've been a fan of [Parcel](https://parceljs.org/) for some time. As a bundler, it's taken what's best about webpack and made it more friendly towards end users, uses more sane defaults, and allowed for some really powerful adaptations that would otherwise not be incredibly available. Previously I've blogged about the ease of setting up a local proxy to use with parcel, so that any back-end components can have their traffic accordingly directed. There's an update now with [the v2 release candidate available](https://v2.parceljs.org/blog/rc0/), so I figure it's worth talking about again.
  164.  
  165. ### Parcel v2 RC
  166.  
  167. The release candidate is exciting to me as it is the culmination of a couple years worth of alpha and beta releases. To their great credit, the parcel team did not rush the release candidate, which seems awfully tempting these days. If you're looking to get started or read up in their documentation, I recommend it. Just pointing your application's `package.json` towards a given target seems to be all the magic sauce required these days, and that's impressive with their many out of the box features and default support.
  168.  
  169. #### [v2.parceljs.org](https://v2.parceljs.org/)
  170.  
  171. ### Previously...
  172.  
  173. Previously, what was required to proxy api calls to say `/api` involved writing a thin node script implementation, making use of `http-proxy` and directing any traffic matching your api endpint's starting path, like `/api`, and forwarding the rest to parcel's local dev server. The parcel api made this all possible from a single script file, so to me it was a great win for power and simplicity. It also took slightly more effort up front, but was a relatively lean implementation of a development dependency. Ideally this is something that would live in a configuration and not an application script, which is exactly what the parcel team seemed to think.
  174.  
  175. note: if you're looking for an example of this, [I've previously blogged about this approach](/web/proxying-parcel/) and used it somewhat extensively
  176.  
  177. ### Doing It With v2
  178.  
  179. You can read [the documentation here](https://v2.parceljs.org/features/development/#api-proxy), as they have a page on just this topic, but the short version is to do two things:
  180.  
  181. 1. install a middleware package alongside your other dev dependencies
  182.  
  183. ```
  184. npm install --develop http-proxy-middleware
  185. ```
  186. 2. generate a `.proxyrc` configuration, like so:
  187.  
  188. ```
  189. #.proxyrc
  190. {
  191.  "/api": {
  192.    "target": "http://localhost:8000/",
  193.    "pathRewrite": {
  194.      "^/api": ""
  195.    }
  196.  }
  197. }
  198. ```
  199.  
  200. That's it, any requests with a path starting with `/api` now will be forwarded to `localhost:8000`. That's pretty simple and incredibly useful.
  201.  
  202. ### Jumping In Now
  203.  
  204. If you've been away from front-end tooling for a little while, the landscape has predictably changed again. If you're looking to get started, I'd recommend having a look at either [Parcel v2](https://v2.parceljs.org/) or [Vite](https://vitejs.dev/). These are the two big contenders I'm tracking that make use of some interesting and modern tooling that makes for some powerful and friendly developer experiences. I won't dive into those and their differences here, but they're worth the look.
  205.  
  206. Happy coding.
  207. ]]></content:encoded>
  208.        </item>
  209.        <item>
  210.            <title><![CDATA[Signs of an Advancing Web]]></title>
  211.            <link>https://edm00se.io/web/signs-of-the-web-advancing/</link>
  212.            <guid>https://edm00se.io/web/signs-of-the-web-advancing/</guid>
  213.            <pubDate>Wed, 31 Mar 2021 00:00:00 GMT</pubDate>
  214.            <description><![CDATA[a flash back to a bygone era, signs that the web is maturing, evolving, getting stronger, better]]></description>
  215.            <content:encoded><![CDATA[The web stack has been evolving and accelerating over the last few years. One of the things I don't miss is having moved on from supporting Internet Explorer. While some people are prone to comparing Safari to IE these days, a parallel in their non-evergreen deployment cycles, I would argue that's still not the case. Regardless, supporting IE was a _huge_ impediment to making use of new and available web stack features for some time.
  216.  
  217. ### Handling Query Parameters
  218.  
  219. The context here is from browser logic, in JavaScript; not server-side handling, which is usually managed through frameworks for defining server logic. I ran across the need recently for a one-off thing in which I needed to parse and grab a query parameter's value. This is easy to do now, and has been for a while provided you don't need to support IE.
  220.  
  221. #### A Blast From The Past
  222.  
  223. It used to be that without the ability to rely on the native web api, we could either turn to libraries (such as jquery or dojo) or write our own utility function to handle that need. There's nothing to stop a person from doing so today, it's just not as common over something as simple as url query param parsing.
  224.  
  225. Having had to walk this path before, it basically amounted to:
  226.  
  227. - get the string value of everything after the `?`, probably via something like `window.location.split('?')[1]`
  228. - parse the values into an object for referencing by key
  229.  - note: since this is in support of IE, things like es6 methods like `Array.prototype.map` are unavailable
  230. - access via a function to keep that silliness self-contained
  231.  
  232. Example:
  233.  
  234. https://gist.github.com/edm00se/abae980afeda1b2086d9581b36e24f99#getQueryParametersOldWay.js
  235.  
  236. This example is somewhat contrived but illustrates the approach. Ultimately, this thankfully feels like nails on a chalkboard after living with JavaScript after es6 (EcmaScript2015) for some time now. It reminds me that I hated writing es5 (or below!) compliant code and supporting Internet Explorer.
  237.  
  238. \*note: this is not how to do things in this day and age
  239.  
  240. #### A Much More Appropriate Way to Do Things
  241.  
  242. It's easy to make use of one of a couple implementations here. You can use the `URL` constructor directly on the output of `window.location` or use `URLSearchParams` with `window.location.search`. An example:
  243.  
  244. https://gist.github.com/edm00se/abae980afeda1b2086d9581b36e24f99#betterWay.js
  245.  
  246. Two lines to accomplish what was a separate function before, or one if you chain off the returned `URLSearchParams` instance and don't need to assign it to a temporary variable.
  247.  
  248. #### Why Couldn't I Use Them Before?
  249.  
  250. Ultimately, using [the `URL` constructor][url-constructor] and [URLSearchParams][url-searchparams] are something that isn't supported by IE. For those who have developed for an IE compatible back before modern polyfilling was widely available, this was a hard limitation in that you would just have to wait and hope that future releases would support it per the web standard.
  251.  
  252. ### Something to Learn Here?
  253.  
  254. Having partially relived my trauma from these past escapades, I was happy to realize that the reason I haven't had to worry about waiting and hoping for IE to support newer web api standards is that the industry has moved forward. There are some poor saps out there still supporting IE due to contractual reasons, but the ability to split modern vs legacy builds has only improved over time. Those legacy builds can be as crammed full of necessary polyfills as needed, and I doubt anyone will be spending too much time prioritizing IE/legacy build _optimization_ when there are better things to do. Also, if anyone's allowing IE users to access their web app without warning them _several times over_ in an age in which [Microsoft has moved on from Internet Explorer][ms-bye-ie], even the first implementation of Edge, that's a sad way to live.
  255.  
  256. [url-constructor]: https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#browser_compatibility
  257. [url-searchparams]: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#browser_compatibility
  258. [ms-bye-ie]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666
  259. ]]></content:encoded>
  260.        </item>
  261.        <item>
  262.            <title><![CDATA[Making Working From Home Work]]></title>
  263.            <link>https://edm00se.io/making-working-from-home-work/</link>
  264.            <guid>https://edm00se.io/making-working-from-home-work/</guid>
  265.            <pubDate>Wed, 18 Mar 2020 00:00:00 GMT</pubDate>
  266.            <description><![CDATA[making the best of a work from home situation, so it works for you]]></description>
  267.            <content:encoded><![CDATA[
  268. This is a cross-post from my [miscellanea blog](https://misc.edm00se.codes/making-working-from-home-work/).
  269.  
  270. ## First Thing's First
  271.  
  272. Happy Saint Patrick's Day! 🍀🇮🇪🍻☘️
  273.  
  274. It may not be a great time for going out, but it's my hope everyone finds some small way to celebrate and have a bit of fun, since lots of people's anxiety levels are probably elevated. In any case, have a good, fun time, and hopefully don't do anything that'll scare the pets, kids, or your spouse. 😆
  275.  
  276. ## Making Working From Home Work
  277.  
  278. I've been working from home in a remote capacity for nearly three years now. I've learned a few tricks to how to help things work for me, so I figured I'd share a couple observations and recommendations, in the hopes it aids someone else who is recently starting their working from home. I won't seek to dwell on the severity of COVID-19 and its spread, but I do hope you all stay safe, clean, and do what's needed to help out in this crazy time.
  279.  
  280. Working from home is a larger topic than many expect, especially amongst those whose first time it is. The complex part is that what works for me may not work for you, making all the articles or posts offering up sage advice may be entirely useless, at least to you or someone. That being said, I have a few suggestions of what to try out and, hopefully, aid you in your quest to make working from home a bit less of an ordeal.
  281.  
  282. ### Embrace Some Routine
  283.  
  284. I normally have a distaste for routine, not because it's unworthy of having one, but because it's uninteresting. Compared to a daily commute to and from an office or work site, a commute from home can be ridiculously small, for those who are lazy or incredibly late in waking up without an alarm. My absolute bare minimum is about 20 feet or so down the hall to my home office.
  285.  
  286. While that sounds great and efficient, as many of us "lazy" developers can strive to be, it also brings with it some warning bells. Laziness in the sense of a lazy (read efficient) developer can make for a great strive to pursue efficient solutions, but shorting yourself on the separation of work and home life means that while you can work from home, you'll never be free of thoughts about work. The separateion for me involves a dedicate space that when I go to it, is where I work. That I can go to a room and close the door and be "at work" is the best way I start my morning.
  287.  
  288. My morning coffee breaks involve me heading downstairs to my kitchen. It's easy to let the home life distract while not in a dedicated work space, so time boxing yourself is a great way of limiting that potentialtiy. For me, getting coffee shouldn't take more than about 5 minutes, especially if I ride a restroom break in around then. This again gives me that separation between active work time at my desk and the other things I'd have to get away from my desk for in a brick and mortar space.
  289.  
  290. #### Recommendations
  291.  
  292. - use a dedicated space
  293.  - unless you need to stretch legs
  294.  - get coffee
  295.  - take a long conference call outside, because that can make the difference in long-running meetings that don't require a second monitor
  296. - time-box your distractions
  297.  - give yourself the opportunity to leave your work cave
  298.  - keep distractions at bay
  299.  - hard rules versus soft rules are things you need to find for yourself, such as whether you can listen to a podcast or have a tv on in the background
  300. - protect that separation
  301.  - let any family members know your expectations while working
  302.  - help set up expectations for others so that they have their own dedicated time/space (this bleeds into the next section)
  303. - re-assess what works or doesn't for you
  304.  - not recognizing issues or successes is like operating blind
  305.  - don't beat yourself up too much, if you need to change something up, do it, and learn from the experience
  306.  
  307. ### As A Parent
  308.  
  309. Looking at things a month ago, my kid likes to come and check in on me after getting home close to my end of work day. Because I've set expectations, she knows to check if my door's open, if I'm on a call to check to see if she can talk to me first, and to let me inform her when I'll be done. These drop-ins are usually far faster than anything any of my former office coworkers would have done and, since we have expectations on both sides, far less distracting.
  310.  
  311. Living with a kid who's now staying at home from school, it's important to help cater to their needs, while you cater to your work needs. Kids have a normally quite structured environment, from their classes during the day, recess at regular intervals, even class structure even for my kindergartener involves a lot of routine. There's nothing wrong letting them have art for a while, painting or coloring, then letting them know to wrap things up and head outside to burn off some energy. This is all a part of their norm anyway, so have a rough plan, follow it as best as you can, and as with everything else, manage everyone's expectations.
  312.  
  313. As it happens, my kid's school sent home some study packets to help with figuring out what some of those efforts should be. My kid's class even had a video conference this morning for some face to face time, questions, along with a story time. This helped a lot with some of the re-establishment of routine, as even young kids know that things aren't normal right now. With some family self-quarantined and some at further distance, we're beginning to schedule some semi-regular video calls to help keep with a sense of connection and interaction beyond just with those of us at home.
  314.  
  315. #### Recommendations
  316.  
  317. - have a plan
  318. - plan on breaks, call it recess if you need to, and make sure they burn some of that energy
  319. - family walks are good, just manage expectations about social distancing with neighbors and any nearby playgrounds
  320.  - we got about an inch (25.4mm) of snow last night and the sandbox and swing set in our yard was still used today
  321.  - our patio furniture may be stored ahead of usual schedule, if nothing more than to provide more of an opportunity for outdoor time
  322.  - sidewalk chalk is a good choice with a clear ground and can help with big fun
  323. - don't stress it all and see what works for you and your kids, one size doesn't fit all, nor should it
  324.  
  325. ### Influx of Other Home/Remote Workers
  326.  
  327. This is tricky one for me at the moment, as my previously normal routine is now full of other people in the house during the day. My dog is about the perfect office coworker in that she almost never needs anything, except infrequently going outside during the day, and is always up for a lunch break walk. Now, we have a kid's daily schedule to manage and my wife is set up in our living room for her work. We've achieved a good rhythym now, but the first day was interesting.
  328.  
  329. The largest change for me beyond the physical presence of my family members has been that instead of making breakfast and lunch for just myself, while making our family dinner about half the time, now I'm helping make breakfast and lunch for three people. This takes a bit more forethought and preparation, so setting the alarm a little bit sooner can help out. We've found ourselves looking beyond mere breakfast cereal a bit more and I have no doubt we'll be getting crazy with some breakfast ideas before long. We've already done breakfast tacos once this week, so it's bound to improve.
  330.  
  331. The key for us was, as mentioned above, not stressing out over  the small things and to manage our expectations for each other and ourselves. We discussed with our daughter what we expected of her during our day and she's been great in adapting with us. My wife and I are taking turns during our meeting heavy times of the day to trade off on who's the parent on point for assisting my kid with anything from getting a snack to preparing some cardboard, newspaper, and water color paint, or the like. It's working out pretty well, and most people adapt over time, so the biggest take away in my opinion is to take it a step at a time and not sweat the small stuff.
  332.  
  333. #### Recommendations
  334.  
  335. - don't sweat the small stuff, seriously; we're all in this together, if remotely, and many of us won't have minor things like the whole family being home as an issue
  336. - plan out your meals in advance
  337.  - as much as you can, since many people are now making breakfast, lunch, and dinner for their whole family
  338.  - barring a true plan, have a rough idea of what meals to have, or what level of leftovers you're at
  339. - go through your pantry and/or your freezer
  340.  - chances are there's more there than you remember
  341.  - cycling through is always a good idea
  342. - bake some bread
  343.  - seriously, if you bake fresh bread, you'll be the hero of the day
  344.  - maybe make some pizza dough and do pizzas on a stone or on the grill
  345.  - the strange science of baking is pretty fun for kids to learn about too
  346.  
  347. ### Extra Referenes
  348.  
  349. Here are a few online things that can help aid you and your kids. A good number of items are on YouTube.
  350.  
  351. #### Kids
  352.  
  353. - [Cosmic Kids Yoga](https://www.youtube.com/user/CosmicKidsYoga), there's no magic here, it's yoga, but with theming and sometimes stories to help keep kids interested; it can help with stretching and burning a little energy during the day
  354. - [The Cinncinatti Zoo's Home Safari videos](https://www.youtube.com/watch?v=IEKhSO4WiX0&list=PLek4nkkPq41obS0YS2V140MJn0rQAYedf), while caretaking during the pandemic, the staff at the Cinncinatti Zoo are helping show and inform about some of their animals; so far we've seen Fiona the (less baby) hippo, Rico the prehensile tailed porcupine, and an ocelot named Sihil
  355. - [Yo-Yo Ma](https://www.instagram.com/yoyoma/), [#SongsOfComfort](https://www.instagram.com/explore/tags/songsofcomfort/), and lots of musicians are performing in a small stage/screen setting and helping provide a bit of a gap; it's pretty neat to see and I recommend checking out to see what your favorite performers are up to
  356.  
  357. #### Food
  358.  
  359. - [The Chef Show](https://www.netflix.com/title/81028317), on Netflix; this is right up there with GBBO as far as food television, in that it's focused on the subject matter, not drama. There's no secret here as to why Jon Favreau wanted to team back up with Chef Roy Choi to make more food, they have a great time, there's lots of great things to learn, and it's pretty wholesome to watch.
  360. - [Binging With Babish](https://www.youtube.com/channel/UCJHA_jMfCvEnv-3kRjTCQXw), the YouTube channel by Andrew Rea, has a "binging with..." episodes recreating food from popular culture in addition to the "basics with..." in which he dives into, well, basics. Interested in making pizza at home? Recently there was an episode on [neopolitan style pizza](https://www.bingingwithbabish.com/recipes/bunnicorn-pizza) (just use a more terrestrial ingredient), or check out how to go about making fresh [sourdough from scratch](https://basicswithbabish.co/basicsepisodes/sourdough-bread).
  361. - [BA Test Kitchen](https://www.youtube.com/channel/UCbpMy0Fg74eXXkvxJrtEn3w) does a bunch of neat videos, from gourmet makes to "it's alive" and beyond; just be forewarned a bit of expletives fly, although most of them are bleeped from my watching
  362. - [4 Levels of Burritos](https://www.youtube.com/watch?v=BgTsKiZODZU), Epicurious has a bunch of those "4 Levels of..." videos, but this burrito video is the gold standard in my opinion.
  363.  
  364. ## Summary
  365.  
  366. There's so much more, like using [a Pomodoro timer](https://splode.github.io/pomotroid/) to keep yourself on task with structured breaks for yourself (which I recommend for software development anyway, as available), getting a good desk chair, taking a walk and stretching regularly, and making sure to do social things with your family now that you see each other even more. [I highly recommend a nice board game](https://splode.github.io/pomotroid/). This is a bit of an endless topic, so feel free to drop some comments and/or share what works for you. Ultimately, stay safe, wash, and look out for each other out there.
  367. ]]></content:encoded>
  368.        </item>
  369.        <item>
  370.            <title><![CDATA[Side Benefits of Static Typing]]></title>
  371.            <link>https://edm00se.io/web/side-benefits-of-static-typing/</link>
  372.            <guid>https://edm00se.io/web/side-benefits-of-static-typing/</guid>
  373.            <pubDate>Mon, 11 Nov 2019 00:00:00 GMT</pubDate>
  374.            <description><![CDATA[static typing your JavaScript carries more than the obvious]]></description>
  375.            <content:encoded><![CDATA[
  376. Regardless of your being for or against statically typing and transpiling your JavaScript code, there are advantages to doing so. No system is absolute and yes, it does transpile into JavaScript proper. That said, there can be a great number of conveniences brought in by typing your JS source code. The most obvious major reason is that, properly defined, your models, API use, and function input and output will be consistently to an expected format. This addresses one of the largest complaints about JavaScript as a language, that everything is so [loosey-goosey](https://m-w.com/dictionary/loosey-goosey).
  377.  
  378. ![the (un)titled goose](./images/untitledGoose.png){.skinny}
  379. *The titular antihero and miscreant of the [Untitled Goose Game](https://goose.game/)*
  380.  
  381. Beyond this primary advantage, there are a good number of side benefits to using a static typed system in front of your JavaScript code base.
  382.  
  383. ### Core and Adjacent Principles
  384.  
  385. Adding static typing to your JavaScript, such as with TypeScript, brings with it a couple of concepts by the sheer nature of the fact that there's a build phase of the source code to create the ultimately consumed JavaScript. This would happen the same if we used a build tool and [babel](https://babeljs.io/) to process JavaScript, so while it's not entirely unique to add to the process around JS, it carries with it some benefits as well.
  386.  
  387. #### Pre-Processing
  388.  
  389. By merely having a build phase in the life cycle of an application, we can do a number of things to ease our concerns. We can create config files for different environments, allowing for the different setups or variable inclusions based on environment. This can be done a number of different ways, such as keeping different `.env` files containing their respective values or having a `config.js` file which resolves an object with its respective variables through a single object interface. Whichever route you take, having this as a pluggable constant in your application helps take the guesswork out of things.
  390.  
  391. We can also abbreviate or mock different integrations or even down to the function level, for the purposes of testing. A testing environment is after all just another environment. This makes scaffolding a bit more self-explanatory as well, thankfully. Most test runners, [such as jest, flag the node environment variable (a la `process.env.NODE_ENV`) to `test` during its run](https://jestjs.io/docs/en/24.0/getting-started.html#using-babel), so setting your testing configuration as _just another environment_ is not only well supported, but desirable.
  392.  
  393. #### Compilation / Transpilation
  394.  
  395. The names of things matter, but when it comes to something processing what is or will be JavaScript I have given up on caring _too much_. There's a difference, I just don't care to get into it here. TypeScript relies on their compiler `tsc` to compile source into browser consumable JavaScript, so with TS feel free to use the word "compile". If you happen to use the term 'transpile', since that's what happens with babel and other JavaScript tooling, I will treat you as having said the correct thing, even though there may be some difference in the nuance of what's going on under the hood. It's sort of like "Merry Christmas", "happy holidays", "happy Kwanzaa", and more. Most people celebrate _something_ around the end of the year, prior to the new year, and if we all want to live healthier and happier lives, we won't take offense to whatever it is someone wishes us. For bonus points, turn it into a game and see how many varied phrases you can "collect" receipt of by others to tally up after the completion of the holiday rush. No, the points don't actually matter, like Who's Line.
  396.  
  397. #### The Side Benefits
  398.  
  399. Some of the key side benefits to static typing in your work flow seem to come in a couple of flavors.
  400.  
  401. ##### Better Editor / Tooling
  402.  
  403. VS code is my primary editor for front-end work. One of the features it brings, by default, is what it calls [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). As far as I'm concerned, it's implemented nearly seamlessly and is a great feature for editing with JavaScript or TypeScript. It offers a high degree of awareness regarding code, be it your source or imported from `node_module`s, as well as a type-ahead completion suggestions that are pretty solid. With static types in use, this also means that there is a far more intelligent display of what is expected. As someone who has done a large amount of Java work previously, this fills the gap of some of what I felt was missing when focusing more on front-end work, without Eclipse / IntelliJ IDEA.
  404.  
  405. Additionally, this greater and true understanding of imported code means that things like tree shaking and code splitting are far more powerful, as knowing explicitly, via named imports/exports, whether a function is needed in the built output is now legitimately definitive. This capability has obvious advantages regarding breaking apart modern front-end heavy builds and pushing an increasingly aware amount of what's needed "down the pipes" to the user based on things like which page they're on or whether or not they're even logged into a valid session.
  406.  
  407. ### Summary
  408.  
  409. Over the last year, I've been increasing my use of TypeScript in my day-to-day work and have considered incorporating it more into my open source and dabbling projects. The best part of it all is that having created [my own preferred flavor of a starter repo](https://github.com/edm00se/vue-parcel-starter) makes this easy to test in a branch and the use of my preferred build tool, [Parcel](https://parceljs.org/), makes this [extremely easy to setup and try out](https://github.com/edm00se/vue-parcel-starter/pull/61/files#diff-60b2029a3ac639f9ff664d542400700aR10).
  410.  
  411. So, do you _need_ to use TypeScript or another static typing solution in your JavaScript? No, you can write perfectly valid JS without it. What I do ask myself has to do far more with whether I want to skip out on some of what TS can give me, without any additional effort. I'm sure you'll find your own answer, but hopefully I've inspired a little extra consideration.
  412.  
  413. ### Bonus
  414.  
  415. As mentioned above, adding TS to my starter repo was easy, almost too easy. When tooling does what it should, it can lead us to great places. Here's how easy it was.
  416.  
  417. ![live parcel dev server detects changes, installs typescript dep, new build is good](./images/addingTypeScript.gif)
  418. *adding typescript to vue components with parcel, stupid easy*
  419. ]]></content:encoded>
  420.        </item>
  421.        <item>
  422.            <title><![CDATA[Maintaining Hacks]]></title>
  423.            <link>https://edm00se.io/web/maintaining-hacks/</link>
  424.            <guid>https://edm00se.io/web/maintaining-hacks/</guid>
  425.            <pubDate>Thu, 22 Aug 2019 00:00:00 GMT</pubDate>
  426.            <description><![CDATA[an exercise in keeping sanity through archaic needs]]></description>
  427.            <content:encoded><![CDATA[
  428. Writing a hacky bit of software to "make something work" is rarely anything a developer wishes to do. They're ~~crimes~~ acts of necessity, born of the moment. True innovation exists within the **need** to achieve a task, but sometimes we merely do what we need to do to get the job done. This post exemplifies one such effort on my part, along with my further efforts to keep the madness at bay.
  429.  
  430. ### In Case You've Been Living Under A Rock
  431.  
  432. [AMP][amp-url] (Accelerated Mobile Pages), for those of you who have blissfully avoided it, sometimes gets a bad reputation. Some of the primary criticisms against AMP include:
  433.  
  434. - it ignores and bypasses most web conventions
  435.  - requiring a large-ish amount of JavaScript files from Google
  436.  - the scripts to load are hosted purely by Google
  437.  - the scripts can change at any time and are un-versioned, are maintained by Google
  438. - effectively creates vendor lock-in for the format
  439. - has that bar over the top of the screen, when viewed on mobile, which cannot be removed
  440.  - there was a bit of an uproar a little while back as something went wrong and the link wasn't clickable and it couldn't be removed or fixed by anyone else
  441.  
  442. Just to be clear, it isn't all doom and gloom. There's a reason I tried AMP out on this very blog in the first place. What it achieves is somewhat impressive, even if it bumps most web convention norms. For a content-focused site, it succeeds in keeping the focus on the content. This includes in limiting what ads can do and how they're displayed. Gone are the multiple pop ups asking to put you on a mailing list or auto playing videos, for the most part. It also limits how much JavaScript can be loaded, to be a valid AMP site; just that which loads the site, none of which is allowed for AMP compliant pages. [This blog's search page does not validate][search-pg-validation] per the AMP spec. While this is annoying for web app developers, it's great for content as a consumer, because there's less for developers to fiddle around with.
  443.  
  444. ### The Hacks In Question
  445.  
  446. The involved hacks are ultimately mechanisms for displaying content that requires JavaScript to load. In both cases, from a third party source. Specifically this is relating to both GitHub gist embeds and Disqus comments. The part that made these become hacks is that AMP's requirements for JS are that it can be loaded, but not from the host site. This leads us to the route of embedding content via an `iframe`; specifically AMP's [`amp-iframe`][amp-iframe-docs] implementation, as they use unique namespaces for components.
  447.  
  448. In my case, I wrote a pair of HTML pages that contained some JavaScript with some logic to take passed URL query parameters to determine what to load in the generated `iframe` for the destination
  449.  
  450. note: The gist workaround is no longer needed, as AMP eventually added this as a native component some time after I did my work around. Also of note, the disqus solution I came to loads their JS, which loads another nested `iframe`; if you parse their query parameter logic you could possibly do without a middleman, although this was not clear when I began as it was a new technique with little documentation at the time.
  451.  
  452. ### Keeping The Madness
  453.  
  454. At first, I just picked a domain name I already owned and controlled and knew I could park a pair of HTML files into without impacting the site. In the case of Disqus, I had to allow the new domain as authorized to interact with my connected account. While this did the job, it lacked in the sense of holding to its purpose and was moving with an unrelated codebase. Preparing to move off that domain meant I would need to deal with things properly. My solution involved:
  455.  
  456. - moving the hack pages to a new home
  457.  - in my case I wound up using a GitHub repository with the pages deployed via GitHub Pages
  458.  - this is free, worked with my existing custom domain name for my root GitHub Pages (user site), and meant it was merely `<my domain>/amp-hacks`
  459. - updating existing references
  460.  - updating my AMP site (blog) to point to the new destination (a simple replace of domain/path from the old one to the new one)
  461.  - updating Disqus as to the authorized domain access
  462. - testing it out
  463.  - this went strangely without a hiccup, hooray! 🎉
  464.  
  465. Where is it now? If you're interested, you can check out my repository, [`github.com/edm00se/amp-hacks`][gh-amp-hacks-repo]. Feel free to check it out, see what it does, or fork it and make it your own; or stare in horror at the workaround needed. For reference the repository is new, but the pages date back to about 3 years ago in a different repo I've since archived; I finally stuffed my hacks into their own box.
  466.  
  467. That's it, the big reveal. Have hacks that are needed to make things work? Throw them in their own box and keep them, documented and available, but isolated. One day they can be removed and the world will be a little bit brighter for it.
  468.  
  469. <!--
  470. [![GitHub stars](https://img.shields.io/github/stars/edm00se/amp-hacks?style=social){.skinny}](https://github.com/edm00se/amp-hacks) [![GitHub forks](https://img.shields.io/github/forks/edm00se/amp-hacks?label=Fork&style=social){.skinny}](https://github.com/edm00se/amp-hacks)
  471. -->
  472.  
  473. ### A Leading Question
  474.  
  475. When your "normal" development practice _requires_ use of hacks, as the `amp-iframe` has been required/used by many to solve issues since AMP's creation, what does that mean for desirability of development for that platform? If a normal requirement, such as a site specific search page which isn't just a form to submit a Google site search, is just unattainable, then it's not a viable development platform. This is where I've arrived, which is to regard AMP as a great way of content consumption, but not as a platform to develop for. I could continue to live with my blog in its current format, it works, and does so pretty well. I'm just moving on.
  476.  
  477. ### What's To Come
  478.  
  479. I've dabbled over the last year, give or take, in:
  480.  
  481. - [vuepress][vuepress], because I love vue.js and its documentation is pretty hot
  482. - [gatsby][gatsby], because the web dev scene has gone gatsby crazy
  483. - [gridsome][gridsome], because I love vue.js and it's the most gatsby-like analogue with vue
  484. - [eleventy][eleventy], because while the world doesn't need yet another static site generator, it's aim is close to jekyll, all while being an unobtrusive build system
  485. - [svelte][svelte], because I love JS, but also love extreme simplicity; this isn't an SSG, but rather an application framework with a focus on low code, reactivity, and no virtual DOM
  486.  
  487. I'm not sure where I'm going with this blog, but considering my obsession over development tooling and the frequency with which I overhaul my blog as an excuse to try out something new, I can't help but imagine it's only a matter of some time before it happens. It's already been about [three years in this format][post-reincarnated], so it's probably time to reincarnate it again.
  488.  
  489. <!-- <iframe class="tweetbu" src="https://tweets.edm00se.codes/1164279630326636549/">failed to load</iframe> -->
  490.  
  491. ### Summary
  492.  
  493. All in all, AMP has its place, but it's not for me. I don't think it's evil, it just prevents my usual playing with things, so as a developer and not a content manager, it's time for me to move on. Also, this post isn't about the specifics of my solutions; merely about what insanity it was and the improved way I eventually settled on handling the hackery. Initially, it was just an extra couple of pages parked in a deployed domain I owned and controlled, but finally I was able to park them in a dedicate space and will serve its purpose until it's no longer needed.
  494.  
  495. [amp-url]: https://amp.dev/
  496. [search-pg-validation]: https://validator.ampproject.org/#url=https%3A%2F%2Fedm00se.io%2Fsearch%2F
  497. [amp-iframe-docs]: https://amp.dev/documentation/components/amp-iframe/
  498. [gh-amp-hacks-repo]: https://github.com/edm00se/amp-hacks
  499. [vuepress]: https://vuepress.vuejs.org/
  500. [svelte]: https://svelte.dev/
  501. [gatsby]: https://www.gatsbyjs.org/
  502. [gridsome]: https://gridsome.org/
  503. [eleventy]: https://www.11ty.io/
  504. [post-reincarnated]: https://edm00se.io/admin/reincarnation/
  505. ]]></content:encoded>
  506.        </item>
  507.        <item>
  508.            <title><![CDATA[A Parcel Plugin For Goodies]]></title>
  509.            <link>https://edm00se.io/web/parcel-plugin-goodie-bag/</link>
  510.            <guid>https://edm00se.io/web/parcel-plugin-goodie-bag/</guid>
  511.            <pubDate>Thu, 14 Mar 2019 00:00:00 GMT</pubDate>
  512.            <description><![CDATA[making IE(11) support just a bit easier]]></description>
  513.            <content:encoded><![CDATA[
  514. # Happy π Day!
  515.  
  516. ### Intro
  517.  
  518. Parcel is a **great** bundling tool and one I've not been too quiet about espousing my preference for it. Recently, I hit a figurative "hoop" I needed to jump through to ensure I could support a contractually required browser, our good [old frenemy Internet Explorer (11)][frenemy-ie]. This is sadly something I couldn't influence, all the usual horrible excuses are at play, yet here I am having to do the dirty work.
  519.  
  520. TL;DR: IE11 didn't work out of the box, as using [the html loader][parcel-html-loader-example] feature of Parcel ensures multiple "bundles", which means the loader relies on both `Promise` and `fetch` APIs before any polyfill can be enacted; skip down to the [solution](#solution) for more.
  521.  
  522. ### Background
  523.  
  524. For a while, I've had [a demo application][app-of-ice-and-fire] that was meant to be a non-trivial AngularJS applicaiton to illustrate how to hook it up to a back-end. When I first created it, it was a testament to the ability to create a modern and relatively web standards driven front-end portion of an application, deployed in an IBM Domino NSF application container, with its corresponding Java backing logic. I used it as an example for [a conference talk I gave][blue-chalky-soup]. It then evolved to be an example application to illustrate [use of an Nginx reverse proxy equipped with PageSpeed][nginx-pagespeed] to ensure an optimized delivery of web assets.
  525.  
  526. Time passed, the web generally moved on from AngularJS, and now, finally, it has been re-incarnated with a mocked back-end, as an example for use in [modernizing an aging AngularJS applicaiton with Parcel][modernize-ng1]. In fact, it has worked so well in this capacity, it has turned into a proving ground for what's required with that process for bringing a larger application from my day job forward to allow for modernized JS development on an existing application.
  527.  
  528. ### Problem
  529.  
  530. Since the approach to modernizing an AngularJS app substitutes the `templateUrl` and `ng-include` inclusion of HTML partial files that haven't been copied to the dist (build) version of the app, since they haven't been required/imported, swapping them with a `require` or `import` statement was necessary. Parcel handles HTML imports rather well, but it carries a side effect of ensuring multiple "bundles" are generated. This means that both the `Promise` and `fetch` APIs are used directly by Parcel's loadng script in the destination browser, APIs that IE11 doesn't have. Also, while `@babel/polyfill` _can_ polyfill `Promise`, it doesn't fire the polyfills before Parcel uses the API, it can't/doesn't polyfill `fetch`.
  531.  
  532. The bottom line is that "out of the box", making use of the HTML import capabilities of Parcel means IE11 will not be supported. **In every other way** Parcel supports IE with a combination of Babel's polyfill and a defined browserslist config in `package.json`; quite well in fact. So, what's a motivated developer to do?
  533.  
  534. ### Solution
  535.  
  536. Enter my (first) [Parcel plugin, "goodie bag"][pp-goodie-bag].
  537.  
  538. #### parcel-plugin-goodie-bag
  539.  
  540. A polyfill for `Promise` and `fetch` to keep Parcel working for those without it.
  541.  
  542. [Looking at you Internet Explorer](https://techcommunity.microsoft.com/t5/Windows-IT-Pro-Blog/The-perils-of-using-Internet-Explorer-as-your-default-browser/ba-p/331732).
  543.  
  544. #### Usage
  545.  
  546. To install and use this plugin, assuming you have Parcel set up already, you need merely install the plugin. That's it, just a quick `npm install` and your troubles are solved.
  547.  
  548. ```sh
  549. npm install --save-dev parcel-plugin-goodie-bag
  550. ```
  551.  
  552. #### How It Works
  553.  
  554. What's happening under the hood is:
  555.  
  556. - a bundled version of a polyfill for both `Promise` and `fetch` APIs is assembled
  557.   - using the [es6 `Promise` polyfill][poly-es6]
  558.   - also the [unfetch `fetch` polyfill][poly-unfetch]
  559. - copies this combination polyfill to the destination directory
  560. - it listens for a root `index.html` bundling event from Parcel
  561. - it injects the generated polyfill file at the beginning of the `head` tag in that root `index.html` file (so as to load before Parcel's loader)
  562.  
  563. All in all, it makes for a pretty quick and seamless addition, allowing for a bit less painful support of IE11.
  564.  
  565. ### Summary
  566.  
  567. This was painful for me. [Not even Microsoft wants to support IE anymore][ms-says-no-to-ie], yet so many cling to the notion that beyond all reason it _must_ be supported. In the end, we have a work around, but the application would run so much better without that bloated overhead. In the future, I may pursue a split bundle, one which has modern support and the other which is the "legacy" support, some demos of which in Parcel I've seen already. This holds great appeal, IMO, as it can let the more web standards compliant browsers actually hit their stride.
  568.  
  569. [frenemy-ie]: https://edm00se.io/web/evergreen-web/
  570. [parcel-html-loader-example]: https://github.com/edm00se/modernize-ng1/blob/master/docs/Migrate.md#updating-the-router
  571. [app-of-ice-and-fire]: https://github.com/edm00se/AnAppOfIceAndFire
  572. [blue-chalky-soup]: https://github.com/edm00se/BlueChalkySoup
  573. [nginx-pagespeed]: https://github.com/edm00se/AD113-Speed-Up-Your-Apps-with-Nginx-and-PageSpeed
  574. [modernize-ng1]: https://github.com/edm00se/modernize-ng1
  575. [pp-goodie-bag]: https://github.com/edm00se/parcel-plugin-goodie-bag
  576. [poly-es6]: https://npm.im/es6-promise
  577. [poly-unfetch]: https://npm.im/unfetch
  578. [ms-says-no-to-ie]: https://techcommunity.microsoft.com/t5/Windows-IT-Pro-Blog/The-perils-of-using-Internet-Explorer-as-your-default-browser/ba-p/331732
  579. ]]></content:encoded>
  580.        </item>
  581.        <item>
  582.            <title><![CDATA[Proxying Parcel]]></title>
  583.            <link>https://edm00se.io/web/proxying-parcel/</link>
  584.            <guid>https://edm00se.io/web/proxying-parcel/</guid>
  585.            <pubDate>Mon, 14 Jan 2019 00:00:00 GMT</pubDate>
  586.            <description><![CDATA[super-powered front-end development]]></description>
  587.            <content:encoded><![CDATA[
  588. ### Intro
  589.  
  590. Parcel describes itself as a "blazing fast, zero configuration web application bundler". I agree with that statement. It's been my targeted development server and front-end bundler for a while now. I've even gone so far as to [create a starter][vue-parcel-starter], just to contain what little config I need to get my preferred tool set to work just right on top of it.
  591.  
  592. ### What Parcel Brings 📦 ⚡️
  593.  
  594. Parcel is a great bundler, which handles things like optimization from uglification to transpilation, code splitting, HMR (w/ ES6 style `import`s), intelligent logging, speedy builds (through parallel workers and caching of code structure), and more. It does this all with zero configuration required out of the box. In fact, when I first tried Parcel, it set such a high bar that even the great strides done in webpack 4, to lower the amount of required config, don't quite match up to my satisfaction level with Parcel. It's why my mug can be found on the [Parcel Open Collective][parcel-open-collective] page in the donor section; a few bucks a month can help an open source project a fair amount.
  595.  
  596. From the developer perspective, it's virtually flawless. Many framework CLI tools set up a well filled out webpack config, and that's great. The only problem is that if your application requires any different asset types or for manual fiddling into some of the specifics. My experience with parcel has involved absolutely no configuration of asset types (css, html, js, framework, ts; the last one requires a ts config, but always does), and I've been better off for it.
  597.  
  598. ### Why Bring a Proxy to the Parcel Party 🥳
  599.  
  600. If you're working against a back-end, say for a monolithic application, especially one that isn't driven by [express][express], you'll probably want to direct your traffic accordingly while developing locally.
  601.  
  602. #### Why Not Express
  603.  
  604. Express is pretty nifty and I've used it a few times. The bottom line about why not to jump through this hoop for express is that the parcel development server and bundler [can be used directly as express middleware][parcel-server-as-express-middleware]. Yeah, it's that easy.
  605.  
  606. As for why I should load express as a development dependency, that wouldn't be terrible, but it seems a bit of a waste of disk space just for extending what's already happening. I really just needed a proxying handler based on route. I wound up settling on [`http-proxy`][npm-http-proxy] and direct used of the Node built-in `http` module.
  607.  
  608. [Bundlephobia.com][bundlephobia] reports the following sizes for these two dependencies:
  609.  
  610. | Package                                  | Size      |
  611. |------------------------------------------|-----------|
  612. | [express(4.16.4)][bundle-express]        | 386.2 kB  |
  613. | [http-proxy(1.17.0)][bundle-http-proxy]  | 20.7 kB   |
  614.  
  615. As you can see, as a dev dependency, this should be under a tenth of the size. As for why to use `http-proxy`? It's what powers `http-proxy-middleware`, which is meant to plug into a wide variety of other server libraries. It also means that it has a fairly universal API.
  616.  
  617. ### How to Set Up the Proxy
  618.  
  619. Conceptually, we'll be following a pretty standard format of switching off of a detected route in the request path, then forwarding to the appropriate development server based on port. The reason I'm detecting request path as the determining factor is that most intelligent application design includes putting a RESTful API, or similar, pattern of placing the endpoints under a `/api/` path. This makes it easily detected via a regex test of the request path.
  620.  
  621. ### Enough Talk, Show Me The Code
  622.  
  623. It's lightly annotated and reads pretty easily.
  624.  
  625. https://gist.github.com/edm00se/5162791576ed6c36b58f71d65646d64b#dev-server.js
  626.  
  627. #### Of Note
  628.  
  629. From the parcel side, you can see I'm setting a couple options, to be passed into the `Bundler` call per [Parcel's Bundler API][parcel-bundler-api]. I'm explicitly setting `publicUrl: '.'`, so that no absolute path to the files will be used; this should be used if your app will be in a sub-directory, not at the root of the domain. Both `watch` and `sourceMaps` default to `true`, but they're there to remind me.
  630.  
  631. Additionally, you can see from the option I'm passing the parcel server's port, I'm setting `ws` to `true`, to make sure the web socket used from Parcel's dev server works.
  632.  
  633. As for the back-end server, I'm setting `prependPath` to `true` (default, still trying to be explicit) which will prepend the target's path to the proxy path.
  634.  
  635. ### Wrapping It Up
  636.  
  637. To make this more developer friendly, just throw it into its own file, such as `dev-server.js` and you can add it as an [npm script][npm-run-scripts] for ultimate simplicity. Provided your back-end server is started, then starting parcel's dev server with the proxy in front of it is as simple as adding to your `package.json`'s `scripts` block, like so:
  638.  
  639. ```json
  640. "scripts": {
  641.  "dev": "node dev-server.js",
  642. ```
  643.  
  644. ### Summary
  645.  
  646. Ultimately, I'm glad I went down this route, as the implementation is simple, is a file I can own and control with my monolithic application, and makes good use of other (supported) code without adding too much to the project's bloat. With any luck, this can inspire you to think about how you load your own development environment(s) and possibly improve your own development expierience.
  647.  
  648. [parcel-bundler]: https://parceljs.org/
  649. [vue-parcel-starter]: https://github.com/edm00se/vue-parcel-starter
  650. [parcel-open-collective]: https://opencollective.com/parcel
  651. [parcel-examples]: https://github.com/parcel-bundler/examples
  652. [express]: https://expressjs.com/
  653. [parcel-server-as-express-middleware]: https://github.com/parcel-bundler/parcel/issues/55#issuecomment-349755034
  654. [npm-http-proxy]: https://www.npmjs.com/package/http-proxy
  655. [bundlephobia]: https://bundlephobia.com/
  656. [bundle-express]: https://bundlephobia.com/result?p=express@4.16.4
  657. [bundle-http-proxy]: https://bundlephobia.com/result?p=http-proxy@1.17.0
  658. [parcel-bundler-api]: https://parceljs.org/api.html
  659. [npm-run-scripts]: https://docs.npmjs.com/cli/run-script
  660. ]]></content:encoded>
  661.        </item>
  662.        <item>
  663.            <title><![CDATA[Workbox]]></title>
  664.            <link>https://edm00se.io/web/workbox/</link>
  665.            <guid>https://edm00se.io/web/workbox/</guid>
  666.            <pubDate>Thu, 08 Nov 2018 00:00:00 GMT</pubDate>
  667.            <description><![CDATA[a cli tool, configuration, and plenty of options for service worker generation]]></description>
  668.            <content:encoded><![CDATA[
  669. ## Preamble
  670.  
  671. Pardon the interruption in the Alexa Skill and serverless series, but a couple of things happened. First, I started using the next version of the Alexa SDK for node.js. Second, I saw an excellent Alexa Skill Developer promotion that I had to write a new skill for, so that took up my free time in October, other than working on my kid's Halloween costume; I'll be updating my next post in the series to reflect the new API. Lastly, I've started diving into improved configurations of [service workers][sw-url], which has led me to be a firm proponent of [workbox][workbox-url].
  672.  
  673. <iframe class="tweetbu" src="https://tweets.edm00se.codes/1058452133492781056/">failed to load</iframe>
  674.  
  675. ### Service Workers
  676.  
  677. A service worker is a segregated bit of JavaScript, executed in a separate context from the main window (in the "background"), and registers against a given origin and path. It's a sort of event driven worker, but if you're looking for more information on what they are and documentation, I recommend:
  678.  
  679. - [the "is service worker ready" page][is-sw-ready]
  680. - [MDN's page on the Service Worker API][sw-url]
  681. - [the "Can I Use" page for serice workers][can-i-use-sw]; spoiler alert, yes you can
  682.  
  683. #### Why Use One
  684.  
  685. The service worker API relies on a few things, such as the `fetch` API and `Promise` API. That being said, what it means at a functional level is that the service worker, once registered, can listen in on and, potentially, intercept failing network requests. As I'm sure you can imagine, this can be immensely powerful, once they're configured, registered, and working correctly.
  686.  
  687. #### Other Requirements
  688.  
  689. Service workers require being loaded/registered over HTTPS. For local testing, you can either implement a development server that supports HTTPS and, as is preferable, a certificate configured for your localhost environment.
  690.  
  691. Since it executes outside the context of the main page, it _must_ be within a separate file.
  692.  
  693. #### Support
  694.  
  695. Ever since iOS 11.3, service workers are supported with some rather minor caveats. Android Chrome has been fully ready for a while. On the desktop, Firefox and Chrome have been playing ball for a while and MS Edge has supported for a little while. This means that [unless you're locked into an abusive relationship with yet another version of IE (11)](/web/evergreen-web/), we can play with the cool toys.
  696.  
  697. #### Registering
  698.  
  699. Feature detection, and environment detection, can be pretty easily done, especially when you're building your source application files. I have a preference of late to [parcel][parcel-url], which allows for making use of the pretty common node.js convention of checking whether the `NODE_ENV` environment variable is set to `production`, this means that, once it is set up, I can avoid loading it in my local environment, for simplicity. All that's left is to create the service worker itself, here referenced as `sw.js`.
  700.  
  701. https://gist.github.com/edm00se/c4defbf1b29ec3f6e10cdb7fc1afcbec#sw-registration.js
  702.  
  703. \*note: since I'm using parcel, it's intelligent enough to try and find all my referenced js files, such as `sw.js`. As I'm not creating the "real" version of this file until the production build is created, and parcel isn't checking the if condition of `'serviceWorker' in navigator`, that means I've created a stubbed, but otherwise emtpy, file called `sw.js` in the source.
  704.  
  705. ### Enter Workbox
  706.  
  707. Workbox describes itself as "...a library that bakes in a set of best practices and removes the boilerplate every developer writes when working with service workers." Workbox describes itself as being the successor to both sw-precache and sw-toolbox.
  708.  
  709. My experience has been pretty close to eliminating the need for manual creation of a service worker, and it has been easier than my experience with either above mentioned library. This is likely why I've enjoyed it so much, I don't need to do the manual work while keeping any specifics to a simple configuration.
  710.  
  711. #### Flavors
  712.  
  713. There are a few flavors of workbox which you can use. These generally amount to a node module, a webpack plugin, and a cli tool. Each is relatively easy to get started with, but I'm going to focus on using the CLI tool.
  714.  
  715. #### Install and Configuration
  716.  
  717. I've found the [workbox-cli][workbox-cli-npm] tool works rather well in an agnostic fashion when used with parcel. There do appear to be a couple parcel plugins which aim to achieve the same, but one is for sw-precache, and the one I found on GitHub targeting workbox [isn't even published to npm][parcel-plugin-workbox-issue]. So here I am, thankfully it's an easy path to follow.
  718.  
  719. 1. `npm install --save-dev workbox-cli`, so simple, just about any dev should be able to do it
  720. 2. run the wizard, which will prompt you with questions to assist in creating your configuration file
  721.  - if you've installed workblox globally, you can call it with the name workbox, `workbox wizard`
  722.  - if you've installed it as a development dependency, as I outline in step 1, you can access it in the `.bin` directory inside of `node_modules`, `./node_modules/.bin/workbox wizard`
  723.  - that script by name will work in an npm script, which includes the nested `.bin` directory in its PATH
  724. 3. add the build of the service worker to your build pipeline, such as npm scripts
  725.  
  726. #### Creating the Configuration File
  727.  
  728. The wizard will ask you things like "where is your build app deployed from?" I'm building an application to the `dist/` directory in my project repo. This is the directory I have defined. Then, having scanned the contents of my `dist/` directory, it prompts me on what to cache, by file type (extension). This is an interactive list, so you can de-select as the case may be, but that's probably unlikely. Lastly it will ask you what you want your file to be named. For consistency with [the script I have to register it](#registering), it will be `sw.js`. Here's my config file:
  729.  
  730. https://gist.github.com/edm00se/c4defbf1b29ec3f6e10cdb7fc1afcbec#workbox-config.js
  731.  
  732. #### Updating The Build Process
  733.  
  734. I have been using an npm script `build` to perform my build of a production version of the app with `npm run build`. This is preserved and only added to, as I then created a `build:sw` script in my `package.json`'s `scripts` block. Now, in my deploy config, it calls the unit tests, build, and build of the service worker as such:
  735.  
  736. ```yml
  737. - npm test
  738. - npm run build
  739. - npm run build:sw
  740. ```
  741.  
  742. #### The Service Worker Build Script
  743.  
  744. You can use one of a couple options in generating the service worker. The CLI tool outlines the following as actions the CLI tool can take:
  745.  
  746. - `wizard`: a step-by-step guide to set up Workbox for your project
  747. - `generateSW`: generates a complete service worker for you
  748. - `injectManifest`: injects the assets to precache into your project
  749. - `copyLibraries`: copy the Workbox libraries into a directory
  750.  
  751. ##### `wizard`
  752.  
  753. I've already discussed what the `wizard` task will prompt for setting up a configuration file.
  754.  
  755. ##### `generateSW`
  756.  
  757. If you have basic pre-caching needs without any advanced configuration, you'll want to use `generateSW`, as it's the simplest. The example project I mention below uses this approach, and the invocation is `workbox generateSW <path-to>/workbox-config.js`. It uses its `precacheAndRoute` implementation out of the box.
  758.  
  759. ##### `injectManifest`
  760.  
  761. This is perhaps the most interesting one, as it's certainly powerful. When your needs exceed simple precaching of assets, you'll want to specify a few things in a source service worker file. In that `src/sw.js` file that was a stub earlier in my described example, now it becomes closer to the final version of the generated service worker; don't worry though, we won't need to copy in any files.
  762.  
  763. First, we update our `workbox-config.js` file to accomodate the source for the `sw.js` file; it's the `swSrc` property.
  764.  
  765. https://gist.github.com/edm00se/c4defbf1b29ec3f6e10cdb7fc1afcbec#workbox-config-inject-manifest.js
  766.  
  767. Now that that's set, it's time to fill out our `src/sw.js` file. We begin it with the `importPackage` of the latest workbox-sw script, in this case from Google's CDN. Then we define the precache strategy that we'll use as the base, leaving an empty array. This will let the glob matching defined in the config file build and populate that array with what's needed.
  768.  
  769. Lastly, we update the command to generate the service worker with the task passed to the workbox cli; `workbox injectManifest <path-to>/workbox-config.js`.
  770.  
  771. \*note: for [more on workbox caching strategies][workbox-caching-strategies], there's plenty to read up on them
  772.  
  773. Finally, we have the two big exceptions that drove me to use `injectManifest` in this example. In the repo for my GitHub user page, I pull down both the JSON response for my user account that drives the information in the corresponding vue component. This is the `https://api.github.com/users/<username>` call. This also has a link to an avatar image, `https://avatars3.githubusercontent.com/...`. Those two specific responses I want to include as cached, or at least cacheable, for this site. Hence, the following [in the `src/sw.js` file](https://github.com/edm00se/edm00se.github.io/blob/source/src/sw.js).
  774.  
  775. https://gist.github.com/edm00se/c4defbf1b29ec3f6e10cdb7fc1afcbec#sw-inject-manifest.js
  776.  
  777. The strategy I used is cahce first, which will cache and prefer the cahced copy of this/these assets. This is a choice on my part and, if I expected these items to change with any great frequency, I would probably want to give ita  network first strategy, so the cache would be the fail over, not the norm.
  778.  
  779. ##### `copyLibraries`
  780. The `copyLibraries` task will do just that, copying the needed libraries that the generated service worker will use, to your server, as opposed to pulling them from the Google CDN. You can invoke it like so:
  781.  
  782. ```sh
  783. workbox copyLibraries <path-to>/workbox/
  784. ```
  785.  
  786. ### An Example
  787.  
  788. If you're looking for an example, I recently put together a simple app in [vue.js][vue-url] which uses each of the above configs and build/deploy tasks, with one change, the workbox config also bundles up some small audio files. The application is a [pointing sound board][pointing-sound-board-gh], in that it is a sound board geared for a humorous addition to pointing poker. It works well in the browser, is installable to a mobile device's home screen (Android and iOS), and works entirely offline. The app is simple, but the tooling around it is pretty impressive, and it's not that hard to set up.
  789.  
  790. #### Shout Out
  791.  
  792. The pointing sound board app is created off of a boiler plate I put together to be the foundation for such applications. It's called [vue-parcel-starter][vue-parcel-starter-gh], and it simplifies a couple of the minor things I had to do to get the tooling I wanted working harmoneously. I also published an npm initializer, so you can create an instance of the latest of that starter file with either:
  793.  
  794. ```sh
  795. npm init vue-parcel <app-name>
  796. ```
  797.  
  798. or
  799.  
  800. ```sh
  801. npx create-vue-parcel <app-name>
  802. ```
  803.  
  804. ### Summary
  805.  
  806. I hope this has been informative, or at least a bit insightful, as to how to go about setting things up. I didn't cover some of the other PWA basics, such as a manifest JSON file, meta headers in your `index.html`, or icon files. Otherwise, it's all there and you can find that other information in my example repository.
  807.  
  808. Until next time, let's build better apps!
  809.  
  810. [sw-url]: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
  811. [is-sw-ready]: https://jakearchibald.github.io/isserviceworkerready/
  812. [can-i-use-sw]: https://caniuse.com/#feat=serviceworkers
  813. [workbox-url]: https://developers.google.com/web/tools/workbox/
  814. [parcel-url]: https://parceljs.org/
  815. [workbox-cli-npm]: https://www.npmjs.com/package/workbox-cli
  816. [parcel-plugin-workbox-issue]: https://github.com/dahnielson/parcel-plugin-workbox/issues/9
  817. [workbox-caching-strategies]: https://developers.google.com/web/tools/workbox/modules/workbox-strategies
  818. [vue-url]: https://vuejs.org/
  819. [pointing-sound-board-gh]: https://github.com/edm00se/pointing-sound-board
  820. [vue-parcel-starter-gh]: https://github.com/edm00se/vue-parcel-starter]]></content:encoded>
  821.        </item>
  822.        <item>
  823.            <title><![CDATA[Developer Dog]]></title>
  824.            <link>https://edm00se.io/serverless/developer-dog-alexa-skill/</link>
  825.            <guid>https://edm00se.io/serverless/developer-dog-alexa-skill/</guid>
  826.            <pubDate>Tue, 07 Aug 2018 00:00:00 GMT</pubDate>
  827.            <description><![CDATA[an Alexa skill for fun and encouragement]]></description>
  828.            <content:encoded><![CDATA[
  829. <!-- {% include series.html %} -->
  830.  
  831. Hello from [#THATConference][that-conf]! This is my second year attending and I've been quite happy. The only thing I miss here is the great people I had the privelege of meeting and seeing within the IBM and friends world, such as the always excellent [MWLUG](https://mwlug.com) that was a little over a week ago, but my work has shifted away over the last year. I'm looking forward to a couple of ideas in the future though, so who knows what the future will bring.
  832.  
  833. There's nothing like a full day at a conference to get your developer juices flowing and a desire to get back to contributing to the blog. I also want to clear out this series to make room for other, related, topics. So here it is! When last we left off, we had covered the basic structure of a serverless function, complete with the needed hooks and handlers for use as an Amazon Alexa Skill.
  834.  
  835. ### Why Dogs?
  836.  
  837. They're pure creatures who are generally friendly and happy to please. This is a pretty encouraging thing, especially combined with many beginners needing a little encouragement when they get into software development.
  838.  
  839. ### An Expanded Example
  840.  
  841. The skill is one that retrieves an array of possible encouraging sayings, generally within the vein of dad jokes, which are dog themed. This array could come from anywhere, some backing database connected via RESTful API, GraphQL, or more. So long as the serverless function can connect to it, it's viable. This example builds from a JSON response hosted on a static site. It's not terribly dynamic, but it's updatable and serves the purpose of being available from a static site, to show off some of my amazing punny sayings. You can find that site's repository at [github.com/edm00se/dev-dog][dev-dog-site-repo], and the live site at [edm00se.codes/dev-dog][dev-dog-site-url].
  842.  
  843. Ultimately the skill will allow us to get a random "fact", this follows the fact skill format, when querying an Alexa device. It will get a random selected string from the array, then speak it. So let's being.
  844.  
  845. #### 1. Getting The Data
  846.  
  847. We'll be accessing an array of string values. It's coming from a known endpoint and will be a JSON file. My approach is to create a data handling function, in its own JavaScript file, which we can require in and use to access the data. This abstracts away any data operations from the functionality of the application logic.
  848.  
  849. https://gist.github.com/edm00se/f2245b183ce69f28879ab2191a711491#facts.js
  850.  
  851. ##### A Note on Fetch
  852.  
  853. Why am I using [Axios][npm-axios]?
  854.  
  855. TL;DR: There's no native support of the native browser [Fetch API][mdn-fetch-api]. This means we need to use _something else_. Initially, I tried using the `node-fetch` package for consistency, but that was a little bloated for my needs. Ultimately, I tried `got` and `axios` packages as well and wound up selecting `axios` for its ease of use, popularity, easy to use API, and file size. For more, check out [my asciicast comparing sizes of these options][fetch-option-asciicast].
  856.  
  857. [![asciicast](https://asciinema.org/a/153546.svg)](https://asciinema.org/a/153546)
  858.  
  859. #### 2. Registering Handlers
  860.  
  861. Registering the intent handlers is quite similar to [the basic one outlined in the previous post](/serverless/hello-alexa/#configure-your-handlers). The largest difference is that the "facts" are being brought in via a handle to a promise, the resulting array has a random member selected, and the response is built out. In this skill, I'm also building out a "card", which is displayed in the app or on a visual Echo device.
  862.  
  863. https://gist.github.com/edm00se/f2245b183ce69f28879ab2191a711491#handlers.js
  864.  
  865. #### 3. Upload and Configure Alexa Skill
  866.  
  867. The last thing to happen for this to work is a set of build and deploy steps. Specifically:
  868.  
  869. 1. building a zip file (since this is larger than the AWS Lambda in-browser editor will allow to directly edit, with the included dependencies)
  870. 2. uploading the zip file to the AWS Lambda console
  871. 3. configuring the Alexa Skill in Skill Builder for:
  872.    - utterances
  873.    - intent(s) to launch
  874.    - certifications (regarding privacy, etc.)
  875.  
  876. This last topic is a whole bit unto itself, so I'll follow-up next time with a video walkthrough of how to wire it all up.
  877.  
  878. ### Want More?
  879.  
  880. I hope this may have sparked some curiosity in getting into both serverless functions and maybe even Alexa Skills. So please feel free to check out [the Developer Dog Alexa Skill][dev-dog-skill-amzn]. It's basic, but brings in much of what's at play in a serverless function and what it takes to create an Alexa Skill.
  881.  
  882. ### Future Plans
  883.  
  884. I've begun standardizing a couple of the steps for my personal projects, such as incorporating some nifty npm scripts to package and bundle up a zip archive that can be uploaded more easily to the AWS Lambda interface for updating. These will need to be for another day though. Until next time, let's build better apps!
  885.  
  886. [that-conf]: https://www.thatconference.com
  887. [dev-dog-site-repo]: https://github.com/edm00se/dev-dog
  888. [dev-dog-site-url]: https://edm00se.codes/dev-dog/
  889. [dev-dog-skill-amzn]: https://smile.amazon.com/edm00se-Developer-Dog/dp/B06XVW6TLL?sa-no-redirect=1
  890. [npm-axios]: https://npm.im/axios
  891. [mdn-fetch-api]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  892. [fetch-option-asciicast]: https://asciinema.org/a/
  893. ]]></content:encoded>
  894.        </item>
  895.        <item>
  896.            <title><![CDATA[Hello Alexa]]></title>
  897.            <link>https://edm00se.io/serverless/hello-alexa/</link>
  898.            <guid>https://edm00se.io/serverless/hello-alexa/</guid>
  899.            <pubDate>Thu, 24 May 2018 00:00:00 GMT</pubDate>
  900.            <description><![CDATA[an intro to serverless and Alexa skills]]></description>
  901.            <content:encoded><![CDATA[
  902. <!-- {% include series.html %} -->
  903.  
  904. ### Background
  905.  
  906. Here's a post I've been working on for longer than I care to admit to. For a few months, I've been wanting to kick off a series on serverless development practices, theory behind why serverless is such a game changer, as well as a couple of demos. The fact of the matter is, each time I've started, I've gotten so bogged down in writing a post about _what serverless is_ and the backing theory, I've managed to loose blogging momentum and it has since idled in my drafts folder for a while. So, what I've settled on for now, is a brief introductory to my first Alexa Skill, and the basics of how it works.
  907.  
  908. ### Intro
  909.  
  910. Have you gotten past your fears of an always listening device in your home? Has conversational computing started to change how you think about software development? Have you wanted to build your first Alexa Skill, with more than a simple "hello world"?
  911.  
  912. [Amazon's Alexa (Echo) devices][amazon-echo] and [services][alexa-voice-service], along with [Google's Home][google-home] line of voice/audio products, are changing the way everyday computing exists and is used in our lives. They're not the only ones, along with Siri, Cortana, and more, these voice and audio services have helped to begin a bit of a minor revolution. This one doesn't require singing and dancing, unless that's your thing, but rather that we re-examine the nature of how we interact with computing devices. The very nature of being able to speak aloud, give instruction in a natural langauge format, and receive information is probably one of the more underrated paradigm shifts of this decade. With any luck, it will only go onwards and upwards from here... unless Skynet takes over.
  913.  
  914. A while back I tweeted out a short poll on what sort of Alexa Skill I should build. I included a couple options and got a couple votes back. Here are the results.
  915.  
  916. <iframe class="tweetbu" src="https://tweets.edm00se.codes/845786561186795521/">failed to load</iframe>
  917.  
  918. This will feed the next post, which covers the Alexa Skill I built. For now, let's have a look at how a basic Alexa Skill is structured.
  919.  
  920. ### Constructing an Alexa Skill
  921.  
  922. At the most basic level, an Alexa Skill is comprised of three main parts:
  923.  
  924. - a backing **service**, generally a serverless implementation, hosted _somewhere_ that the Alexa skill can invoke
  925.  - this can be a publicly accessible HTTP(S) endpoint
  926.  - for Amazon's product usage, the most common is AWS Lambda
  927.  - AWS Lambdas use their ARN (Amazon Resource Names), which is an Amazon internal name space; quite easy to talk from AWS Lambda to an Alexa Skill (point at the right ARN)
  928.  - for Alexa skills, most often make use of the Alexa SDK or related packages (to assist in handling the hooks in a consistent fashion)
  929. - a **configuration** of the Alexa Skill
  930.  - matches vocal commands such as name and actions, responses
  931.  - it matches registered phrases to "intents"
  932.  - these intents can be either standard ones from Amazon, such as "cancel", or custom ones such as "do this thing I defined"
  933. - it is **published** to the Alexa Skill "store" on Amazon
  934.  - it can be available to test pre-release / while in development, for the developer's associated account (and beta testers)
  935.  - the publishing process involves review
  936.  - my experience has been around 24 hours turn around
  937.  - some things are annoying in that they can be quite arbitrary
  938.    - like one of my skills which fit their described naming conventions as it is listed, but their review said it didn't; they were empirically incorrect, but there is _no_ appeals process
  939.  
  940. ### How About an Example Skill?
  941.  
  942. Before we go further, I'd like to ask you to think about how we register human speech and match up the intentions behind them to any conveyed ideas or actions to take. It's no small task, but thankfully we don't have to reinvent the wheel. For Alexa Skills, the Alexa Skills Kit brings a fair amount of tooling easily installed via `npm` (and respective equivalents for other runtimes and languages). For the most part, we'll have our functions that define what happens when an action is taken, and the "Intent" definitions, that call corresponding actions. There's lots of room to talk about what these mean and I am taking a somewhat simplistic approach to the theory, but enough theory for now! I know I'm bound to come back to the theory, I seem to do so more than I might like, but for now, let's assume we know what we're doing and why. On to the examples!
  943.  
  944. #### Example 1: Hello World
  945.  
  946. Stop me if you've heard this before, but we'll begin with a good ol' "hello world", you know this one, right? So, what do we need to start with?
  947.  
  948. ##### Scaffold The Project
  949.  
  950. 1. this is a Node.js example, so we'll be creating a new project directory and initializing with npm
  951.    - `mkdir hello-world`
  952.    - `cd hello-world`
  953.    - `npm init -y`
  954. 2. we'll use the official [Alexa Skills Kit SDK for Node.js][alexa-sdk-node]
  955.    - `npm install --save alexa-sdk`
  956. 3. we'll have a short and sweet Node.js module, so create the source file
  957.    - `touch index.js`
  958. 4. massage your `package.json`, update your:
  959.    - name
  960.    - description
  961.    - ensure the "main" points to `index.js` (that should be the default)
  962.  
  963. ##### Build Out `index.js`
  964.  
  965. To correctly hook into the epxected events, we'll **export** a `handler` function, containing hooks to our various "intentions". This is what is expected by the Alexa SDK; for more [on registering handlers, check out their wiki][alexa-sdk-register-handlers].
  966.  
  967. Here's a start to this file:
  968.  
  969. https://gist.github.com/edm00se/21702cdba32200001a6e3884b759415d#begin.js
  970.  
  971. ##### Configure Your Handlers
  972.  
  973. Since we are now bringing in our handlers, here's a quick look at what is entailed. For starters, it's an object, as you can see from the declaration above. We will match our intent names to a function, which will perform actions and `emit` an action based on some pre-defined actions. These can also be a different registered intention, such as chaining an "I didn't understand that" prior to calling the help intent response.
  974.  
  975. Here's a basic configuration for "Hello World".
  976.  
  977. https://gist.github.com/edm00se/21702cdba32200001a6e3884b759415d#handlers.js
  978.  
  979. To break things down, you'll notice there are two forms of intents, custom and standard Amazon intents. The Amazon standard intents are common across skills, and cover cancellations, stopping, and asking for help.
  980.  
  981. The first custom intent is the `LaunchIntent`. That registers an action, "speaking" a phrase and emitting an event.
  982.  
  983. ### Next Time: A Better Example
  984.  
  985. I have a better example for the next post, the skill I wrote following the above twitter poll. Next time I will cover an Alexa Skill I have published, along with some show-and-tell about configuring the skill with intentions in the AWS Lambda and Alexa Skill Consoles.
  986.  
  987. [amazon-echo]: http://www.amazon.com/echo
  988. [alexa-voice-service]: https://developer.amazon.com/alexa-voice-service
  989. [google-home]: https://madeby.google.com/home/
  990. [alexa-sdk-node]: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs
  991. [alexa-developer-skills]: https://developer.amazon.com/alexa-skills-kit/build
  992. [alexa-sdk-register-handlers]: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/wiki/Developing-Your-First-Skill#implementing-request-handlers
  993. ]]></content:encoded>
  994.        </item>
  995.        <item>
  996.            <title><![CDATA[Good News]]></title>
  997.            <link>https://edm00se.io/self-promotion/looking-back-and-forward/</link>
  998.            <guid>https://edm00se.io/self-promotion/looking-back-and-forward/</guid>
  999.            <pubDate>Tue, 23 Jan 2018 00:00:00 GMT</pubDate>
  1000.            <description><![CDATA[and a little retrospective]]></description>
  1001.            <content:encoded><![CDATA[
  1002.  
  1003. The past year has been great fun. I've had a couple of major changes, including a change in job and beginning to work from home. I've also done a few different projects that I hadn't been involved in before. I'll get to those in a little bit, but first...
  1004.  
  1005. ![good news everyone](./images/GoodNewsEveryone.png){.skinny}
  1006. *How about some good news for a change?*
  1007.  
  1008. ### Thank You!
  1009.  
  1010. I am quite happy to have been named an IBM Champion, returning for the fourth year in a row. I always hope that what I choose to blog about or share in the open source and collaborative spaces is worth something to other people. It's an honor to rejoin my fellow IBM Champions and I'd like to extend a great big welcome to all those newly named!
  1011.  
  1012. <!-- insert links to announcements and namings for ICS + Cloud -->
  1013.  
  1014. ### Projects
  1015.  
  1016. #### New Things
  1017.  
  1018. As I mentioned, I've gone down the proverbial rabbit-hole on a couple of different projects this past year. As usual, these  If you're interested or want to more about them, let me know. Here are some of the highlights.
  1019.  
  1020. - three [npm packages][npm-edm00se] published (of note)
  1021.  - [generator-xsp][gen-xsp], a yeoman generator for scaffolding an [XPages][xpages-info] compatible On Disk Project
  1022.  - [node-dora][node-dora], a node wrapper for [DORA][dora]
  1023.  - [dora-cli][dora-cli], a CLI tool to invoke `node-dora`
  1024. - [eleven new repositories on GitHub][edm00se-github-2017]
  1025.  - including [an Alexa skill][dev-dog-skill] ([link to skill on Amazon.com][amzn-dev-dog]) and [a companion site][dev-dog-site], which drives the content
  1026. - answered [seven questions on StackOverflow][edm00se-stackoverflow]
  1027. - blogged nine times
  1028.  
  1029. ### What Does It All Mean?
  1030.  
  1031. I've blogged less than the previous years, that's for certain. I've been trending towards more focused efforts, with more specific blog posts. I've also dabbled at terse (short and relevant) videos, such as with my [Docker Quick Tips][docker-tips-playlist]. I've got a couple of longer efforts in the works, which hopefully will be of use. I'm working on focusing more on cloud offerings, architecture, implementation, and development practices. I was greatly pleased to be named an IBM Champion for Cloud in 2017 in addition to Collaboration Solutions, so I'm looking to keep up that end of things. No matter what happens, it should be a fun ride.
  1032.  
  1033. On the side, over the last year, I've also taken up the hobbies of 3D printing, baking, and I finished configuring and setting up my home NAS. All in all, I've been busy, especially with code and other work, just at the expense of writing into the blog. I'll try to catch up on the more interesting things I've been up to, so hopefully others can gain from my experience.
  1034.  
  1035. ### More Recently
  1036.  
  1037. <iframe class="tweetbu" src="https://tweets.edm00se.codes/954467891453284353/">failed to load</iframe>
  1038.  
  1039. Recently, just this past week in fact, I created and launched a "recipe" for [Franz][franz-messenger]. Franz is an [Electron based][electron] application client that wraps around many messenger formats, including WhatsApp, Skype, Slack, and other web services, such as Google Inbox, and more.
  1040.  
  1041. Franz version 5, which is in active beta, is pretty stable and has quite an extensible format, which made creating a new "service" type pretty easy. In fact, it was so easy to implement, I added a service "recipe" for [IBM Watson Workspace][ibm-ww] during my lunch break. It's essentially configured as a low complexity [node module][node-module] with some configuration definitions, regarding the live web portal (workspace.ibm.com), and a rather small JS function for an event loop which registers events, such as direct messages or general/chat messages. If you're intrigued, check out the repository.
  1042.  
  1043. #### [GitHub: Watson Workspace for Franz][franz-watson-workspace]
  1044.  
  1045. Included in the readme are instructions for how to clone the "recipe", until it has been merged into Franz's codebase, which I'll submit after its had some beta testing.
  1046.  
  1047. As always, contributions are welcome!
  1048.  
  1049. ### Until Next Time
  1050.  
  1051. Cheers! 🍻
  1052.  
  1053. [npm-edm00se]: https://www.npmjs.com/~edm00se
  1054. [gen-xsp]: https://www.npmjs.com/package/generator-xsp
  1055. [xpages-info]: http://xpages.info
  1056. [node-dora]: https://www.npmjs.com/package/node-dora
  1057. [dora]: https://github.com/camac/dora
  1058. [dora-cli]: https://www.npmjs.com/package/dora-cli
  1059. [edm00se-github-2017]: https://github.com/search?p=1&q=user%3Aedm00se+created%3A2017-01-01..2017-12-31&type=Repositories&utf8=%E2%9C%93
  1060. [dev-dog-skill]: https://github.com/edm00se/developer-dog-alexa-skill
  1061. [dev-dog-site]: https://edm00se.codes/dev-dog/
  1062. [amzn-dev-dog]: https://bit.ly/dev-dog-skill
  1063. [edm00se-stackoverflow]: https://stackoverflow.com/search?q=user%3A1720082+created%3A2017
  1064. [docker-tips-playlist]: https://docs.google.com/forms/d/e/1FAIpQLSfb3Pbhxs9KRcs8UpT4XJE4bTj0gJeZ1Ay9F57xt6KYtU_aJA/viewform
  1065. [franz-messenger]: https://meetfranz.com
  1066. [electron]: https://electronjs.org
  1067. [ibm-ww]: https://workspace.ibm.com/
  1068. [node-module]: https://nodejs.org/api/modules.html#modules_modules
  1069. [franz-watson-workspace]: https://github.com/edm00se/franz-recipe-watson-workspace
  1070. ]]></content:encoded>
  1071.        </item>
  1072.        <item>
  1073.            <title><![CDATA[Composing With Docker]]></title>
  1074.            <link>https://edm00se.io/docker/composing-with/</link>
  1075.            <guid>https://edm00se.io/docker/composing-with/</guid>
  1076.            <pubDate>Thu, 02 Nov 2017 00:00:00 GMT</pubDate>
  1077.            <description><![CDATA[a config in the repo beats a script on the host]]></description>
  1078.            <content:encoded><![CDATA[
  1079. ### Background
  1080.  
  1081. About a year ago, I blogged on [automating server upgrades with Docker and a BASH script](/docker/scripting-server-upgrades/). This met the needs I had at the time, and worked itself out to be pretty stable. But, since I think about such things and always question my preconceptions, I went down a path of creating a [Docker compose config file][compose-file], something I wouldn't have had to create from scratch by waiting a little while as [one appeared as an example from GitLab][gitlab-compose-file-ex]. As it turns it, it was a great learning experience regardless, so this post isn't so much meant to solely cover the contents of a config, but rather the process I took to get there.
  1082.  
  1083. In the process of converting, the logic has wwitched from being contained purely a script, which was written in BASH and could be somewhat more difficult for some to read, to a [YAML][yaml-url] config file; `docker-compose.yml`. YAML may seem strange at first to some people, but most people understand the concept of `key` to `value` pairings, so I would hazard this would pass the "my mother" test\* of ease of reading. YAML is meant to be "...a human friendly data serialization standard for all programming languages."
  1084.  
  1085. \*Note: that's not an insult mom, it just illustrates the point.
  1086.  
  1087. ### Docker Compose
  1088.  
  1089. A Docker compose file is written in [YAML][yaml-url] and follows a [certain file specification][compose-file]. The basic idea is to define "services", configured with any port, volume, or links to other dependant containers. These generally follow the `docker run` command line option flags, along with some other added benefits. If you caught it, that means that a Docker compose file can define between one and a large number of containers, as opposed to requiring a separate execution of `docker run ...` to create each one manually from the CLI; this multi-container capability, combined with setting my options permanently\*, is what first drew me into using Docker compose.
  1090.  
  1091. #### A Brief Conceptual
  1092.  
  1093. Say you have a web application, which runs in Node, and talks to a database (this should work for any runtime that has a dependency list and manager). Presuming you're handling all db creation/configuration via scripts already, the creation of a specific instance, aside from instance/live data, is pretty trivial, especially if you can pull it from a base Docker image already. So, assuming your applications `Dockerfile` achieves the basic requirements, then all that's left is to effectively "wire up" the configuration of things such as persisted volumes, port mapping, etc.
  1094.  
  1095. If you're looking for such an example that's boiled down and easy to follow, [here's one I prepared earlier][gh-compose-example].
  1096.  
  1097. ### Creating the Config
  1098.  
  1099. A `docker-compose.yml` file, alternatively any `.yml` and can be used with the `docker-compose` command (`-f some-other.yml` or `--file`) has properties defined at the high level and inside of others. At the high level, a `version` is required, as specifics change across different release versions. Then, we'll need to define our services. Additional high level properties could be `volumes`, as sharing a persisted volume between containers requires a higher level definition than inside a given service.
  1100.  
  1101. #### Project Directory
  1102.  
  1103. Here's a simplified directory tree of how this would look.
  1104.  
  1105. ```sh
  1106. my-sample-app
  1107. ├── app.js
  1108. ├── docker-compose.yml
  1109. ├── Dockerfile
  1110. ├── node_modules
  1111. └── package.json
  1112. ```
  1113.  
  1114. #### Config Basics
  1115.  
  1116. https://gist.github.com/edm00se/ccab5a897aa47faa4b2fb4881f8a6bf2#docker-compose.yml
  1117.  
  1118. The `Dockerfile` information relevant to build a container from a base image, so only things like persisting volumes and port bindings are generally at play.
  1119.  
  1120. #### Filling It Out
  1121.  
  1122. From the given example, you can see the ports are bound and persisted volumes are specified. The formatting of these lines follows `hostPort:containerPort` (the port on the host OS binding to the port that the containerized application is exposing). If you ever question the syntax, consult [the documentation][docker-compose-port-short-syntax]. If you're forgetful like myself, you may need to re-read it a couple times before it sinks in.
  1123.  
  1124. The only tricky thing you may see in this config is that the database, a service called "redis", is mentioned by image name (from the [Docker hub][docker-hub-url] registry), but that it is marked as a dependency by the "web" service, by its service name. Otherwise the "build" property just tells compose that the current working directory is the base application ("web" service) to be built; I was explicit in the default `Dockerfile` name, which isn't needed, and the "context" property just tells it where to find where to run the default "build" from.
  1125.  
  1126. #### Completed Config
  1127.  
  1128. Here's the completed config file for my GitLab instance.
  1129.  
  1130. https://gist.github.com/edm00se/6d095caa3e2d9753b2b1d773a011ef23#docker-compose.yml
  1131.  
  1132. You may notice I'm referencing the current working directory with `.`. This works fine in Docker compose and is a great way to reference the current working directory (or project path, relative to the location of the `docker-compose.yml` file).
  1133.  
  1134. ##### A Note on CWD
  1135.  
  1136. Current working directory (cwd), denoted as `.` on \*nix systems (and PowerShell) is pretty powerful. It's available in Docker compose config files, but not in a vanilla `docker run...` command. If you're looking to get around that limitation and have access to a *nix or like environment, check out my [quick tip on subject on YouTube][yt-docker-cwd].
  1137.  
  1138. https://www.youtube.com/watch?v=MdRWkqcbLJI&list=PLk_BgI9qpsGjVwLPqBpVKqA3M4Sdv8wlw
  1139.  
  1140. ### Updating the Upgrade Shell Script
  1141.  
  1142. The last thing I did was to update the "upgrade" BASH script I had written, as my admin and I were using it already and it provides a sense of consistency. Underlying the script invocation and the wrapper I placed around the "guts" to handle a lockfile, I swapped out the `docker run ...` command and options for simply:
  1143.  
  1144. ```sh
  1145. docker-compose pull # pulls the latest of any images in the registry
  1146. docker-compose down # downs the current instance containers
  1147. docker-compose up -d # creates and runs in the background a new, updated instance
  1148. ```
  1149.  
  1150. ### Deployment Concerns
  1151.  
  1152. As you can probably guess, our logic of where we keep our "single source of truth" to build and run our application has shifted away from a script that needs to be copied to, or configured on, a host machine to being an asset of the source control repository. This has a lot of advantages, as it means that any instance should inherit from the source control, being the "single source of truth", instead of relying on the assets having been copied correctly, at the correct version, and the appropriate dependencies installed. This leads obviously into a more continuous deployment model and is one I favor. If the configuration is correct, then the specifics of the instance should only vary in data and cloned (commit) level. This is great and eliminates lots of fiddling around with deployment concerns and checks and balances to protect against an "oops". Checks and balances are still good for sanity, but become secondary to the fact of the matter that the assets exist at a commit level and the correctness is boolean.
  1153.  
  1154. ### Profit
  1155.  
  1156. If you're able to put together a `docker-compose.yml` file without too much fuss, you'll find yourself able to do other things with YAML configurations, up to and including [Docker "stacks"][docker-stacks-url], which seek to provide a similar mechanism to define an entire server stack via configuration, since Docker compose is orientated more towards multiple container applications. All in all, YAML is used by more than just Docker, is easy to pick up, and the Docker compose configurations aren't terribly difficult to understand; your career skills will thank you for spending a little time getting familiar with compose.
  1157.  
  1158. ### Source Repository
  1159.  
  1160. Per usual, I have a repository on GitHub where you can find this in full. Feel free to use it, modify it, or whatnot.
  1161.  
  1162. #### [github.com/edm00se/composing-gitlab-docker][gh-repo]
  1163.  
  1164. The simple node example can be found on GitHub as well, [github.com/edm00se/simple-docker-compose-node-redis-demo][gh-compose-example].
  1165.  
  1166. Until next time, 🍻!
  1167.  
  1168. [compose-file]: https://docs.docker.com/compose/compose-file/
  1169. [gitlab-compose-file-ex]: https://docs.gitlab.com/omnibus/docker/README.html#install-gitlab-using-docker-compose
  1170. [yaml-url]: https://yaml.org
  1171. [gh-compose-example]: https://github.com/edm00se/simple-docker-compose-node-redis-demo
  1172. [docker-stacks-url]: https://docs.docker.com/compose/bundles/
  1173. [yt-docker-cwd]: https://www.youtube.com/watch?v=MdRWkqcbLJI&t=3s&index=3&list=PLk_BgI9qpsGjVwLPqBpVKqA3M4Sdv8wlw
  1174. [docker-hub-url]: https://hub.docker.com/
  1175. [docker-compose-port-short-syntax]: https://docs.docker.com/compose/compose-file/#ports
  1176. [docker-compose-up-specifics]: https://docs.docker.com/compose/reference/up/
  1177. [gh-repo]: https://github.com/edm00se/composing-gitlab-docker
  1178. ]]></content:encoded>
  1179.        </item>
  1180.        <item>
  1181.            <title><![CDATA[Hacktoberfest and More]]></title>
  1182.            <link>https://edm00se.io/open-source/hacktober2017/</link>
  1183.            <guid>https://edm00se.io/open-source/hacktober2017/</guid>
  1184.            <pubDate>Tue, 24 Oct 2017 00:00:00 GMT</pubDate>
  1185.            <description><![CDATA[the code still runs on servers]]></description>
  1186.            <content:encoded><![CDATA[
  1187. ### Hacktoberfest 2017
  1188.  
  1189. October brings many good things with it. It's the beginning of autumnal colors here, along with some yard raking in my case. It also brings with it not just Oktoberfest, but Hacktoberfest!
  1190.  
  1191. ![Hacktoberfest 2017](./images/Hacktoberfest2017.png){.skinny}
  1192.  
  1193. [Hacktoberfest][hacktoberfest-url] is a month long open source support initiative, sponsored by [Digital Ocean][dig-ocean], partnering with [GitHub][gh-url]. It's meant to promote open source involvement and contribution. As added incentive, if you meet the criteria, you can get a free t-shirt (and stickers). Who doesn't like free?
  1194.  
  1195. #### Criteria for Completion
  1196.  
  1197. It's pretty simple:
  1198.  
  1199. - sign up at [hacktoberfest.digitalocean.com][hacktoberfest-url]
  1200. - complete four (4) [Pull Requests][gh-pr] on [GitHub.com][gh-url] to any combination of public repositories
  1201.  
  1202. That's it, no step three. If you complete those by the end of October, you should get an email from Digital Ocean announcing your awesomeness, along with a link to put in your mailing address.
  1203.  
  1204. #### What Makes Sense?
  1205.  
  1206. Any public repository works for Hacktoberfest, but you should ideally seek out some of the projects that you've found useful, helpful, or meaningful. A couple of the more notable projects I was happy to contribute to this month includes:
  1207.  
  1208. - [nuxt.js][nuxt-url], "...a framework for universal [Vue.js][vue-url] applications" (things including [SSR][ssr])
  1209. - [nuxtent][nuxtent-url], a library for use with nuxt "for content heavy sites as easy as using Jekyll, Hugo, or any other static site generator"
  1210.  
  1211. In case you haven't picked up on a theme, my love of vue has only grown over the last year. It's recently broken v2.5.0, had a full [road map published][vue-roadmap], and even done [an AMA on hashnode][vue-ama]. It's a great project, is easily configured, reads well to a novice, has great documentation, is quite performant, and [projects are easily scaffolded][vue-cli] in a variety of flavors.
  1212.  
  1213. [Update]
  1214.  
  1215. [Egghead.io][egghead] just release a full course covering creation of a news app with vue, the vue-cli, and nuxt. It's listed as free and looks to be a good one, so [go check it out!][vue-nuxt-egghead]
  1216.  
  1217. [/Update]
  1218.  
  1219. #### Know of Any Good Open Source Projects?
  1220.  
  1221. As it happens, if you're a reader here from the Domino/XPages community, you may be aware of a tool I launched at the beginning of the year; an [XPages][xpages] ODP compatible [Yeoman generator][yeo], [generator-xsp][gen-xsp]. It's been [blogged about][gen-xsp-blog] and [screen cast on Notes in 9][gen-xsp-ni9]; so if you're curious to use, please do. More importantly, I'm seeking a combination of additional contributors or maintainers, so if you have thoughts, please [raise an issue][gen-xsp-iss] to suggest features, spot bugs, or [submit a PR][gen-xsp-pr].
  1222.  
  1223. ### IBM Champion Nomination
  1224.  
  1225. I've had the great honor of being named an IBM Champion for three years running. I tend to stick to the Domino/XPages spaces (Collaboration Solutions) and Cloud (Bluemix), but there are more than just two categories. Nominations have been opened up for 2018, so if you know of anyone deserving of being nominated, please [put in some good words][ibm-champion-nomination]! Submissions are open through November 13th.
  1226.  
  1227. ### Summary
  1228.  
  1229. There's a lot going on these days, it seems, but it's nice to take some time to pay back to open source for all the use its given us. No, it doesn't have to be mine by any means, there are lots of great projects that could use a little help. So get out there, there's still some time left in the month! Keep on hacking !
  1230.  
  1231. [hacktoberfest-url]: https://hacktoberfest.digitalocean.com/
  1232. [gh-url]: https://github.com/
  1233. [dig-ocean]: https://www.digitalocean.com/
  1234. [gh-pr]: https://help.github.com/articles/about-pull-requests/
  1235. [nuxt-url]: https://nuxtjs.org/
  1236. [vue-url]: https://vuejs.org/
  1237. [ssr]: https://nuxtjs.org/guide#server-rendered-universal-ssr-
  1238. [nuxtent-url]: https://nuxtent.now.sh/
  1239. [vue-roadmap]: https://github.com/vuejs/roadmap
  1240. [vue-ama]: https://hashnode.com/ama/with-vuejs-team-cj7itlrki03ae62wuv2r2005s
  1241. [xpages]: https://xpages.info/
  1242. [yeo]: https://yeoman.io/learning/
  1243. [gen-xsp]: https://github.com/edm00se/generator-xsp
  1244. [gen-xsp-blog]: https://edm00se.io/self-promotion/ni9-node-tools-grab-bag/
  1245. [gen-xsp-ni9]: https://www.notesin9.com/2017/04/04/notesin9-205-leverage-domino-development-with-new-tools/
  1246. [gen-xsp-iss]: https://github.com/edm00se/generator-xsp/issues/new
  1247. [gen-xsp-pr]: https://github.com/edm00se/generator-xsp/pull/new/master
  1248. [ibm-champion-nomination]: https://www.ibm.com/developerworks/champion/nominate.html
  1249. [vue-cli]: https://github.com/vuejs/vue-cli
  1250. [egghead]: https://egghead.io/
  1251. [vue-nuxt-egghead]: https://egghead.io/courses/create-a-news-app-with-vue-js-and-nuxt
  1252. ]]></content:encoded>
  1253.        </item>
  1254.        <item>
  1255.            <title><![CDATA[Go Evergreen]]></title>
  1256.            <link>https://edm00se.io/web/evergreen-web/</link>
  1257.            <guid>https://edm00se.io/web/evergreen-web/</guid>
  1258.            <pubDate>Tue, 17 Oct 2017 00:00:00 GMT</pubDate>
  1259.            <description><![CDATA[not just for trees anymore]]></description>
  1260.            <content:encoded><![CDATA[
  1261. ### Happy 🎂 Day IE 11!
  1262.  
  1263. On the 17th of October in 2013, Internet Explorer 11 was released from Microsoft. That means that as of today, this popular\* browser is now four years old and, with all respect to it, it really ought to go.
  1264.  
  1265. ![Belle chucking Gaston out the door](./images/Out_Gaston.gif){attribution="Gaston is a character from the Disney film Beauty and the Beast. All rights belong to Disney.".skinny}
  1266. *Good day sir. I said good day!*
  1267.  
  1268. ### Evergreen Browsers
  1269.  
  1270. What makes a browser, or any software for that matter, [evergreen][evergreen-about]? Well, the basic requirements for a browser, or any piece of software for that matter, are specifically the support of automatic updates, that bring in:
  1271.  
  1272. - security fixes
  1273. - bug fixes
  1274. - rolling/evolving feature udpates
  1275.  
  1276. Some of the more popular evergreen browser right now are Chrome, Firefox, and MS Edge. Internet Explorer, for example, is not an evergreen browser.
  1277.  
  1278. ### Why Does It Matter?
  1279.  
  1280. To be perfectly honest, the advancements in JavaScript (EcmaScript) to make use of its annual specification update means that we need to stay on top of our games. In addition to keeping up with the standards being evolved with the spec, we have security concerns as well. Does everyone remember the stretch of SSL issues we saw with [POODLE][poodle-about] and friends that lasted a minor eternity? Does anyone think that will never happen again? We need to stay flexible in our updates and reaction to threats, that's not in question and should sell this "argument" in and of itself.
  1281.  
  1282. When a web browser is evergreen, it brings with it flexible adaptation to security threats, pursuit of the evolving web standards specifications, and lets us have access to the latest and greatest APIs so that we as developers aren't held back. Everyone wins from the user, through the developers, up to the vendors.
  1283.  
  1284. ## A Case In Point
  1285.  
  1286. In case you're wondering how this sort of thing comes up in practice, I've got a somewhat handy example that I ran into in my day job. Consider the following object literal assignment in JavaScript. For this example, we're using ES5 syntax.
  1287.  
  1288. https://gist.github.com/edm00se/ac9a624ec1943268f738bb624ab0cc51#basic_ob_assign.js
  1289.  
  1290. Now consider we add another function (`yetAnotherFunc`) and while adding it to the object, we get a typo.
  1291.  
  1292. https://gist.github.com/edm00se/ac9a624ec1943268f738bb624ab0cc51#oops_assign.js
  1293.  
  1294. Obviously, we meant to use a `:` in place of a `,` but what happens when we have this? Even though we're writing to an ES5 level, evergreen browsers have been picking up significantly more of the ES2015(ES6) and above features and syntax support. Seeing how this is the evolving spec (with annual updates), there's no reason to hold back. So, if you haven't guessed it by now, this works perfectly fine in any browser that supports the [shorthand assignment in Object initializer][obj-init-shorthand], which is part of the ES6 spec. We've defined `yetAnotherFunc` twice since we used the same property name as the function name, but since both reference the same function, there's not much in the way of issues. This also means that because older browsers, such as IE11, aren't evergreen, they _do not_ and cause us to find a functional issue in potentially only one of our tested browsers. (\*any that doesn't support beyond ES5)
  1295.  
  1296. If you're saying, "but that's not IE11's fault", you'd be absolutely correct. IE11 doesn't support ES6, but then it _never will_ and is holding us back; the entire point I'm trying to make here.
  1297.  
  1298. #### See It In Action
  1299.  
  1300. <iframe width="700"
  1301.    title="ES6 Object Initializer Effect via Typo"
  1302.    height="320"
  1303.    frameborder="0"
  1304.    src="https://codepen.io/edm00se/embed/JraNrJ/?height=378&theme-id=0&default-tab=js,result&embed-version=2">
  1305.  See the Pen <a href='https://codepen.io/edm00se/pen/JraNrJ/'>JraNrJ</a> by Eric McCormick (<a href='https://codepen.io/edm00se'>@edm00se</a>) on <a href='https://codepen.io'>CodePen</a>
  1306. </iframe>
  1307.  
  1308. ### Summary
  1309.  
  1310. The ultimate intent of this post is to stress the importance of what we need a web browser to do. Ultimately, just as [Continuous Integration (CI)][ci-about] has led to great advances in how we accomplish development along with its testing and release processes through sheer flexibility, consistency, and immediacy; we also should look for those qualities in our web browser(s). If a browser grows stale or sluggish in its release cycle (such as non-evergreen browsers), then we all suffer for its inability to respond to security threats, release with the known evolving specifications from [W3C][w3c-about] and [TC39][tc39-about]. It's not just about having the new and shiny, it's also about assurance of response to security threats and adapting to the direction of the web.
  1311.  
  1312. In the end, the question I've been asking myself is:
  1313.  
  1314. > How do we get from _needing to support IE(11)_ to the bright future where we don't have to care if a browser vendor allows itself to go out of date?
  1315.  
  1316. I'm fairly certain this will need to be [grassroots][grassroots-about] in nature. We need to "bubble up" our need to move on from inflexible browsers; previously, I would include something like the [browse happy][browse-happy] include (\*this is limited, as it is generally detecting IE specifically) and point those with outdated browsers to it, but it still lists IE 11; part of what I'm trying to avoid.
  1317.  
  1318. \*Note: I don't mean to hate on IE, rather Microsoft has moved on with Edge and while they have taken a very business stance of supporting IE for a longer time than I would prefer, but it's for their business customers who have locked-in requirements of older software requiring, say, IE 7. This is rather meant to highlight the need for us as the development community to force the movement and let the "norm" be something favorable to everyone.
  1319.  
  1320. Until next time, 🍻!
  1321.  
  1322. [evergreen-about]: https://www.hanselman.com/blog/TheEvergreenWeb.aspx
  1323. [obj-init-shorthand]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015
  1324. [poodle-about]: https://en.wikipedia.org/wiki/POODLE
  1325. [ie-version-history]: https://en.wikipedia.org/wiki/Internet_Explorer_version_history#Release_history_for_desktop_Windows_OS_version
  1326. [chrome-version-history]: https://en.wikipedia.org/wiki/Google_Chrome_version_history
  1327. [firefox-version-history]: https://en.wikipedia.org/wiki/Firefox_version_history#Rapid_releases
  1328. [edge-version-history]: https://en.wikipedia.org/wiki/Microsoft_Edge#Release_history
  1329. [ci-about]: https://en.wikipedia.org/wiki/Continuous_integration
  1330. [w3c-about]: https://www.w3.org/standards/
  1331. [tc39-about]: https://ecma-international.org/memento/TC39.htm
  1332. [browse-happy]: https://browsehappy.com/
  1333. [grassroots-about]: https://en.wikipedia.org/wiki/Grassroots
  1334. ]]></content:encoded>
  1335.        </item>
  1336.        <item>
  1337.            <title><![CDATA[Change is in the Air]]></title>
  1338.            <link>https://edm00se.io/self-promotion/change-is-in-the-air/</link>
  1339.            <guid>https://edm00se.io/self-promotion/change-is-in-the-air/</guid>
  1340.            <pubDate>Fri, 01 Sep 2017 00:00:00 GMT</pubDate>
  1341.            <description><![CDATA[Summer, ThatConf, Open Source, and Work]]></description>
  1342.            <content:encoded><![CDATA[
  1343. ### I'm Back
  1344.  
  1345. ![What can I say except "you're welcome"?](./images/MauiYoureWelcome.gif){attribution="Maui is a character from the Disney film Moana. All rights belong to Disney."}
  1346.  
  1347. ##### In Case You Missed It
  1348.  
  1349. If you find yourself asking "where was Eric?", this should summarize it all:
  1350.  
  1351. <iframe class="tweetbu" src="https://tweets.edm00se.codes/863881998561136641/">failed to load</iframe>
  1352.  
  1353. Instead of trying to do everything all summer, I tend to take a break from blogging and a lot of open source endeavors over the summer. It means I can focus on family time along with yard and house projects.
  1354.  
  1355. ![Ah... Summer](./images/Olaf_summer.gif){attribution="Olaf is a character from the Disney film Frozen. All rights belong to Disney."}
  1356.  
  1357. That's all paid off and, with fall fast approaching, I've found myself wanting to start those things back up; ramping up into winter when I don't do as much outdoors. I've had a couple of things going on, so I figured a post to recap would be in good order.
  1358.  
  1359. ### That Conference
  1360.  
  1361. This year, instead of joining friends at [MWLUG][mwlug-url] 😢 (now renamed to CollabSphere, next in August 2018 in Ann Arbor, MI), I had the opportunity to attend [That Conference][that-conf-url] for the first time. It's been on my radar for a few years, but this was my first time and I was quite pleased with it. It had a lot of great sessions, some great keynotes, there were some breakout sessions as well, which included some great topics and I wish I'd gone to more. The one I _did_ go to was on [PWAs][pwa-url]; there was some good conversation which was made even better by the fact that there is some amazing potential for PWAs w/ the inclusion of [service workers][service-workers-url]. I'm hoping to go back next year as it was a great conference, only made better in that it was held at the [Kalahari Resort & Convention Center in Wisconsin Dells][kalahari-url].
  1362.  
  1363. All in all, That Conference was great and I'm looking forward to it next year. If I'm ambitious enough, I'll submit an abstract as well.
  1364.  
  1365. #### Aside: Kalahari
  1366.  
  1367. The Kalahari was a great venue for the conference. It's also a great Wisconsin Dells resort, complete with indoor and outdoor water parks, and an indoor theme park. We drove down an extra day in advance and hit up the water parks, since my kid went through her first swim class this summer. During the conference, my wife and kid were able to play in the on premise parks and get over to a petting zoo and water show in the area. All in all, it wasn't just a great venue, it's a great location for family riding along. When my kid's older, she may wish to partake in the family track of That Conference, which looks to be pretty awesome.
  1368.  
  1369. #### Aside: Service Workers
  1370.  
  1371. The key ingredient in PWAs for me is the [service worker][service-worker-api]. A couple of days prior to That Conference kicking off, I had seen a screen shot of the in-development status of [service workers for WebKit][webkit-sw-status], meaning that the last major player is on its way to implementation; specifically Apple (MS was already in progress). The best part about PWAs is that they're "progressively" enhanced, hence the name. This means that once iOS fully adopts the support, PWAs, such as this very blog, will have the full sw capability readily available, with no changes necessary. Want to see it in action? You can load this blog in Firefox or Chrome (including on Android) and switch off your network connection (airplane mode or check offline in Chrome's DevTools) and you'll find some pages still load from cache and that a network error doesn't crash the whole site (web app). Web apps 1, offline dinosaur 0.
  1372.  
  1373. Want to keep track of service workers? Try [the 'is service worker ready?' site][is-sw-ready].
  1374.  
  1375. ### Work: aka the Day job
  1376.  
  1377. One other big change from this summer is that, for the last two months, I've started working for a new company. It's definitely a bit of a change in a couple of regards. I'm part of a decently sized team now, instead of being the solo developer on a tiny team of two. I'm working on a different platform that makes use of some of my front-end and Java backend skills, with some slated Node work that will play to my sense of direction. If that wasn't enough, I've started working from home as well. It's an interesting change with great benefits like not commuting 20+ minutes twice a day and nearly unlimited high quality coffee, but comes with the downside of a decreasing amount of interaction with other human beings. For now though, the dog will do 🐶.
  1378.  
  1379. ### Open Source Contributions
  1380.  
  1381. I have a few ideas. That's all I'm saying right now, since I've got my sights set on a few things which are going to take a bit of setup. Watch this space.
  1382.  
  1383. Until next time, cheers! 🍻
  1384.  
  1385. [that-conf-url]: https://www.thatconference.com/
  1386. [pwa-url]: https://developers.google.com/web/progressive-web-apps/
  1387. [service-workers-url]: https://webkit.org/status/#specification-service-workers
  1388. [kalahari-url]: https://book.kalahariresorts.com/wisconsin/
  1389. [webkit-sw-status]: https://webkit.org/status/#specification-service-workers
  1390. [fetch-api-url]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  1391. [service-worker-api]: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
  1392. [is-sw-ready]: https://jakearchibald.github.io/isserviceworkerready/
  1393. [mwlug-url]: https://www.mwlug.com/
  1394. ]]></content:encoded>
  1395.        </item>
  1396.        <item>
  1397.            <title><![CDATA[Open Source Contribution]]></title>
  1398.            <link>https://edm00se.io/open-source/contribution/</link>
  1399.            <guid>https://edm00se.io/open-source/contribution/</guid>
  1400.            <pubDate>Fri, 16 Jun 2017 00:00:00 GMT</pubDate>
  1401.            <description><![CDATA[learning by doing more and playing nice with others]]></description>
  1402.            <content:encoded><![CDATA[
  1403. ### Intro
  1404.  
  1405. It's time to clear some of the backlog. I started this post a few months back and it should probably be sent on its way to clear the pile of drafts I haven't finished yet... 🤔
  1406.  
  1407. I have a bit of a passion for open source software. My preferred distribution of Linux has been Ubuntu since 4.10, the Warty Warthog (I was even a minor contributor on a short lived, wildly popular project that aimed at improving the Ubuntu experience early on), I've enjoyed most open source projects I've run into, and I thoroughly enjoy supporting open source projects. Recently I had an opportunity to learn a little more than I knew prior to, by being nudged to submit a pull request to a project I'm interested in.
  1408.  
  1409. ### The Project
  1410.  
  1411. The project is [prettier-eslint's cli tool](https://github.com/kentcdodds/prettier-eslint-cli), to which I added a (simple) logging option `--silly-logs`. This came about while running a requested test from Mr. Kent C. Dodds, while the project was still in beta (pre-release). Why am I celebrating a CLI flag for a cli tool? I'm not, I'm more geeking out over the tool itself instead of my minor partial involvement. That's a part of the beauty of open source, anyone can contribute or help, even with something as small as testing out a beta version.
  1412.  
  1413. https://twitter.com/kentcdodds/status/821767817569583104
  1414.  
  1415. #### Issue
  1416.  
  1417. This led to an observation that while I was getting the benefits of [prettier](https://npm.im/prettier), it was inducing a dangling comma, making it fail my [eslint](https://npm.im/eslint) configuration (which explicitly disallows them). While I could reconfigure my eslint config to accommodate for and/or use a dangling comma, that sort of defeats the purpose of plugging into an existing eslint config. The whole point of Mr. Dodds's [`prettier-eslint`](https://npm.im/prettier-eslint-) and its cli tool, [`prettier-eslint-cli`](https://npm.im/prettier-eslint-cli) which I was testing, was to provide a more seamless integration with the two.
  1418.  
  1419. ![prettier working, just not playing great with eslint](./images/PrettierEslint_DanglingComma.png)
  1420. *prettier working, just not playing great with eslint*
  1421.  
  1422. #### Prettier?
  1423.  
  1424. What is [prettier](https://npm.im/prettier)? It's a tool for consistently formatting JavaScript, including support for modern syntaxes, in an opinionated fashion. I liken it to how I have My Eclipse and DDE settings, so that my XML attributes get forcibly formatted onto separate lines. In the end, it's supposed to help make your code more readable and less confusing. ð\x9f\x8e\x89
  1425.  
  1426. ### The Pull Request
  1427.  
  1428. Since it was requested, and it was something I thought would help me learn a little and potentially be of interest to others, I [followed Kent's recommendation](https://github.com/prettier/prettier-eslint-cli/pull/1#issuecomment-273576020) and submitted a pull request (PR) to add the `--sillyLogs` option. Did it go perfectly the first time? Not at all! That's a sure sign in my book that I'm learning; I was mildly out of my comfort zone and trying something new. In fact, since this time frame, I've gone on to find a true appreciation for [jest](https://npm.im/jest) over [mocha](https://npm.im/mocha) for my Node test runner and framework; it's incredibly slick and has lots of great benefits, and I found it thanks to this PR and Kent's use of, and recommendations of, it.
  1429.  
  1430. Since this project was a CLI tool consuming a "regular" Node module (API), so the logging option I added was under [a dependent repo's (PR #8 for `prettier-eslint`)](https://github.com/prettier/prettier-eslint/pull/8).
  1431.  
  1432. ### The Results
  1433.  
  1434. This speaks for itself. Once the logging was established, some modifications were made in the `-cli` project, and now it plays great with my project's eslint config. That's a big win, in my opinion. Here it is, doing what it does w/ the same section of my code, acting as I want and cleaning things up for me.
  1435.  
  1436. ![prettier working, and respecting the h\*ck out of my eslint config](./images/PrettierEslint_WorkingGreat.png)
  1437. *prettier working, and respecting the h\*ck out of my eslint config*
  1438.  
  1439. ### Shout Out
  1440.  
  1441. Now that it has hit version 1.0, the cli tool works great! It formats my code using `prettier` and triggers an `eslint --fix`, making my `eslint` config win in the end, with the formatting assist from `prettier`. It helps make for some good looking code, so you may wish to check it out. There's also [a package for Atom](https://github.com/kentcdodds/prettier-eslint-atom) and for [VS Code](https://marketplace.visualstudio.com/items?itemName=RobinMalfait.prettier-eslint-vscode).
  1442.  
  1443. ### Want to Get Involved?
  1444.  
  1445. My suggestion, especially if you're new at it, is to follow a small handful of developers on Twitter or GitHub; preferably ones with interesting projects that sound useful and innovative to you. Watch, read, learn, listen. When there's an opportunity to help, go for it. My only words of caution, don't commit to more than you can do and if you need help, ask for it. You'll `<sarcasm>` never guess who one of the developers I follow is `</sarcasm>` 🙄. In fact, one of the reason I follow Kent is that he actively creates, maintains, and contributes to projects that help people contribute more; in my estimation, that's one hallmark of a truly good person in the open source world. In fact, he's even got a (free) series on [egghead.io](https://egghead.io) that is helpful for such beginners, for any interested:
  1446.  
  1447. > [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
  1448.  
  1449. ### Closing Thoughts
  1450.  
  1451. This is far from my first open source contribution, and it certainly won't be my last, but what stood out to me was that I was more assistive in being aware and willing to test something that just hacking away on my own side projects. Don't get me wrong, those have plenty of usefulness in their own right, but when we interact with others, we learn how they do things, and that can help us change and grow our own habits, processes, and traits.
  1452.  
  1453. Until next time, cheers!
  1454.  
  1455. ### 🍻
  1456. ]]></content:encoded>
  1457.        </item>
  1458.        <item>
  1459.            <title><![CDATA[Docker Quick Tips]]></title>
  1460.            <link>https://edm00se.io/docker/quick-tips/</link>
  1461.            <guid>https://edm00se.io/docker/quick-tips/</guid>
  1462.            <pubDate>Fri, 28 Apr 2017 00:00:00 GMT</pubDate>
  1463.            <description><![CDATA[containers for life]]></description>
  1464.            <content:encoded><![CDATA[
  1465. ### Docker
  1466.  
  1467. If you have been living under a rock, Docker is pretty much amazing. If you haven't been living under a rock, you may be getting used to the idea of Docker, but still have the occasional question. I've found myself using Docker in increasing amounts and complexity over the last year or so. I've recently decided to start recording some of the tasks I've found useful, some of which may be less familiar to a beginner. If you're so inclined, check out the playlist, embedded here.
  1468.  
  1469. https://www.youtube.com/watch?v=47FfdVddCFA&list=PLk_BgI9qpsGjVwLPqBpVKqA3M4Sdv8wlw
  1470.  
  1471. [View this playlist on YouTube.com][docker-tips-playlist]
  1472.  
  1473. #### Requests?
  1474.  
  1475. If there's anything you'd like to see covered (and it's something I already have a solution to), feel free to ask about it.
  1476.  
  1477. <div class="center">
  1478.  <iframe width="500"
  1479.    height="540"
  1480.    frameborder="0"
  1481.    src="https://docs.google.com/forms/d/e/1FAIpQLSfb3Pbhxs9KRcs8UpT4XJE4bTj0gJeZ1Ay9F57xt6KYtU_aJA/viewform?embedded=true"
  1482.    style="background-color:white;">
  1483.  </iframe>
  1484. </div>
  1485.  
  1486. [docker-tips-playlist]: https://bit.ly/EricDockerQuickTips]]></content:encoded>
  1487.        </item>
  1488.        <item>
  1489.            <title><![CDATA[Notes in 9: Dev Tools Grab Bag]]></title>
  1490.            <link>https://edm00se.io/self-promotion/ni9-node-tools-grab-bag/</link>
  1491.            <guid>https://edm00se.io/self-promotion/ni9-node-tools-grab-bag/</guid>
  1492.            <pubDate>Tue, 04 Apr 2017 00:00:00 GMT</pubDate>
  1493.            <description><![CDATA[a pair of Node-based tools for use with Domino + XPages]]></description>
  1494.            <content:encoded><![CDATA[
  1495. ### Intro
  1496.  
  1497. I'm on Notes in 9 again, with a "grab bag" of a couple of tools I've put together recently that may be of a varying degree of useful for other Domino + XPages developers. You don't _need_ these to do development, but for the right person, they may help with their development workflow.
  1498.  
  1499. Also of note, with the upgrade to Swiper with the FP8 release of Notes + Domino Designer, the limitations previously mentioned are no longer there! This means that my second tool I talked about, `node-dora(-cli)`, may be even more niche, but hey, it was a good learning experience.
  1500.  
  1501. ### The Video
  1502.  
  1503. Head over to [Notes in 9.com episode #205](https://www.notesin9.com/2017/04/04/notesin9-205-leverage-domino-development-with-new-tools/) to check it out, or watch it embedded here or on YouTube.
  1504.  
  1505. https://www.youtube.com/watch?v=fR4z2VpAoWo
  1506.  
  1507. ### Summary
  1508.  
  1509. I'm guessing that a number of people will regard these tools as a bit of a novelty, but I hope some will find them pretty awesome, or at least useful. They don't exactly do anything a developer couldn't do on their own, but they fit into development workflow for speedier development; at least, that's the idea. I'm open to contributions of nearly any kind, so please feel free to get involved.
  1510.  
  1511. For anyone looking to reference my notes from the screencast, you can find that below.
  1512.  
  1513. ### Ni9: Some Tools for Your Toolbox
  1514.  
  1515. #### 1. Intro
  1516.  
  1517. - who am I?
  1518. - the [argument for automation](https://medium.com/@kentcdodds/an-argument-for-automation-fce8394c14e2)
  1519. - contact info
  1520.  
  1521. #### 2. generator-xsp
  1522.  
  1523. ##### Required
  1524.  
  1525. - [node](https://nodejs.org/) (+ npm)
  1526.    - recommend [nvm](https://github.com/creationix/nvm/blob/master/README.markdown#installation), [n](https://github.com/tj/n#readme), or [nodist](https://github.com/marcelklehr/nodist#readme)
  1527. - [yo](https://yeoman.io/) + [generator-xsp](https://github.com/edm00se/generator-xsp)
  1528.  
  1529. ##### Goal
  1530.  
  1531. 1. set up app from scratch
  1532. 2. config or existing app's ODP
  1533. 3. create elements from sub-generators
  1534.    - XPage
  1535.    - Class
  1536.    - managed bean (configured in `faces-config.xml`)
  1537.    - "rest", which creates an `xe:restService` control, using an `xe:customRestService` with `CustomServiceBean` to back the logic
  1538.  
  1539. ##### Result
  1540.  
  1541. An ODP you can import into DDE.
  1542.  
  1543. - an arbitrary task, common to all modern developers, including XPages developers
  1544. - if you need a quick refresher, or a crash course, watch [Notes in 9 ep #131](https://www.notesin9.com/2013/11/12/notesin9-131-use-sourcetree-for-better-xpages-source-control/), and the specifics of importing from source control (git/hg, etc.) start at: 34m 03s
  1545.  
  1546. ##### Collaboration
  1547.  
  1548. - contributors welcome!
  1549. - big or small!
  1550.  
  1551. #### 2. node-dora
  1552.  
  1553. - a node package wrapper for [dora](https://github.com/camac/dora)
  1554. - install for use as an npm script via the [dora-cli](https://github.com/edm00se/node-dora-cli) package
  1555.  - includes the `node-dora` package from npm
  1556.  - wraps for cli invocation
  1557.  - _can_ be installed globally
  1558.  - ensures dora cleaning, w/ npm deps, great for CI environments
  1559. - can set in your `package.json`'s "scripts" block, such as `"clean": "dora 'My Amazing ODP'"`
  1560.  
  1561. #### 3. Summary
  1562.  
  1563. Automation, like source control, can "save your life".
  1564.  
  1565. Questions: [AMA](https://github.com/edm00se/ama)
  1566. ]]></content:encoded>
  1567.        </item>
  1568.        <item>
  1569.            <title><![CDATA[Custom JSON Serialization With GSON]]></title>
  1570.            <link>https://edm00se.io/java/custom-json-serilization/</link>
  1571.            <guid>https://edm00se.io/java/custom-json-serilization/</guid>
  1572.            <pubDate>Mon, 23 Jan 2017 00:00:00 GMT</pubDate>
  1573.            <description><![CDATA[forcibly preventing scientific notation?]]></description>
  1574.            <content:encoded><![CDATA[
  1575. ### Intro
  1576.  
  1577. Here's a curious one, in which I found myself with a limitation of not being able to output JSON with scientific notation values.
  1578.  
  1579. ![wait, what?](./images/ExcuseMe.gif)
  1580.  
  1581. If you're wondering why that is, since both JSON and JavaScript allow scientific notation of number values, you are absolutely correct and that's a great question. The strange thing was that I found myself outputting perfectly valid JSON to be consumed by something specific which didn't allow scientific notation. I'm not entirely sure why that was a bridge I had to cross, but the path of least resistance involved my forcing the output values to be serialized in standard notation.
  1582.  
  1583. ### Serializing JSON With GSON
  1584.  
  1585. Normally, serialization of JSON via GSON is so simple, a developer with merely a basic understanding of Java could pull it off 😜. Here's a sample for baseline comparison.
  1586.  
  1587. https://gist.github.com/edm00se/095626db6513c4bf01cadeff92935094#Generic.java
  1588.  
  1589. So, since the formatting of the numeric values, which I was passing into a `Map` as `Double`s for serialization, we'll be registering a new `TypeAdapter` with the `JsonSerializer` and instruct it how to handle the value in place of the default. Here's the result, wrapped into a method in a utility class.
  1590.  
  1591. https://gist.github.com/edm00se/095626db6513c4bf01cadeff92935094#Utils.java
  1592.  
  1593. Now that we have a new way of handling our `Gson` instantiation, we can use it as such:
  1594.  
  1595. https://gist.github.com/edm00se/095626db6513c4bf01cadeff92935094#New.java
  1596.  
  1597. ### Summary
  1598.  
  1599. I'm still left questioning why I had to do this, but the exercise is an intersting one anyway. Should someone need to customize their responses even further, this is a great place to start from. I'm still a pretty big fan of GSON and while it may not be for everyone, or even the best option in OSGi plugin development with options like JAX-RS and others, it's certainly a heavily used library that is both useful and generally easy to implement.
  1600.  
  1601. ### 🍻
  1602. ]]></content:encoded>
  1603.        </item>
  1604.        <item>
  1605.            <title><![CDATA[Recapping 2016]]></title>
  1606.            <link>https://edm00se.io/self-promotion/recapping-2016/</link>
  1607.            <guid>https://edm00se.io/self-promotion/recapping-2016/</guid>
  1608.            <pubDate>Mon, 16 Jan 2017 00:00:00 GMT</pubDate>
  1609.            <description><![CDATA[a retrospective and ideas for the future]]></description>
  1610.            <content:encoded><![CDATA[
  1611. ### Intro
  1612.  
  1613. Per usual, I've had a little break between things and decided to catch up with a bit of a summary of some recent things that each didn't necessitate their own post.
  1614.  
  1615. ### 2017 IBM Champion
  1616.  
  1617. For starters, I'm honored to be named [an IBM Champion in Collaboration Solutions (/ Social Business)](https://www.ibm.com/developerworks/community/blogs/762e655e-e86c-4624-9662-ee81c6874de1/entry/Introducing_the_2017_IBM_Champions) for the third time. This would be a [hat trick](https://en.wikipedia.org/wiki/Hat-trick) in (ice) hockey 🏒. I'm happy to be recognized with a group of people, developers and more, who are passionate about both their work and the platforms they work on. There are 123 people named for 2017, 44 of whom are new (79 returning).
  1618.  
  1619. For 2017 I have a new distinction in which I'm happy to be named an IBM Champion for Cloud 🎉.
  1620.  
  1621. ![So I've Got That Going for Me, Which is Nice](./images/GotThatGoingForMe.jpg)
  1622.  
  1623. ### IBM Connect
  1624.  
  1625. The only big downer in my life right now is that I won't be attending IBM Connect this year. It was a great time for the last two years for me and I hope to attend again. I do believe that there's an interesting bit of initiative on IBM's part that the change in locale involves heading to San Francisco, which is home to a great amount of the emerging side of technology. It should be a great time and I'm sorry to be missing out on seeing familiar faces and presenting on one of my favorite topics. With any luck, I may be back to it next year and hope everyone has a great time.
  1626.  
  1627. ### 2016 Year in Review
  1628.  
  1629. I feel compelled to hold off on my "year in review" style posts until after the new year, mostly on account of not wanting to engage in too much self-aggrandizing my personal efforts.
  1630.  
  1631. - [answered 11 questions on Stack Overflow](https://stackoverflow.com/search?q=user%3A1720082+created%3A2016)
  1632.  - including on options for [creating a REST endpoint with Domino/XPages](https://stackoverflow.com/questions/36062424/basic-rest-service-for-my-xpage-application/36064707#36064707)
  1633. - blogged 29 times
  1634.  - a task runner centric implementation of a modern front-end application running on Domino/XPages
  1635.  - using Docker with SonarQube
  1636.  - integrating the core xslt transforms from [DORA](https://github.com/camac/dora) into gulp
  1637.  - writing my first npm module (the first of [a few](https://www.npmjs.com/~edm00se))
  1638.  - working with [headless Domino Designer](https://www-10.lotus.com/ldd/ddwiki.nsf/dx/Headless_Designer_Wiki) with Jenkins CI
  1639.  - writing a [Node.js](https://nodejs.org/en/) [app to connect to _virtually anything_](https://github.com/edm00se/express-app-fun) as a data back-end
  1640.  - extending that [app structure to substitute in an iSeries / DB2 back-end via JDBC](https://github.com/edm00se/express-app-fun/tree/iseries)
  1641.  - extending that [app a different way to make use of a Notes/Domino NSF's db](https://github.com/edm00se/express-app-fun/tree/domino), via [Nils Tarjei Hjelme's domino-nsf node module](https://github.com/nthjelme/nodejs-domino)
  1642.  - git, on various use cases
  1643.  - renewing Let's Encrypt certificates in an IBM Bluemix application
  1644.  - getting started with learning Angular (2)
  1645.  - and I even found time to overhaul the format of my blog
  1646. - published two videos on Notes in 9
  1647.  - [#189: SonarQube in Docker](https://www.notesin9.com/2016/02/24/notes-in-9-189-introduction-to-sonarqube-with-a-side-of-docker/)
  1648.  - [#191: A Beard, An App, A Blender](https://www.notesin9.com/2016/06/28/notesin9-191-a-beard-an-app-and-a-blender/)
  1649. - presented [at IBM Connect](https://github.com/edm00se/BeardAppBlender#readme)
  1650. - presented [at ICON US](https://github.com/edm00se/beer-debt-mk2#readme) with [Shean McManus](https://twitter.com/sheanpmcmanus)
  1651. - presented [at MWLUG](https://github.com/edm00se/BP101-A-Modernized-Developer-Workflow-With-Domino-and-XPages#readme)
  1652.  
  1653. I also started, then held back, a project which didn't receive enough attention from me, making it be delayed until I feel ambitious again; I'm sure it will come back, but once it fits in with my timing again. [I don't have enough time to do _everything_](https://github.com/edm00se/ama/issues/1).
  1654.  
  1655. ### Recent News
  1656.  
  1657. For developers, if you're curious as to such things, [githubreportcard.reflect.io](https://githubreportcard.reflect.io/) was recently published, making for an intriguing look at a person's GitHub contributions.
  1658.  
  1659. [Wes Bos](https://wesbos.com/) recently released his [javascript30.com](https://javascript30.com/) course for free, which after the first week has been pretty good. I recommend it to anyone looking to keep their JavaScript skills sharp or are looking to learn a little bit from how another experienced JS dev does things.
  1660.  
  1661. #### [generator-xsp](https://github.com/edm00se/generator-xsp)
  1662.  
  1663. As far as other news goes, I've gotten my first [Yeoman generator](https://yeoman.io/) to version 1.0. I'll have more information on it before too long, but the short version is that it can help a developer scaffold an XPages compatible On Disk Project for an application, generate XPages or Custom Controls, Java Classes, including as a managed bean, and the last feature added for v1.0 release was the inclusion of the ability to scaffold a `CustomServiceBean` for use by an `xe:restService` control.
  1664.  
  1665. [![NPM badge](https://nodei.co/npm/generator-xsp.png?downloads=true&downloadRank=true&stars=true){.skinny}](https://npmjs.org/package/generator-xsp)
  1666.  
  1667. [Contributions are welcome](https://github.com/edm00se/generator-xsp/blob/master/CONTRIBUTING.md#contributing)! I have a basic [road map](https://waffle.io/edm00se/generator-xsp) for the immediate future, but I am open to suggestions, contributions, and even just discussion. If you're looking to help but don't know where to get started, please hit me up [on Twitter](https://twitter.com/edm00se) or in the [XPages Slack chat](https://twitter.com/edm00se).
  1668.  
  1669. #### Another Generator
  1670.  
  1671. Another yeoman generator I put together (finally) was of a genericized version of the slide deck I used for my Chalk Talk from IBM ConnectED 2015. It's called [`generator-presto-preso`](https://github.com/edm00se/generator-presto-preso) and while I'll likely continue to make some minor improvements, Pull Requests are welcome.
  1672.  
  1673. ### What's Next?
  1674.  
  1675. I'm currently planning on a couple of follow-up features to `generator-xsp` and fully expect to record a video for [Notes in 9](https://www.notesin9.com/) on it before long. I also am planning on a revisit to my [App of Ice and Fire](https://github.com/edm00se/AnAppOfIceAndFire) for a short series on updating an application's front-end to a modern build process with Domino applications. After all that, we'll see what the year brings!
  1676.  
  1677. ### [AMA](https://github.com/edm00se/ama#readme)
  1678.  
  1679. If you have other questions on things, feel free to [Ask Me (Nearly) Anything](https://github.com/edm00se/ama#readme) in my AMA repo on GitHub.
  1680.  
  1681. ### 🍻
  1682.  
  1683. [npm-url]: https://npmjs.org/package/generator-xsp
  1684. ]]></content:encoded>
  1685.        </item>
  1686.        <item>
  1687.            <title><![CDATA[Rebirth: An App of Ice and Fire]]></title>
  1688.            <link>https://edm00se.io/xpages/rebirth-an-app-of-ice-and-fire/</link>
  1689.            <guid>https://edm00se.io/xpages/rebirth-an-app-of-ice-and-fire/</guid>
  1690.            <pubDate>Wed, 14 Dec 2016 00:00:00 GMT</pubDate>
  1691.            <description><![CDATA[refactoring the build process to better the app]]></description>
  1692.            <content:encoded><![CDATA[
  1693. ### Intro
  1694.  
  1695. If you read my blog for any of the [Saga of Servlets](https://edm00se.io/servlet-series/) series, then I hope that you're excited I'm returning to the application I put together for it. This time, it's as a conversation piece in regards to some of the build process modernization I engaged in recently, in order to unify the code base in [its git repository](https://github.com/edm00se/AnAppOfIceAndFire). In any case, it's helping pave the way forward before I update some of the back-end elements, when it will again be a talking point for some additional rework and optimizations.
  1696.  
  1697. ### Rebirth
  1698.  
  1699. When my App of Ice and Fire (viewable here: [iceandfire.mybluemix.net](https://iceandfire.mybluemix.net) on Bluemix) was last left off, it had an [Angular 1.x](https://angularjs.org/) application as the front-end, with a [gulp](https://gulpjs.com/) driven build process to optimize all the front-end assets, and was backed by some excellent servlets (extending `DesignerFacesServlet`) which were talking points for the previous entries in the series, which focused more on general premise of servlet construction and application structure with XPages and Domino. Sadly, the application was left to sit for several months, which was approaching the better part of a year now. During this time, several new frameworks came out; specifically the release and popularity of [React](https://facebook.github.io/react/), [Angular 2](https://angular.io/)'s release, and my current favorite, [vue.js](https://vuejs.org/), not only started gaining popularity, but hit its version 2.0 as well. Additionally, I got on the bandwagon of switching to using npm scripts as the primary build pipeline; an approach that makes the dependencies, builds, and build-related actions all rather self-contained and neat. In other words, it was time for something new.
  1700.  
  1701. [![Hyrkoon the Hero, with Lightbringer in hand](./images/Hykroon_Lightbringer.jpg)
  1702. *Hyrkoon the Hero, with Lightbringer in hand*](https://awoiaf.westeros.org/index.php/Lightbringer)
  1703.  
  1704. ### [What's New](https://github.com/edm00se/AnAppOfIceAndFire/releases/tag/v1.0.0)
  1705.  
  1706. - npm scripts are incorporated for build pipeline, with gulp for build process (for now at least)
  1707. - re-incorporation of the multiple project repositories (the Bluemix implementation had previously been its own repo)
  1708.  - the Bluemix version is now in its own branch, `bluemix`
  1709. - the src/ directory for the front-end application is shared between branches
  1710.  - with an npm script to copy from `master` into the `bluemix` branch
  1711.  - this paves the way for sharing the Java codebase between the `master` and `bluemix` branches, to follow on
  1712. - the front-end (UI layer) app is its own branch, for use as a git subtree in the other versions (for single-source development)
  1713. - npm script included as part of install to fix font-awesome, specifically to remove the version number, no more failing CSS references in XPages
  1714. - no more symlinking! it was confusing to those who didn't know it and wasn't strictly necessary, so it was removed
  1715.  
  1716. ### Build Pipeline
  1717.  
  1718. The largest changes, from the front-end perspective, are in the implementation of the front-end assets and the build pipeline that includes them for use. For a break down, here's a quick description of my npm scripts, which can be [viewed in full here](https://github.com/edm00se/AnAppOfIceAndFire/blob/v1.0.0/package.json#L6-L18).
  1719.  
  1720. https://gist.github.com/edm00se/b9a046d3f167228b24278e759700f1bd#abbreviated-package.json
  1721.  
  1722. You can see the `postinstall` script, which runs immediately after `install`; so when a user runs `npm install` and pulls in the dependencies, it will be kicked off immediately following it, without need for the user's intervention. One major thing of note is that while bower is still in use, this makes it more seamless, unobtrusive, and easy to switch to an alternative solution some day.
  1723.  
  1724. The `fix-fa` scripts perform the removal of both the `?v=4.7.0` (the currently specified version) and `?#iefix&v=4.7.0`, which have been known to cause issues with being loaded from XPages. They are split apart into `fix-fa:ver` and `fix-fa:ie`, run together by the "parent" task. Of note, I had to escape both the special characters and also the escape character, as that's how the [replace](https://npm.im/replace) tool I implemented needs it.
  1725.  
  1726. The `build` and `build:watch` tasks both do what you would expect, but now [my `gulpfile.js`](https://github.com/edm00se/AnAppOfIceAndFire/blob/v1.0.0/gulpfile.js) is considerably cleaner, with _only_ the build specifics defined as gulp tasks.
  1727.  
  1728. The most complex npm script is the `dev` script, which:
  1729.  
  1730. - runs [`json-server`](https://npm.im/json-server) as [previously outlined in my blog](https://localhost:4000/front-end/alternate-front-end-development/)
  1731. - starts [a `browser-sync`](https://npm.im/browser-sync) instance for live-reloading and watching of the built app
  1732. - performs a build and watches the source files in `src/` via the `build:watch` (which is the default `gulp` task)
  1733.  
  1734. Aside from being a bit of a populist move at the moment, it's good in that you can boil down your npm scripts to just a small handful of truly worthwhile scripts. A user can generally remember that `npm run dev` will run the watch and live-reload environment, with `npm run build` being the one-off build task, and `npm run clean` the clean command to remove the built assets. If nothing else, they're more easily documented in [a project's ReadMe file](https://github.com/edm00se/AnAppOfIceAndFire/blob/v1.0.0/ReadMe.md#use).
  1735.  
  1736. There is absolute benefit to being able to make use of both the npm ecosystem for the various packages to perform the required tasks involved. I didn't have to write my own cross-environment supported script to do any of what goes on in the npm scripts, let alone something as complex as [`concurrently`](https://npm.im/concurrently), which manages the task of performing simultaneous commands or npm scripts. Anything I can run from the host (and require as a dependency) from the CLI is available to be used. This is quite powerful and, once properly configured, should seem somewhat straightforward.
  1737.  
  1738. #### What's Missing?
  1739.  
  1740. In the future, I have plans to refactor the front-end portion of the app, along with addressing some of the concerns such as my copying _all_ the library assets into `ODP/WebContent/libs/`, which is inelegant. This should set up a blog post or two covering these topics. All in all, it's a rather usable scenario at the moment.
  1741.  
  1742. ### How to Check it Out
  1743.  
  1744. [The repository itself](https://github.com/edm00se/AnAppOfIceAndFire) is the best place to have a look. You can also find [the `v1.0.0` release information](https://github.com/edm00se/AnAppOfIceAndFire/releases/tag/v1.0.0) a decent place to look.
  1745.  
  1746. ### Summary
  1747.  
  1748. This may just be a node fan's nerd-ing out, but I think it shows how to improve the build pipeline for a given application over time, to make it more manageable and easily run by the user/developer. I'm sure I'll come back to this app in the future, so stay tuned, as there are good things ahead.
  1749. ]]></content:encoded>
  1750.        </item>
  1751.        <item>
  1752.            <title><![CDATA[Scripting Server Upgrades]]></title>
  1753.            <link>https://edm00se.io/docker/scripting-server-upgrades/</link>
  1754.            <guid>https://edm00se.io/docker/scripting-server-upgrades/</guid>
  1755.            <pubDate>Fri, 11 Nov 2016 00:00:00 GMT</pubDate>
  1756.            <description><![CDATA[GitLab running in Docker and the never-ending case for automation]]></description>
  1757.            <content:encoded><![CDATA[
  1758. ### Intro
  1759.  
  1760. This one might be slight departure from my usual, but those that have followed my blogging this past year will have noticed a bit more of a leaning towards DevOps in some of my posts. This echoes a lot of what I've been concluding as increasingly a necessary part of _development_; that we need to consider a picture large enough to encompass the themes surrounding development functions and, like any good developer (DRY ~= "lazy"), automate the heck out of it.
  1761.  
  1762. <iframe class="tweetbu" src="https://tweets.edm00se.codes/796390523032498176/">failed to load</iframe>
  1763.  
  1764. ### Overview
  1765.  
  1766. I had [previously blogged](/scm/self-hosting-scm-server/) on some of the scm hosting options available, both external and self-hosted options, and my personal take on a few of them. I've generally gone the direction of being increasingly git-centric, which has led me to implement a GitLab instance at work. I had previously run GitLab but then switched off, as I ran into an issue that seemed to be a corrupted data store from back around the version 7.10-ish timeframe. I had also been running it natively on an Ubuntu host. When I finally came back to GitLab, this time for at work, I decided that manually installing via the scripted omnibus package just _wasn't for me_; some of what I remember is that I had to jump through hoops on configuration, fighting dependencies with the Ubuntu repositories, and a surprising amount of upkeep just to upgrade. From what I've heard from others since then, it sounds to have smoothed out a bit, or my experience was atypical; always a likely explanation in my case, it would seem.
  1767.  
  1768. #### Docker
  1769.  
  1770. Another of my recent infatuations is with containerization, specifically Docker. The best part about this new GitLab server we stood up at work is the fact that my admin and I decided to roll it in a docker instance. This was meant to preclude some of the issues I had run into and provide an initial deploy of our first Docker-ized app, for hopefully an easier to upgrade process. This has held true and paid off several times over. In fact, I've graduated from merely copying and pasting my `docker run` command into wrapping it into an `upgrade.sh` shell script, so as to prevent the possibility of my mistakenly not copying or pasting correctly. This has been a great win, especially as the GitLab team is hard at work with new features and plenty of security patches coming out seemingly weekly, or faster.
  1771.  
  1772. ### Why Script Something Already Short?
  1773.  
  1774. In all honesty, if you've heard of the concept of "configuration over code", my take on it is that we should go out of our way spend a little bit of programming effort making our code more easily configured. This may or may not match up exactly with any coding ideologies, but the important take-away is that this is all an [argument for automation](https://medium.com/@kentcdodds/an-argument-for-automation-fce8394c14e2). Automated, or scripted, processes mean not just a reduction in time spent, but also a decrease in likelihood of human error and provides a learning opportunity for those embracing the process.
  1775.  
  1776. ### Details
  1777.  
  1778. The GitLab instance I've settled on is not far off of [the example docker command given on the GitLab site](https://docs.gitlab.com/omnibus/docker/#run-the-image). What is different is a couple of specifics relating to our HTTPS certificates. Here's the genericized version of the docker command at the heart of things.
  1779.  
  1780. #### The Docker Command
  1781.  
  1782. https://gist.github.com/edm00se/764c067ec485f5cf1d1518f71dd2d029#docker_cmd.sh
  1783.  
  1784. For those wondering, the published ports are binding the exposed ports (80 and 443) on alternate bound ports to the host OS. This then runs through a host OS [Nginx](https://nginx.org/) reverse proxy. You can note that the executed instance is `--detach`ed, meaning not bound to the immediate shell session (runs in the background), `--restart always` is set so that it will start when the host OS is restarted (and the docker daemon starts up). Additionally, you can see our `--volume` mount points which bind the local file system (in `/srv/gitlab/`) to various directories contained within the docker container; these give persistence, as the upgrade process actually removes, as in deletes, the existing instance entirely, then is created again new, just quickly and consistently enough to not realy seem "new". It also specifies that we're going to use [the "latest" corresponding tag for gitlab-ce (in the gitlab published space) from Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ce/).
  1785.  
  1786. #### The BASH Script
  1787.  
  1788. https://gist.github.com/edm00se/764c067ec485f5cf1d1518f71dd2d029#upgrade.sh
  1789.  
  1790. Even if you aren't accustomed to writing or reading shell scripts, you should be able to follow this pretty easily. For the most part, you can see I'm setting some variables such as the `CONTAINER_NAME` (gitlab), `DOCKER_SOURCE` (gitlab), and `VERSION` (latest). Otherwise the progression is generally to the effect of:
  1791.  
  1792. - pull the latest tag from Docker Hub (done while existing container is running, no harm done)
  1793. - existing container stopped and removed
  1794. - new version of container (from newly pulled tag) is started up in the same fashion, inheriting data (PostgreSQL), log folders, config, and HTTPS certificates
  1795.  
  1796. I even included a helpful reminder at the end of the script to watch for the migration task, which consumes a fair amount of the allowed CPU for a minute or two.
  1797.  
  1798. ### Summary
  1799.  
  1800. All in all, neither the task nor the scripted solution were terribly difficult, but it puts things at a high level of consistency and makes it so easy, that virtually anyone with SSH access to the host server could do the upgrade with virtually no knowledge of either docker or GitLab. To demonstrate how easy this all is, I even went so far as to snag a recording on [asciinema.org](https://asciinema.org), but it's mostly just waiting for progress bars to complete, so I'm not embedding it here.
  1801.  
  1802. I hope this may spark some interest for someone out there. I've had nothing but great experiences with running GitLab in a docker container, including the couple (incredibly rare) occasions that something went wrong (generally a permissions thing, easily solved with the right command) as being able to roll back a version is pretty darned easy; one can merely replace "latest" with the previous tag as listed [on Docker Hub for GitLab](https://hub.docker.com/r/gitlab/gitlab-ce/tags/) and re-run the `upgrade.sh` for a reversion. If anyone has further questions, let me know. 🍻
  1803. ]]></content:encoded>
  1804.        </item>
  1805.        <item>
  1806.            <title><![CDATA[Everything Old is New Again]]></title>
  1807.            <link>https://edm00se.io/admin/reincarnation/</link>
  1808.            <guid>https://edm00se.io/admin/reincarnation/</guid>
  1809.            <pubDate>Tue, 25 Oct 2016 00:00:00 GMT</pubDate>
  1810.            <description><![CDATA[reincarnating the blog for performance and readability]]></description>
  1811.            <content:encoded><![CDATA[
  1812. ### Intro
  1813.  
  1814. Every so often, it's good to reassess one's position. This is good from both a standpoint of being inquisitive and even interrogative, but when it comes to the ever changing landscape of the front-end development space, it's not only inevitable, but must be embraced for what feels the need to "stay afloat". I'm changing theme of my blog, hopefully for the better. The previous theme was good and did a great job of getting things started, but while I had forked a copy of a [good source theme](https://github.com/hmfaysal/hmfaysal-omega-theme), I wound up tinkering a fair amount and giving it quite a few customizations. Over time, things grew and I found myself spending some time on the side trying to optimize the blog performance.
  1815.  
  1816. ### Why?
  1817.  
  1818. The largest reasons for the change in blog theme are for:
  1819.  
  1820. 1. performance
  1821. 2. readability
  1822. 3. the fun of it
  1823.  
  1824. The new theme I've adopted (and customized a fair amount already), [amplify for Jekyll](https://github.com/ageitgey/amplify), is highly performant and makes use of the [AMP Project](https://www.ampproject.org/learn/about-amp/) (Accelerated Mobile Pages). This wasn't what I set out to do, but I was looking for a fast, elegant, and lightweight delivery for modern content, with a high focus on mobile-first design. I believe the amplify theme achieves this, and the AMP components are surprisingly quite decent to work with. I'm still toying with a few things and making them more "AMP normal", but for the most part, my page should even be able to load via the Google AMP CDN for some pretty darned fast load times. The constraints set by the AMP Project also mean that the forced structure of the components should mean it will load quite fast, regardless, which is a good thing.
  1825.  
  1826. This isn't born purely of a desire to have an even higher [PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/) ranking, but I have managed to settle on a theme that looks and reads well, focuses on the content, and eliminates a lot of the bloat of some of the jQuery driven effects that were really just window dressing.
  1827.  
  1828. ### Future
  1829.  
  1830. I've got a couple of things cooking in the development space, this is merely a pit stop along the way. Until next time, 🍻
  1831. ]]></content:encoded>
  1832.        </item>
  1833.    </channel>
  1834. </rss>

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//edm00se.io/rss.xml

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda