<?xml version="1.0" encoding="UTF-8"?><rss 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" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Mark's Blog]]></title><description><![CDATA[Random thoughts]]></description><link>https://mark.bahnman.ca/</link><image><url>https://mark.bahnman.ca/favicon.png</url><title>Mark&apos;s Blog</title><link>https://mark.bahnman.ca/</link></image><generator>Ghost 4.8</generator><lastBuildDate>Tue, 10 Mar 2026 09:32:21 GMT</lastBuildDate><atom:link href="https://mark.bahnman.ca/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA['Don't break the build!' or 'The dangers of approximating version numbers in dependencies']]></title><description><![CDATA[<p>One of the first rules of collaborative code work my teachers taught to me was, &quot;Don&apos;t break the buld!&quot; which is something developers have likely heard at one point or another during their careers. It&apos;s a good piece of advice, albeit a little vague:</p>]]></description><link>https://mark.bahnman.ca/dont-break-the-build/</link><guid isPermaLink="false">60e4236e53df56552cd0223c</guid><category><![CDATA[devops]]></category><dc:creator><![CDATA[Mark Bahnman]]></dc:creator><pubDate>Fri, 11 Jul 2014 21:02:00 GMT</pubDate><content:encoded><![CDATA[<p>One of the first rules of collaborative code work my teachers taught to me was, &quot;Don&apos;t break the buld!&quot; which is something developers have likely heard at one point or another during their careers. It&apos;s a good piece of advice, albeit a little vague: is a successful compile good enough to satisfy &apos;Not breaking the build&apos;? Today we get a lesson in specifics inspired by the story of <a href="http://bower.io">bower</a> and a <a href="https://github.com/raszi/node-tmp/issues/26">library</a> for making temporary files and directories <a href="https://github.com/bower/bower/pull/1403">breaking</a> their tool without a single line of code changing in bower.</p><h4 id="code-expectations">Code Expectations</h4><p>When you build release a version of your software you should expect that it will compile and run the same way today as it did yesterday. But your code likely depends on one or more libraries/frameworks/what-have-you from other developers and what&apos;s worse than having your build break because <em>you</em> made a change is having it break because someone else made a change. An obvious solution is to use a specific version of that dependancy consistently and update only when necessary. This is why we use version numbers (and tag releases) so that we have a common point of reference when using and talking about code. But how different is version 0.2.0 from 0.2.1?</p><h4 id="enter-semver">Enter Semver</h4><p><a href="http://semver.org/">Semver</a> is a semantic versioning sytem which seeks to standardize the meaning of version numbers. Version numbers come in the form of <code>major.minor.patch</code> and you increment each as such:</p><ul><li>MAJOR version when you make incompatible API changes</li><li>MINOR version when you add functionality in a backwards-compatible manner, and</li><li>PATCH version when you make backwards-compatible bug fixes.</li></ul><p>This (hypothetically) should take the guesswork out of versioning and provide at the very least &#xA0;semblemance of a guarantee to those who would use your software. With this system you could target <code>1.X.Y</code> or <code>1.2.Z</code> versions of a library you depend on and have a reasonable expectation that for any value X, Y or Z your code should work. Node even allows for this type of version targeting with their package manager <a href="https://www.npmjs.org/doc/misc/semver.html">npm</a>. You can target <code>^1.2.3</code> which will be valid for versions greater than <code>1.2.3</code> and less than <code>2.0.0</code>. Or you can be safer and target <code>~1.2.3</code> which will be valid for versions greater than <code>1.2.3</code> and less than <code>1.3.0</code>.</p><h4 id="problem-exists-between-chair-and-keyboard">Problem Exists Between Chair And Keyboard</h4><p>The main issue here is that you have to be confident that the maintainer of the software you depend on will adhere to this and that they&apos;re testing process is robust enough to catch anything that will break how you use it. If breaking changes are made in a patch bump you&apos;re effectively SOL if you rely on approximating versions. You can contact whomever maintains the software that broke and inform them - or help them fix if it&apos;s open source and you have the time - but who knows when they&apos;ll have time to get to it. A simple hotfix for this would be to narrowly target the exact version that was last working. This way the next time you (or your Continuous Integration platform) run <code>npm install</code> or update your tools you won&apos;t be surprised with errors.</p><p>A best practice to take away from this would be to only use exact versions for dependancies to provide consistency for your build and assurances to your users. All the fancy automated testing pipelines in the world mean nothing when their green badge on your github is only valid for the exact point in time that it was run.</p>]]></content:encoded></item><item><title><![CDATA[Embedding Soundcloud's HTML5 widget]]></title><description><![CDATA[<p>This isn&apos;t very well documented, but Soundcloud&apos;s HTML5 widget works as well with normal URLs - like the use in their flash widget - as it does with their preferred API URLs.</p><p>One of the suggested routes to get the API URL is to use their</p>]]></description><link>https://mark.bahnman.ca/soundcloud-html5-widget/</link><guid isPermaLink="false">60e422d853df56552cd02232</guid><category><![CDATA[javascript]]></category><dc:creator><![CDATA[Mark Bahnman]]></dc:creator><pubDate>Wed, 02 Jul 2014 01:58:00 GMT</pubDate><content:encoded><![CDATA[<p>This isn&apos;t very well documented, but Soundcloud&apos;s HTML5 widget works as well with normal URLs - like the use in their flash widget - as it does with their preferred API URLs.</p><p>One of the suggested routes to get the API URL is to use their <a href="https://developers.soundcloud.com/docs/api/reference#resolve"><code>/resolve</code></a> endpoint which you give a regular URL such as <code>https://soundcloud.com/jackconte</code> and it responds with a 401 redirect to &#xA0;<code>https://api.soundcloud.com/users/19328797</code>. This is nice, but the reason their flash widget integrated well into hubski was a user just had to give the regular URL and we could generate the code for the widget from it. To use the HTML5 widget - and get the benefits of being able to embed audio in mobile browsers - we would have to set up an extra API call with Soundcloud to get a client id in order to use the endpoint at all.</p><p>Another way is to use their <a href="https://developers.soundcloud.com/docs/oembed"><code>/oembed</code></a> endpoint. You can send the URL and get back a JSON payload which includes the full html for the widget. This simplifies things greatly and you can make a client side request like this</p><pre><code class="language-language-javascript">&lt;div id=\&quot;sc\&quot;&gt;&lt;/div&gt;
&lt;script&gt;
var URL = \&quot;https://soundcloud.com/jackconte\&quot;

$.getJSON(\&quot;http://soundcloud.com/oembed?format=json&amp;url=\&quot; + URL, function( data ) {
  $(&apos;#sc&apos;).append(data.html);
});
&lt;/script&gt;
</code></pre><p>to render the widget. This is especially useful if they ever change the widget HTML format and you don&apos;t need to register a client id.</p><p>Ideally, I&apos;d like to do a simple transform on the URL and spit out the HTML for the widget &#xE0; la youtube. I was trying to set this up and I couldn&apos;t find any easier way of doing this so I thought I&apos;d see what would happen if I shoved the URL in like so:</p><pre><code class="language-language-markup">&lt;iframe id=&apos;sc-widget&apos; width=&apos;100%&apos;&apos; height=&apos;166&apos; scrolling=&apos;no&apos; frameborder=&apos;no&apos; src=&apos;https://w.soundcloud.com/player/?url=&apos;https://soundcloud.com/jackconte&amp;show_artwork=true&apos;&gt;&lt;/iframe&gt;
</code></pre><p>To my surprise it just worked, as you can see here</p><p>I&apos;m not that surprised that they haven&apos;t documented this that well; there are a lot of benefits of forcing developers to register and go through your API. For now it serves its purpose, but I&apos;ll likely change to a more robust client side version using oEmbed when Hubski&apos;s frontend is refactored.</p>]]></content:encoded></item><item><title><![CDATA[Reimplementing Markdown in Arc Lisp]]></title><description><![CDATA[<p>Markdown is a double edged sword in online commenting. It is so ubiquititous in popular community sites that it is expected to be used but it can be fragmented in what is used from the spec and how it is implemented. At hubski we use a small subset of the</p>]]></description><link>https://mark.bahnman.ca/reimplementing-markdown-in-arc-lisp/</link><guid isPermaLink="false">60e4225253df56552cd02224</guid><category><![CDATA[lisp]]></category><category><![CDATA[markdown]]></category><dc:creator><![CDATA[Mark Bahnman]]></dc:creator><pubDate>Wed, 12 Feb 2014 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Markdown is a double edged sword in online commenting. It is so ubiquititous in popular community sites that it is expected to be used but it can be fragmented in what is used from the spec and how it is implemented. At hubski we use a small subset of the markdown spec with our own customizations (such as shout-outs). Normally I would use an appropriate library for parsing markdown and be done with it, but unfortunately no one has written one that I know of.</p><h4 id="arcs-markdown">Arc&apos;s Markdown</h4><p>What we do use is a modified version of news.arc which has a very simple implementation which only allows for italicizing, code blocks, and clickable links. It goes character by character and checks for a asterisk or double space at the start of a new line and then looks ahead for an applicable end character. After it parses the correct characters it then saves the parsed string in the appropriate file for the comment or submission. If you need to edit your text it then calls an unmarkdown function to convert it back. This works for pg and he and Nick Sivo (kogir) don&apos;t seem to have any desire to extend it.</p><p>This is the function which converts markdown strings to html:</p><pre><code class="language-language-groovy">(def markdown (s (o maxurl) (o nolinks))
  (let ital nil
    (tostring
      (forlen i s
        (iflet (newi spaces) (indented-code s i (if (is i 0) 2 0))
          (do (pr  \&quot;&lt;p&gt;&lt; pre&gt;&lt;code&gt;\&quot;)
            (let cb (code-block s (- newi spaces 1))
              (pr cb)
              (= i (+ (- newi spaces 1) (len cb))))
            (pr \&quot;&lt;/code&gt;&lt;/pre&gt;\&quot;))
          (iflet newi (parabreak s i (if (is i 0) 1 0))
             (do (unless (is i 0) (pr \&quot;&lt;p&gt;\&quot;))
                 (= i (- newi 1)))
            (and (is (s i) #\\*)
                 (or ital
                     (atend i s)
                     (and (~whitec (s (+ i 1)))
                          (pos #\\* s (+ i 1)))))
             (do (pr (if ital \&quot;&lt;/i&gt;\&quot; \&quot;&lt;i&gt;\&quot;))
                 (= ital (no ital)))
            (and (no nolinks)
                 (or (litmatch \&quot;http://\&quot; s i)
                     (litmatch \&quot;https://\&quot; s i)))
             (withs (n   (urlend s i)
                     url (clean-url (cut s i n)))
               (tag (a href url rel &apos;nofollow)
                 (pr (if (no maxurl) url (ellipsize url maxurl))))
               (= i (- n 1)))
             (writec (s i))))))))
</code></pre><p>Up until now we have hacked onto that implementation all of the formating we currently have. For things like bolding, quoting and strikethroughs that was easy enough: look for a character (\*, \+, |, or ~), look ahead for its matching character, replace as necessary. As we added more and more functionality it got progressively trickier to keep things from breaking.</p><h4 id="problems">Problems</h4><p>One of the obvious problems that arises is that it doesn&apos;t account for nested markdown. For instance, when it sees a code block it prints out the contents of the block with the appropriate tags wrapped. It skips over checking for any more markdown withint the tags.</p><p>Another more subtle one is that it stores the html version of the string. What most websites do (from what I&apos;m told) is store both the markdown and html versions. This way you only have to deal with a markdown to html parser and not the reverse. The benefits of this become obvious when you want to have unordered lists. For instance, according to the spec -, \+, and \* are all valid bullets for an unordered list. When converting back which do you choose to display to the user?</p><h4 id="solution">Solution</h4><p>So in order to allow for nested markdown I decided to rewrite how we dealt with it. First we go through our input string and break it up into a list of tokens. Simple tokens are things like \*, \+, |, ~, \\, [, ], (, ), and newlines. More complex ones include image and video links which get embedded. Luckily, if we separate things by spaces we&apos;ll get links by themselves. For instance we want to turn, &quot;This is \+bolded\+.<br>Here&apos;s a embedded link: <a href="http://i.imgur.com/zAuxnTw.gif">http://i.imgur.com/zAuxnTw.gif</a>&quot; into</p><pre><code class="language-language-groovy">(\&quot;This\&quot; #\\space \&quot;is\&quot; #\\space #\\+ \&quot;bolded.\&quot; #\
ewline \&quot;Here&apos;s\&quot; #\\space \&quot;a\&quot; #\\space \&quot;embedded\&quot; #\\space \&quot;link:\&quot; #\\space \&quot;http://i.imgur.com/zAuxnTw.gif\&quot;)
</code></pre><p>Links provide their own problem. We want to be able to split up links of the form \[Text\]\(url\) while allowing the url to contain parens. The way I ended up doing it is only tokenizing (&apos;s which are preceeded by a ] and counting the number of open parens in the current buffered text.</p><p>From here we can use this list much in the same way as the original implementation but instead go token by token and build an html string. As we go through the list we can keep state through either tail recursion or local variables in a loop (which is what the original uses). I ended up using both in different places to simplify things. In this way we can signal when the text is in a bolded or italic state without skipping over anything.</p><p>Another thing we added was an escape character &apos;\\&apos; in case you wanted to use an asterisk or two on their own.</p><p>Unfortunately much of our text data is still only stored in html string form. One day I will write a script to go through all our files and add the markdown versions of the text. We plan on moving our data to a proper database anyhow, so we&apos;ll probably end up doing that then as it will entail rewriting all our &apos;database&apos; calls anyway.</p><h4 id="conclusion">Conclusion</h4><p>This is definitely a better system for dealing with markdown and will make it easier for us to add more things in the future should the need arise. I&apos;ve come to realize, however, that for the greatest extensibility and generalization I probably should have gone with more formal approach akin to a markdown compiler. When I started this I didn&apos;t know anything about how compilers worked, but if I were to do it again I would implement a relatively simple compiler with three phases: Lexical Analysis, Semantic Analysis, and Code Generation. Lexical Analysis would be similar in that it built a list of tokens. Semantic analysis would take each of these token and create a parse tree with appropriate nodes (God I long for a nice typing system). Then we would just walk through the tree and print out the approriate string. I believe with this approach we would be able to represent everything you ever wanted in a markdown. Hopefully with the restructuring of hubski, however, I won&apos;t need to.</p>]]></content:encoded></item><item><title><![CDATA[Javascript, jQuery, and Modules: Refactoring the Hubski Enhancement Suite userscript]]></title><description><![CDATA[<p>Having not done much with javascript other than some simple hacks, I was inspired by <a href="https://shanetomlinson.com/2013/testing-javascript-frontend-part-1-anti-patterns-and-fixes/">this post</a> to do a major refactor of the Hubski Enhancement Suite userscript. You can look at the full refactored userscript <a href="https://gist.github.com/markbahnman/4968550">here</a>. It has the exact same functionality as the before but I believe it</p>]]></description><link>https://mark.bahnman.ca/javascript-jquery-and-modules-refactoring-the-hubski-enhancement-suite-userscript/</link><guid isPermaLink="false">60e41fa853df56552cd02211</guid><category><![CDATA[javascript]]></category><dc:creator><![CDATA[Mark Bahnman]]></dc:creator><pubDate>Wed, 13 Feb 2013 20:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Having not done much with javascript other than some simple hacks, I was inspired by <a href="https://shanetomlinson.com/2013/testing-javascript-frontend-part-1-anti-patterns-and-fixes/">this post</a> to do a major refactor of the Hubski Enhancement Suite userscript. You can look at the full refactored userscript <a href="https://gist.github.com/markbahnman/4968550">here</a>. It has the exact same functionality as the before but I believe it will be much nicer to read, maintain and extend with these changes.</p><h3 id="making-javascript-human-readable-with-jquery">Making javascript human readable with jQuery</h3><p>First things first, we want to the to be code easy to understand and thus easier to maintain. For instance, can you guess what this piece of code does?</p><pre><code class="language-language-javascript">window.location = document.getElementsByClassName(&apos;gridfeed&apos;)[0].childNodes[feedSelectionIndex].childNodes[1].childNodes[2].childNodes[1].childNodes[1].href;
</code></pre><p>It&apos;s not easy to tell without following the through each set of children. I think it can be agreed that this is easier on the eyes.</p><pre><code class="language-language-javascript">feed.currentNode.find(&apos;.savesplit &gt; a:contains(\&quot;hide\&quot;)&apos;).click();
</code></pre><p>I&apos;ll admit, something as simple as selectors blew my mind when I first saw them. One disadvantage to using jQuery in a userscript is that in Google Chrome the file containing jQuery is out of the scope of the userscript. In order to access the file you need to inject your script into the page.</p><pre><code class="language-language-javascript">function addScript(callback) {
    window.onload = function() {
        var script = document.createElement(&apos;script&apos;);
        script.textContent = &apos;(&apos; + callback.toString() + &apos;)();&apos;;
        document.body.appendChild(script);
    }
}

function main() { /* Userscript goes here */ }

addScript(main);
</code></pre><p>This uses a simplified version of the addJquery function by <a href="http://erikvold.com/blog/index.cfm/2010/6/14/using-jquery-with-a-user-script">Erik Void</a>. We don&apos;t need to load jQuery ourselves because Hubski does that for us; we merely need it in our scope. Also, loading our own jQuery in addition to hubski&apos;s introduces its own set of problems.</p><h3 id="abstracting-functionality-with-modules">Abstracting functionality with modules</h3><p>Each piece of major functionality can be wrapped into its own module. Each module has the general form</p><pre><code class="language-language-javascript">modules[&apos;moduleKey&apos;] = (function() {
    var Module = {
        init: function() { /*initialize stuff*/},
        isLoaded: function() {/*determine if module should be run*/}
    };
    privateMember;
    privateFunction() {}
    return Module;
    }());
</code></pre><p>Every module needs at least two function: <code>init()</code> to initialize the module (attach event handlers, insert spans, etc) and <code>isLoaded()</code> to determine if the module should be run on the current page. This makes loading all applicable modules as easy as</p><pre><code class="language-language-javascript">for(mod in modules) {
    if(modules[mod].isLoaded()) {
        modules[mod].init();
    }
}
</code></pre><p>The eventual goal of using this pattern is to be able to do unit tests which will require that we be able to initialize and cleanup the modules for each test.</p><h3 id="getting-rid-of-massive-ifelse-blocks">Getting rid of massive if/else blocks</h3><p>The shortcuts module contains all of the keyboard shortcuts for every applicable page on hubski. One of the major changes made to the shortcut functions is instead of using massive if/else blocks for determining which combination of keys was we use objects to map the functions to the key code. We have seperate objects for each applicable page (A single post, the user feed, notifications page, etc) and store keycode/function pairs in each object as such.</p><pre><code class="language-language-javascript">var postShortKeys = {
        &apos;65&apos;: // &apos;a&apos;
            function() { $(&apos;.longplusminus &gt; a&apos;).click(); },
        &apos;82&apos;: // &apos;r&apos;
            function() { $(&apos;[name=\&quot;text\&quot;]&apos;).focus(); },
        &apos;83&apos;: // &apos;s&apos;
            function() { $(&apos;.titlelinks &gt; a:contains(\&quot;save\&quot;)&apos;).click(); }
    };
</code></pre><p>Once we have all these objects defined we can determine which ones should be available to the user and combine them using the <a href="http://api.jquery.com/jQuery.extend/">extend</a> function.</p><pre><code class="language-language-javascript">var keyMap = {};
function buildKeyMap() {
    $.extend(keyMap,generalShortKeys);
    if(isPost) {$.extend(keyMap,postShortKeys)};
    // ...
}        
</code></pre><p>This simplifies the function call in the keyup event handler to something like</p><pre><code class="language-language-javascript">    keyMap[event.code]();
</code></pre><p>There are a few drawbacks to using this method as opposed to a if/else or switch/case block, however. For instance, it doesn&apos;t look as nice when you are using several different key codes to do the same thing. If you were using a switch statement you could let the those cases just fall through. Also if you want to use key combinations such as <code>Shift + &lt;key&gt;</code> you can&apos;t simply rely on the keycode, as <code>Shift + o</code> and <code>o</code> will be detected with the same code.</p>]]></content:encoded></item></channel></rss>