Jesse Shawl
2024-01-26T00:00:00Z
https://jesse.sh/
Jesse Shawl
jesse@jesse.sh
diy jwt signatures
2024-01-26T00:00:00Z
https://jesse.sh/diy-jwt-signatures/
<p>I recently had a need to create a jwt with a valid signature in a
browser without using a library.</p>
<h2>Creating a JWT</h2>
<p>The header and the payload</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> header <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"typ"</span><span class="token operator">:</span> <span class="token string">"JWT"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"alg"</span><span class="token operator">:</span> <span class="token string">"ES256"</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">const</span> payload <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"iss"</span><span class="token operator">:</span> <span class="token string">"joe"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"exp"</span><span class="token operator">:</span> <span class="token number">1300819380</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"http://example.com/is_root"</span><span class="token operator">:</span> <span class="token boolean">true</span><br /><span class="token punctuation">}</span></code></pre>
<p>are base 64 encoded</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">base64UrlEncode</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">object</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token function">btoa</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>to</p>
<pre class="language-js"><code class="language-js"><span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>header<span class="token punctuation">)</span><br /><span class="token comment">// eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9</span></code></pre>
<p>and</p>
<pre class="language-js"><code class="language-js"><span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span><br /><span class="token comment">// eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ==</span></code></pre>
<p>but the padding should be removed (<a href="https://datatracker.ietf.org/doc/html/rfc7515#appendix-C">rfc7515</a>)</p>
<pre class="language-diff"><code class="language-diff">const base64UrlEncode = (object) => {<br /><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> return btoa(JSON.stringify(object))<br /></span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> .replace(/=/g,"")<br /></span><span class="token prefix inserted">+</span><span class="token line"> .replace(/\//g,"_")<br /></span><span class="token prefix inserted">+</span><span class="token line"> .replace(/\+/g,"-")<br /></span></span>}</code></pre>
<p>and joined with <code>"."</code> to form a jwt</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>header<span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span><br /><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span><br /><span class="token comment">// eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ</span></code></pre>
<h2>Signing the JWT</h2>
<p>Generate an asymmetric keypair</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token punctuation">{</span>publicKey<span class="token punctuation">,</span> privateKey<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">generateKey</span><span class="token punctuation">(</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">namedCurve</span><span class="token operator">:</span> <span class="token string">"P-256"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// non-extractable private key</span><br /> <span class="token punctuation">[</span><span class="token string">"sign"</span><span class="token punctuation">,</span> <span class="token string">"verify"</span><span class="token punctuation">]</span><br /><span class="token punctuation">)</span></code></pre>
<p>I picked <code>ECDSA</code> using <code>P-256</code> because of the <code>Recommended+</code> label in
<a href="https://datatracker.ietf.org/doc/html/rfc7518#section-3.1">rfc7518</a></p>
<p>Sign the jwt with the private key</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> signature <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">sign</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"SHA-256"</span> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> privateKey<span class="token punctuation">,</span> <br /> jwt<br /><span class="token punctuation">)</span></code></pre>
<p>but that throws an error</p>
<pre class="language-txt"><code class="language-txt">Uncaught (in promise) TypeError: SubtleCrypto.sign: <br />Argument 3 could not be converted to any of: <br />ArrayBufferView, ArrayBuffer.</code></pre>
<p>so the data must be encoded as a <code>Uint8Array</code></p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> encodedJwt <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TextEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span>jwt<span class="token punctuation">)</span></code></pre>
<p>and re-signed</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> signature <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">sign</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"SHA-256"</span> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> privateKey<span class="token punctuation">,</span> <br /> encodedJwt<br /><span class="token punctuation">)</span></code></pre>
<p>convert the signature's <code>ArrayBuffer</code> to a <code>Uint8Array</code></p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> encodedSignature <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Uint8Array</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span></code></pre>
<p>and base64 encode the signature as a binary string</p>
<pre class="language-diff"><code class="language-diff">const base64UrlEncode = (object) => {<br /><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> let string<br /></span><span class="token prefix inserted">+</span><span class="token line"> if( object instanceof Uint8Array ) {<br /></span><span class="token prefix inserted">+</span><span class="token line"> string = String.fromCharCode(...object)<br /></span><span class="token prefix inserted">+</span><span class="token line"> } else {<br /></span><span class="token prefix inserted">+</span><span class="token line"> string = JSON.stringify(object)<br /></span><span class="token prefix inserted">+</span><span class="token line"> }<br /></span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> return btoa(JSON.stringify(object)) <br /></span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> return btoa(string)<br /></span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> .replace(/=/g,"")<br /></span><span class="token prefix unchanged"> </span><span class="token line"> .replace(/\//g,"_")<br /></span><span class="token prefix unchanged"> </span><span class="token line"> .replace(/\+/g,"-")<br /></span></span>}</code></pre>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> base64UrlEncodedSignature <span class="token operator">=</span> <span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>encodedSignature<span class="token punctuation">)</span><br /><span class="token comment">// rF6SuHTRcYWeGOkkgQ0QMGHomW_5e4s7dYKtU5Gvteiy_qmy8fEnpLFuIRfP0J4hIiYWCEftfWOFMUeEpXl_Qw</span></code></pre>
<p>and add it to the existing jwt</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> signedJwt <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>jwt<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>base64UrlEncodedSignature<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span></code></pre>
<p>and now I can see that it is verifiable in
<a href="https://jwt.io/#debugger-io?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.rF6SuHTRcYWeGOkkgQ0QMGHomW_5e4s7dYKtU5Gvteiy_qmy8fEnpLFuIRfP0J4hIiYWCEftfWOFMUeEpXl_Qw&publicKey=%7B%22alg%22%3A%22ES256%22%2C%22crv%22%3A%22P-256%22%2C%22ext%22%3Atrue%2C%22key_ops%22%3A%5B%22verify%22%5D%2C%22kty%22%3A%22EC%22%2C%22x%22%3A%22j2jaePi0nnRXfd7Nz1YqyiDyD1fYL9cfSEWbBt_buHY%22%2C%22y%22%3A%2246FnOBf4iV62BQdxISktM_kgH_jPnABCpOXp7ABLr3U%22%7D">the jwt.io debugger</a>
🥳 but also 🔐</p>
<h2>Verifying the JWT Signature</h2>
<p>split up the signed jwt</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token punctuation">[</span>encodedHeader<span class="token punctuation">,</span> encodedPayload<span class="token punctuation">,</span> encodedSignature<span class="token punctuation">]</span> <span class="token operator">=</span> <br /> signedJwt<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span></code></pre>
<p>decode the signature</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> decodedSignatureFromJwt <span class="token operator">=</span> <span class="token function">atob</span><span class="token punctuation">(</span>encodedSignature<span class="token punctuation">)</span><br /><span class="token comment">// Uncaught (in promise) DOMException: String contains an invalid character</span></code></pre>
<p>put back the url unsafe characters</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> decodedSignatureFromJwt <span class="token operator">=</span> <span class="token function">atob</span><span class="token punctuation">(</span><br /> encodedSignature<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">-</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span><span class="token string">"+"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">_</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span><span class="token string">"/"</span><span class="token punctuation">)</span><br /><span class="token punctuation">)</span></code></pre>
<p>but this is a binary string</p>
<pre class="language-js"><code class="language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>decodedSignatureFromJwt<span class="token punctuation">)</span><br /><span class="token comment">// ¬^�¸tÑq���é$��0aè�où{�;u�S�¯µè²þ©²ññ'¤±n!�ÏÐ�!"&��Gí}c�1G�¥y�C</span></code></pre>
<p>convert it to a <code>Uint8Array</code></p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> decodedSignatureFromJwtAsUint8Array <span class="token operator">=</span> Uint8Array<br /> <span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>decodedSignatureFromJwt<span class="token punctuation">,</span> <span class="token parameter">c</span> <span class="token operator">=></span> c<span class="token punctuation">.</span><span class="token function">charCodeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br /><span class="token comment">// new Uint8Array([172,94,146,184,116,209,113,133,158,24,233,36,129,13,16,48,97,232,153,111,249,123,139,59,117,130,173,83,145,175,181,232,178,254,169,178,241,241,39,164,177,110,33,23,207,208,158,33,34,38,22,8,71,237,125,99,133,49,71,132,165,121,127,67])</span></code></pre>
<p>and verify the signature</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> verified <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"SHA-256"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> publicKey<span class="token punctuation">,</span><br /> decodedSignatureFromJwtAsUint8Array<span class="token punctuation">,</span><br /> <span class="token keyword">new</span> <span class="token class-name">TextEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>encodedHeader<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>encodedPayload<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br /><span class="token punctuation">)</span><br /><span class="token comment">// true</span></code></pre>
<p>Pretty cool. Here's all the code I used in copy-paste format:</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">const</span> header <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"typ"</span><span class="token operator">:</span> <span class="token string">"JWT"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"alg"</span><span class="token operator">:</span> <span class="token string">"ES256"</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">const</span> payload <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"iss"</span><span class="token operator">:</span> <span class="token string">"joe"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"exp"</span><span class="token operator">:</span> <span class="token number">1300819380</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"http://example.com/is_root"</span><span class="token operator">:</span> <span class="token boolean">true</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">const</span> <span class="token function-variable function">base64UrlEncode</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">object</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> string<br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>object <span class="token keyword">instanceof</span> <span class="token class-name">Uint8Array</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> string <span class="token operator">=</span> String<span class="token punctuation">.</span><span class="token function">fromCharCode</span><span class="token punctuation">(</span><span class="token operator">...</span>object<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> string <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">return</span> <span class="token function">btoa</span><span class="token punctuation">(</span>string<span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">=</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\/</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">"_"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\+</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">"-"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>header<span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span><br /> <span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span><br /><br /> <span class="token comment">// sign</span><br /> <span class="token keyword">const</span> encodedJwt <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TextEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span>jwt<span class="token punctuation">)</span><br /> <span class="token keyword">const</span> <span class="token punctuation">{</span> publicKey<span class="token punctuation">,</span> privateKey <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">generateKey</span><span class="token punctuation">(</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">namedCurve</span><span class="token operator">:</span> <span class="token string">"P-256"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token boolean">false</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"sign"</span><span class="token punctuation">,</span> <span class="token string">"verify"</span><span class="token punctuation">]</span><br /> <span class="token punctuation">)</span><br /> <br /> <span class="token keyword">const</span> signature <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">sign</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"SHA-256"</span> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> privateKey<span class="token punctuation">,</span><br /> <span class="token keyword">new</span> <span class="token class-name">TextEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span>jwt<span class="token punctuation">)</span><br /> <span class="token punctuation">)</span><br /> <span class="token keyword">const</span> encodedSignature <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Uint8Array</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span><br /> <span class="token keyword">const</span> jwtWithSignature <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>jwt<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">base64UrlEncode</span><span class="token punctuation">(</span>encodedSignature<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><br /><br /> <span class="token keyword">const</span> jwk <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">exportKey</span><span class="token punctuation">(</span><span class="token string">"jwk"</span><span class="token punctuation">,</span> publicKey<span class="token punctuation">)</span><br /><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"jwt.io debugger url:"</span><span class="token punctuation">)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://jwt.io/#debugger-io?token=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>jwtWithSignature<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&publicKey=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encodeURIComponent</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>jwk<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br /><br /> <span class="token comment">// verify</span><br /> <span class="token keyword">const</span> importedPublicKey <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">importKey</span><span class="token punctuation">(</span><span class="token string">"jwk"</span><span class="token punctuation">,</span> jwk<span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">namedCurve</span><span class="token operator">:</span> <span class="token string">"P-256"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"verify"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><br /> <span class="token keyword">const</span> <span class="token punctuation">[</span>encodedHeaderFromJwt<span class="token punctuation">,</span> encodedPayloadFromJwt<span class="token punctuation">,</span> encodedSignatureFromJwt<span class="token punctuation">]</span> <span class="token operator">=</span> jwtWithSignature<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span><br /> <span class="token keyword">const</span> decodedHeaderFromJwt <span class="token operator">=</span> <span class="token function">atob</span><span class="token punctuation">(</span>encodedHeaderFromJwt<span class="token punctuation">)</span><br /> <span class="token keyword">const</span> decodedPayloadFromJwt <span class="token operator">=</span> <span class="token function">atob</span><span class="token punctuation">(</span>encodedPayloadFromJwt<span class="token punctuation">)</span><br /> <span class="token keyword">const</span> decodedSignatureFromJwt <span class="token operator">=</span> <span class="token function">atob</span><span class="token punctuation">(</span>encodedSignatureFromJwt<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">-</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">"+"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">_</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br /> <span class="token keyword">const</span> decodedSignatureFromJwtAsUint8Array <span class="token operator">=</span> Uint8Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>decodedSignatureFromJwt<span class="token punctuation">,</span> <span class="token parameter">c</span> <span class="token operator">=></span> c<span class="token punctuation">.</span><span class="token function">charCodeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br /> <span class="token keyword">const</span> verified <span class="token operator">=</span> <span class="token keyword">await</span> crypto<span class="token punctuation">.</span>subtle<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"ECDSA"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"SHA-256"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> publicKey<span class="token punctuation">,</span><br /> decodedSignatureFromJwtAsUint8Array<span class="token punctuation">,</span><br /> <span class="token keyword">new</span> <span class="token class-name">TextEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>encodedHeaderFromJwt<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>encodedPayloadFromJwt<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br /> <span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<h2>Further Reading</h2>
<ul>
<li><a href="https://datatracker.ietf.org/doc/html/rfc7515">JSON Web Signature rfc7515</a></li>
<li><a href="https://datatracker.ietf.org/doc/html/rfc7518">JSON Web Algorithms rfc7518</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto">SubtleCrypto API docs</a></li>
</ul>
Just enough Java for Crafting Interpreters
2023-10-10T00:00:00Z
https://jesse.sh/just-enough-java-for-crafting-interpreters/
<p>I've been working my way through Robert Nystrom's <a href="https://craftinginterpreters.com/">Crafting Interpreters</a> and had to learn <em>some</em> Java in order to run
the <code>Scanner</code> class implemented in Chapter 4.</p>
<p>The last time <a href="https://gist.github.com/jshawl/127cf26be3fe2357342110df9b49dd60">I wrote Java</a>
was right before I withdrew from my first CS 101 course, so I had to put
some time into mastering the basics. This post documents a handful of the
things I had to learn to start scanning tokens in Lox!</p>
<p>Here's a link to an example that will have you up and running with Java
in no time! <a href="https://github.com/jshawl/just-enough-java-for-crafting-interpreters">https://github.com/jshawl/just-enough-java-for-crafting-interpreters</a></p>
<h2>Installing the JDK</h2>
<p>The last time I tried installing the JDK on a mac, I was prompted to create
an account with Oracle, which I did not want to do. I decided to go with
<code>docker compose</code> for all of my Java work. The <code>compose.yml</code> file looks like:</p>
<pre class="language-yml"><code class="language-yml"><span class="token key atrule">services</span><span class="token punctuation">:</span><br /> <span class="token key atrule">java</span><span class="token punctuation">:</span><br /> <span class="token key atrule">image</span><span class="token punctuation">:</span> eclipse<span class="token punctuation">-</span>temurin<span class="token punctuation">:</span><span class="token number">17</span></code></pre>
<p>Starting the <code>java</code> service with <code>docker compose run java bash</code> will open
a shell where you can then run <code>javac</code> to compile your code and <code>java</code> to
run it.</p>
<h2>Creating a first Java class</h2>
<p>I created my first class inside of <code>com/jshawl/example</code> so that the package
name <code>com.jshawl.example</code> matched the directory structure. More on packages
in a minute...</p>
<p>Your directory structure should look like this:</p>
<pre class="language-text"><code class="language-text">./<br />├── com/<br />│ └── jshawl/<br />│ └── example/<br />│ └── FirstClass.java<br />└── compose.yml</code></pre>
<p>and inside of <code>FirstClass.java</code>:</p>
<pre class="language-java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>jshawl<span class="token punctuation">.</span>example</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">class</span> <span class="token class-name">FirstClass</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Hello from FirstClass!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<h2>Compiling and Running</h2>
<p>Let's modify the <code>compose.yml</code> to specify a volume for the newly
created <code>com/</code> directory:</p>
<pre class="language-diff"><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line">services:<br /></span><span class="token prefix unchanged"> </span><span class="token line"> java:<br /></span><span class="token prefix unchanged"> </span><span class="token line"> image: eclipse-temurin:17<br /></span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> working_dir: /app<br /></span><span class="token prefix inserted">+</span><span class="token line"> volumes:<br /></span><span class="token prefix inserted">+</span><span class="token line"> - ./com:/app/com</span></span></code></pre>
<p>Start the container:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> compose run java <span class="token function">bash</span></code></pre>
<p>Compile the code:</p>
<pre class="language-bash"><code class="language-bash">javac com/jshawl/example/FirstClass.java</code></pre>
<p>This will create a new file <code>com/jshawl/example/FirstClass.class</code>.</p>
<p>You can now run this code with:</p>
<pre class="language-bash"><code class="language-bash">java com.jshawl.example.FirstClass<br /><span class="token comment">#=> Hello from FirstClass!</span></code></pre>
<h2>Packages</h2>
<p>Packages allow us to group related classes and use a namespace to import
classes from the standard library à la <code>import java.io.BufferedReader</code>.</p>
<p>For the examples in Crafting Interpreters, several classes reference
each other, and a shared package allows us to include code without a
specific <code>import</code> statement.</p>
<p>Let's create a second class to demo this functionality:</p>
<pre class="language-java"><code class="language-java"><span class="token comment">// in com/jshawl/example/SecondClass.java</span><br /><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>jshawl<span class="token punctuation">.</span>example</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">class</span> <span class="token class-name">SecondClass</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">greetings</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Greetings from SecondClass."</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>and create an instance of this class inside of <code>FirstClass.java</code>:</p>
<pre class="language-diff"><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> public static void main(String[] args) {<br /></span><span class="token prefix unchanged"> </span><span class="token line"> System.out.println("Hello from FirstClass!");<br /></span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> SecondClass secondClass = new SecondClass();<br /></span><span class="token prefix inserted">+</span><span class="token line"> secondClass.greetings();</span></span></code></pre>
<p>you can now recompile:</p>
<pre class="language-bash"><code class="language-bash">javac com/jshawl/example/FirstClass.java</code></pre>
<p>and run and you'll see the output from <code>SecondClass</code>:</p>
<pre class="language-bash"><code class="language-bash">java com.jshawl.example.FirstClass<br /><span class="token comment">#=> Hello from FirstClass!</span><br /><span class="token comment">#=> Greetings from SecondClass.</span></code></pre>
<p>You might have noticed I didn't explicitly compile <code>SecondClass</code>. <code>javac</code>
is smart enough to compile the code that is referenced in <code>FirstClass</code>.</p>
<p>If you wanted to compile everything even if it isn't used anywhere yet,
use a glob:</p>
<pre class="language-bash"><code class="language-bash">javac com/jshawl/example/*.java</code></pre>
<p>I'll plan to come back and add to this guide if I encounter any other
foundational knowledge about building and running Java programs, but so
far this was enough for me to start a jlox prompt.</p>
<p>Happy hacking!</p>
Require esm imports
2023-05-08T00:00:00Z
https://jesse.sh/require-esm-imports/
<p>See what I did there?</p>
<p>I recently encountered a project that used <code>require</code> for including external modules, and when upgrading the dependency's major version, I was met with this error:</p>
<pre><code>Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/beeper/index.js from .../index.js not supported.
Instead change the require of index.js in .../index.js to a dynamic import() which is available in all CommonJS modules.
at async Promise.all (index 0) {
code: 'ERR_REQUIRE_ESM'
}
</code></pre>
<p>I'm curious about a couple of things here:</p>
<ul>
<li>How can a package tell that I'm using <code>require</code>?</li>
<li>Why would a package require <code>import</code> and prevent <code>require</code></li>
<li>What is the technical implementation for throwing <code>ERR_REQUIRE_ESM</code></li>
</ul>
<h2>Steps to reproduce</h2>
<p>in index.js:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> beeper <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'beeper'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token function">beeper</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>in package.json:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"beeper"</span><span class="token operator">:</span> <span class="token string">"2.1.0"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Run <code>node index.js</code>. If I upgrade the dependency with
<code>npm install beeper@latest --save</code> and rerun <code>node index.js</code> I see the
<code>ERR_REQUIRE_ESM</code>.</p>
<h2>What did <code>beeper</code> do between 2.1.0 and 3.0.0?</h2>
<p>Looking at the <a href="https://github.com/sindresorhus/beeper/releases/tag/v3.0.0">3.0.0 release</a> I see:</p>
<blockquote>
<p>This package is now pure ESM. Please read <a href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">this</a>.</p>
</blockquote>
<p>There is a ton of useful information in here, but I wanted to see the
beeper-specific change.</p>
<p>From <a href="https://github.com/sindresorhus/beeper/compare/v2.1.0...v3.0.0">https://github.com/sindresorhus/beeper/compare/v2.1.0...v3.0.0</a> I
see a few useful things:</p>
<ul>
<li><code>module.exports =</code> was replaced with <code>export default</code></li>
<li>there is a new <code>"exports": "./index.js"</code> in <code>package.json</code></li>
<li>there is a new <code>"type": "module"</code> in <code>package.json</code></li>
</ul>
<h2>Reproduce the beeper package</h2>
<p>The only things required to reproduce the <code>ERR_REQUIRE_ESM</code> are:</p>
<pre><code>package/
├── index.js
└── package.json
</code></pre>
<p>where <code>index.js</code> is an empty file and <code>package.json</code> only has one
property: <code>"type": "module"</code>.</p>
<p>Neat! Now we know that the <code>ERR_REQUIRE_ESM</code> error is coming from the <code>"type": "module"</code> set in <code>package.json</code>.</p>
<p>From <a href="https://nodejs.org/api/packages.html#type">the documentation</a>:</p>
<blockquote>
<p>If the nearest parent package.json lacks a "type" field, or contains "type": "commonjs", .js files are treated as <a href="https://nodejs.org/api/modules.html#modules-commonjs-modules">CommonJS</a>. If the volume root is reached and no package.json is found, .js files are treated as CommonJS.</p>
</blockquote>
<p>Oh that's interesting. NodeJS supports both CommonJS <em>and</em> ES Modules.</p>
<h2>Is supporting both CJS and ES modules feasible?</h2>
<p>Well, no; this looks pretty complex:</p>
<ul>
<li><a href="https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html">https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html</a></li>
<li><a href="https://stackoverflow.com/a/75347455/850825">https://stackoverflow.com/a/75347455/850825</a></li>
<li><a href="https://antfu.me/posts/publish-esm-and-cjs">https://antfu.me/posts/publish-esm-and-cjs</a></li>
</ul>
<p>I now understand why the release notes linked to the
<a href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">Pure ESM Package</a> note.</p>
<iframe src="https://giphy.com/embed/jUwpNzg9IcyrK" width="480" height="360" frameBorder="0" class="giphy-embed" allowFullScreen=""></iframe>
HTTP Authentication via SSH
2023-04-08T00:00:00Z
https://jesse.sh/http-authentication-via-ssh/
<ul>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#demo">Demo</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#background-and-motivation">Background and motivation</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#how-it-works">How it works</a>
<ul>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#generate-a-session-secret">Generate a session secret</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#poll-for-the-ssh-connection">Poll for the SSH connection</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#ssh-in-with-the-session-secret">SSH in with the session secret</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#map-the-session-secret-to-the-users-public-key">Map the session secret to the user's public key</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#redirect-to-an-authenticated-page">Redirect to an authenticated page</a></li>
</ul>
</li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#next-steps">Next steps</a></li>
<li><a href="https://jesse.sh/http-authentication-via-ssh/#other-projects-worth-checking-out">Other projects worth checking out</a></li>
</ul>
<h2>Demo</h2>
<p>Visit <a href="https://auths.sh/">https://auths.sh/</a> and follow the command line instructions, or <a href="https://github.com/jshawl/auths.sh">browse the
source code</a>, specifically:</p>
<ul>
<li><a href="https://github.com/jshawl/auths.sh/blob/main/ssh.go">ssh.go</a> - the SSH server</li>
<li><a href="https://github.com/jshawl/auths.sh/blob/main/http.go">http.go</a> - the HTTP server</li>
</ul>
<h2>Background and motivation</h2>
<p>I've been working on <a href="https://proof.im/">https://proof.im/</a> and thinking about cooler ways
to authenticate, as if authenticating with SSH private key signatures weren't
cool enough already. The signature generation part still felt a little too
geeky for me and I often have to reference the arcane <code>ssh-keygen</code> syntax
to generate a signature.</p>
<p>It occurred to me that I could take advantage of the Diffie-Hellman key
exchange to authenticate users without asking them to manually generate their
own signatures and send them over HTTP.</p>
<h2>How it works</h2>
<p>Here's a super high level overview with more detail below:</p>
<ol>
<li>The web server generates a session secret and stores it in a cookie.</li>
<li>The web server provides SSH instructions, passing the session secret as a command</li>
<li>The ssh server handles the key exchange and stores the session secret with the user's public key</li>
<li>The web server polls for the SSH connection with the session secret</li>
<li>The web server sets a cookie and redirects to an authenticated page.</li>
</ol>
<h3>Generate a session secret</h3>
<p>Nothing special here. Every request to <a href="https://auths.sh/">https://auths.sh/</a> will generate a uuid and store
the value in a cookie.</p>
<p>The web server tells the user to ssh in with the provided uuid as the SSH command, like:</p>
<pre class="language-txt"><code class="language-txt">ssh auths.sh 00000000-0000-0000-0000-000000000000</code></pre>
<p>The cookie serves no purpose other than storing the session secret for
subsequent requests. It's possible to skip the cookie entirely,
but a cookie based session allows user-friendlier URLs like:
<code>https://auths.sh/session</code> instead of something like</p>
<pre class="language-txt"><code class="language-txt">https://auths.sh/session?secret=00000000-0000-0000-0000-000000000000</code></pre>
<p>As an extra bonus, the session secret won't show up in the web server logs.</p>
<h3>Poll for the SSH connection</h3>
<p>It will probably take some time for the user to make the SSH connection, so the
browser polls for the status of the SSH connection with the secret:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> int <span class="token operator">=</span> <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"/status"</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span></code></pre>
<p>When the HTTP server responds with a 200, we know that the user has connected
via SSH with the session secret and can now redirect them to an authenticated page.</p>
<p>The <code>/status</code> endpoint attempts to serve a file named <code>/tmp/00000000-0000-0000-0000-000000000000</code> and responds with a 200 once the SSH server
generates that file.</p>
<h3>SSH in with the session secret</h3>
<pre class="language-txt"><code class="language-txt">ssh auths.sh 00000000-0000-0000-0000-000000000000</code></pre>
<p>The ssh server will know a couple of things at this point:</p>
<ol>
<li>The username - <a href="https://pkg.go.dev/github.com/gliderlabs/ssh#Session.User"><code>s.User()</code></a></li>
<li>The user's public key - <a href="https://pkg.go.dev/github.com/gliderlabs/ssh#Session.PublicKey"><code>s.PublicKey()</code></a></li>
<li>The session secret - <a href="https://pkg.go.dev/github.com/gliderlabs/ssh#Session.Command"><code>s.Command()[0]</code></a></li>
</ol>
<p>In addition, the SSH server can infer that the user is the original owner
of the public key because - how else would they have completed the key exchange?.</p>
<p>Lastly, it's possible for someone to guess the session secret and use a different
public key, but the session is relatively short-lived and UUIDs are hard to guess.</p>
<h3>Map the session secret to the user's public key</h3>
<p>The SSH server stores the session secret with the user's public key. In this demo,
it looks like this:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">type</span> User <span class="token keyword">struct</span> <span class="token punctuation">{</span><br /> Name <span class="token builtin">string</span><br /> PublicKey <span class="token builtin">string</span><br /><span class="token punctuation">}</span><br /><br />authorizedKey <span class="token operator">:=</span> gossh<span class="token punctuation">.</span><span class="token function">MarshalAuthorizedKey</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">PublicKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br />d <span class="token operator">:=</span> User<span class="token punctuation">{</span>Name<span class="token punctuation">:</span> s<span class="token punctuation">.</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> PublicKey<span class="token punctuation">:</span> strings<span class="token punctuation">.</span><span class="token function">TrimSpace</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>authorizedKey<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><br />file<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">MarshalIndent</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token string">" "</span><span class="token punctuation">)</span><br />os<span class="token punctuation">.</span><span class="token function">WriteFile</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"/tmp/%s"</span><span class="token punctuation">,</span> s<span class="token punctuation">.</span><span class="token function">Command</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> file<span class="token punctuation">,</span> <span class="token number">0644</span><span class="token punctuation">)</span></code></pre>
<p>That is... write a json file named <code>/tmp/00000000-0000-0000-0000-000000000000</code>
with the contents:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"Name"</span><span class="token operator">:</span> <span class="token string">"jesse"</span><span class="token punctuation">,</span><br /> <span class="token property">"PublicKey"</span><span class="token operator">:</span> <span class="token string">"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGaGowvVxI4t5W+w6TCCxD4pEBHA3cb0wR63EGy3r4AI"</span><br /><span class="token punctuation">}</span></code></pre>
<h3>Redirect to an authenticated page</h3>
<p>Remember the polling from before? A 200 status indicates the public key
has been stored with the session secret, so we can safely redirect to a
page that requires authentication.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">d</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> window<span class="token punctuation">.</span>location <span class="token operator">=</span> <span class="token string">'/session'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>The web browser sends the session secret with the GET request, so the web
server is able to identify the SSH user. Pretty cool.</p>
<h2>Next steps</h2>
<p>This is more or less a proof of concept for authenticating an HTTP session
via SSH. I'm curious about updating the sign up process for <a href="https://proof.im/">https://proof.im/</a>
to offer this auth mechanism.</p>
<p>I also want to read the following books to make sure my understanding of
the key exchange doesn't leave a loophole where ssh'ers can forge a public
key:</p>
<ul>
<li><a href="https://www.oreilly.com/library/view/ssh-the-secure/0596008953/">SSH, The Secure Shell: The Definitive Guide</a></li>
<li><a href="https://www.tiltedwindmillpress.com/product/ssh-mastery-2nd-edition/">SSH Mastery</a></li>
</ul>
<p>I'm also beginning to think about how other clients might integrate SSH-based HTTP
authentication into their applications: right now, the public key is the unique identifier
for a user, but I'm not sure there's anything I can send over HTTP that would be cryptographically
verifiable. e.g. the SSH server knows the public key but doesn't expose anything that <em>only</em>
the user could have provided, like a private key or signature. <a href="https://pkg.go.dev/golang.org/x/crypto/ssh#Signature"><code>crypto/ssh</code></a> has types for <code>Signature</code> but I think that's only used for signatures
generated with the host private key.</p>
<h2>Other projects worth checking out</h2>
<ul>
<li><a href="https://github.com/charmbracelet/wish">Wish</a> - Make SSH apps, just like that! 💫</li>
<li><a href="https://github.com/gliderlabs/ssh">gliderlabs/ssh</a> - Easy SSH servers in Golang</li>
<li><a href="https://proof.im/">https://proof.im/</a> - Zero-trust proof of identity</li>
</ul>
Recursive React Forms
2022-12-08T00:00:00Z
https://jesse.sh/recursive-react-forms/
<p>I was recently working on a <a href="https://en.wikipedia.org/wiki/SOAP">SOAP</a> client user interface and needed the ability
to nest inputs to an arbitrary depth and edit the data inline.</p>
<h2>Demo</h2>
<p>Given an object of unknown depth:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"age"</span><span class="token operator">:</span> <span class="token number">33</span><span class="token punctuation">,</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"first"</span><span class="token operator">:</span> <span class="token string">"Jesse"</span><span class="token punctuation">,</span><br /> <span class="token property">"last"</span><span class="token operator">:</span> <span class="token string">"Shawl"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"interest"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"javascript"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"for"</span><span class="token operator">:</span> <span class="token string">"sure"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Display a form that visually matches the data structure and allows users to edit the data.</p>
<p class="codepen" data-height="666" data-theme-id="light" data-default-tab="result" data-slug-hash="mdKoaXP" data-user="jshawl" style="height: 666px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/jshawl/pen/mdKoaXP">
Recursive React Form</a> by Jesse Shawl (<a href="https://codepen.io/jshawl">@jshawl</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h3>Why Use Recursion?</h3>
<p>I probably could have assumed that any data structures would not be more than 3 or so
levels deep and kept track of the level, but I was curious about handling an infinitely deep
data structure.</p>
<h3>How does it work?</h3>
<p>I started out with the simple case:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"age"</span><span class="token operator">:</span> <span class="token number">33</span><br /><span class="token punctuation">}</span></code></pre>
<p>and focused on a simple component to manage state changes:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">TableForm</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>data<span class="token punctuation">}</span></span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">(</span><br /> <span class="token operator"><</span>table<span class="token operator">></span><br /> <span class="token operator"><</span>thead<span class="token operator">></span><br /> <span class="token operator"><</span>tr<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span>key<span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span>value<span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>tr<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>thead<span class="token operator">></span><br /> <span class="token operator"><</span>tbody<span class="token operator">></span><br /> <span class="token punctuation">{</span>Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">key</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token operator"><</span>tr<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span><span class="token punctuation">{</span>key<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span><span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>tr<span class="token operator">></span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><br /> <span class="token operator"><</span><span class="token operator">/</span>tbody<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>table<span class="token operator">></span><br /> <span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>Making the data structure a little more complex will error out, because <code>data[key]</code> is an object
and React doesn't allow objects as children. Enter the recursive case.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">TableForm</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>data<span class="token punctuation">}</span></span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">(</span><br /> <span class="token operator"><</span>table<span class="token operator">></span><br /> <span class="token operator"><</span>thead<span class="token operator">></span><br /> <span class="token operator"><</span>tr<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span>key<span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span>value<span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>tr<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>thead<span class="token operator">></span><br /> <span class="token operator"><</span>tbody<span class="token operator">></span><br /> <span class="token punctuation">{</span>Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">key</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token operator"><</span>tr<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span><span class="token punctuation">{</span>key<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span>td<span class="token operator">></span><span class="token punctuation">{</span><br /> <span class="token keyword">typeof</span> data<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">"object"</span> <span class="token operator">?</span> <br /> <span class="token operator"><</span>TableForm data<span class="token operator">=</span><span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator">:</span> <span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>td<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>tr<span class="token operator">></span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><br /> <span class="token operator"><</span><span class="token operator">/</span>tbody<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>table<span class="token operator">></span><br /> <span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>This will render the nested tables no problem. Things became complicated when I tried to handle change events recursively. Let's dig in.</p>
<h2>Recursive Change Handlers</h2>
<p>I decided to manage state all the way up at the <code><App /></code> level, which told me I'd need to
pass an <code>onChange</code> prop to <code>TableForm</code>.</p>
<p>We also need some inputs. Here's an abbreviated set of components:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">TableForm</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>data<span class="token punctuation">,</span> onChange<span class="token punctuation">}</span></span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token punctuation">{</span><br /> <span class="token keyword">typeof</span> data<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">"object"</span> <span class="token operator">?</span> <br /> <span class="token operator"><</span>TableForm data<span class="token operator">=</span><span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span>onChange<span class="token punctuation">}</span><span class="token operator">/</span><span class="token operator">></span> <span class="token operator">:</span><br /> <span class="token operator"><</span>input value<span class="token operator">=</span><span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span>onChange<span class="token punctuation">}</span><span class="token operator">/</span><span class="token operator">></span><br /> <span class="token punctuation">}</span><br /> <span class="token comment">// ...</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">(</span><br /> <span class="token operator"><</span>TableForm onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token parameter">d</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'updated data is:'</span><span class="token punctuation">,</span> d<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token operator">></span><br /> <span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>This will work great for the single level data structure. e.g. modify the <code>age</code> input and the object that comes out of the top level <code>onChange</code> will be <code>{age: 33}</code>.</p>
<p>The problem is that editing children objects will bubble all the way up to the top level <code>onChange</code>.</p>
<p>e.g. given an object like:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"first"</span><span class="token operator">:</span> <span class="token string">"Jesse"</span><span class="token punctuation">,</span><br /> <span class="token property">"last"</span><span class="token operator">:</span> <span class="token string">"Shawl"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p><code>onChange</code> will only ever send back <code>{first: "Jesse"}</code> or <code>{last: "Shawl"}</code>.</p>
<p>I wanted to make sure the entire data structure came back out, so:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">TableForm</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>data<span class="token punctuation">,</span> onChange<span class="token punctuation">}</span></span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token punctuation">{</span><br /> <span class="token keyword">typeof</span> data<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">"object"</span> <span class="token operator">?</span> <br /> <span class="token operator"><</span>TableForm data<span class="token operator">=</span><span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token parameter">d</span> <span class="token operator">=></span> <span class="token function">onChange</span><span class="token punctuation">(</span><span class="token operator">...</span>data<span class="token punctuation">,</span> <span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token operator">:</span> d<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token operator">/</span><span class="token operator">></span> <span class="token operator">:</span><br /> <span class="token operator"><</span>input value<span class="token operator">=</span><span class="token punctuation">{</span>data<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token parameter">e</span> <span class="token operator">=></span> <span class="token function">onChange</span><span class="token punctuation">(</span><span class="token operator">...</span>data<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token operator">:</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token operator">/</span><span class="token operator">></span><br /> <span class="token punctuation">}</span><br /> <span class="token comment">// ...</span><br /><span class="token punctuation">}</span></code></pre>
<p>Now editing the <code>first</code> input will trigger the top level <code>onChange</code> to send back the entire object:</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">{</span><br /> <span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"first"</span><span class="token operator">:</span> <span class="token string">"Jesse"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"last"</span><span class="token operator">:</span> <span class="token string">"Shawl"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Pretty cool! You can scroll up to play around with the demo or fork it on <a href="https://codepen.io/jshawl/pen/mdKoaXP">codepen</a>.</p>
<p>I'm super tempted to make a slick Soap client UI now that I have the basic code to edit its data structure. We'll see what comes of it.</p>
Dual Range Input
2022-09-20T00:00:00Z
https://jesse.sh/dual-range-input/
<p>I recently had a need for an <code><input type='range'></code> with start and end buttons, so I built one:</p>
<p class="codepen" data-height="309" data-theme-id="light" data-default-tab="html,result" data-slug-hash="WNJjPRv" data-user="jshawl" style="height: 309px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/jshawl/pen/WNJjPRv">
Dual Range Input Hello World</a> by Jesse Shawl (<a href="https://codepen.io/jshawl">@jshawl</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p><a href="https://github.com/jshawl/dual-range-input">View on GitHub</a>
<a href="https://www.npmjs.com/package/dual-range-input">View on npm</a></p>
<p>I made a few API and design decisions that I wanted to think through
here for the next npm package I might build.</p>
<h2>Design Decisions</h2>
<h3>Functional UI From the Start</h3>
<p>It is important to me that a broken/unloaded/javascript-less state
still functions as intended.</p>
<p>The hello world example (above) demonstrates how the package depends on two existing <code><input type='range'></code>s.</p>
<p>I'm curious about auto generating these inputs from JavaScript but for now
I'm happy with requiring the user to provide them. This has the added benefit of being able to attach event listeners to and modify the values of the underling <code>input</code>s with no conflict issues.</p>
<h3>Browser Native Module Support</h3>
<p>Using ES Modules means not having to deal with a build system:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">import</span> DualRangeInput <span class="token keyword">from</span> <span class="token string">'https://unpkg.com/dual-range-input@latest/script.js'</span><span class="token punctuation">;</span><br /> <span class="token function">DualRangeInput</span><span class="token punctuation">(</span><span class="token string">".dual-range-1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>The source code <em>is</em> the distributed code, which means I don't have to worry about compilation before distribution.</p>
<h3>Sane Default Behavior</h3>
<p>The first action I implemented was making each slider "push" the other when
a particular minimum or maximum value was exceeded.</p>
<p>e.g. dragging the start past the end should also drag the end with it.</p>
<p>I also added in <code>:active</code> states to match the browser-native styles, and made
it so that the active button appears above the inactive button.</p>
<p>Lastly, I had started with always showing the values but realized this created problems when the start and end were very close together - the numbers would overlap! I decided to only show the value of the range during dragging, which in my humble opinion is an enhancement to how the browser-native range input should behave.</p>
<h2>API Decisions</h2>
<h3>Default Export is a Function</h3>
<p>The default export from the package is a function, which allows users to call
the library whatever they want:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> customRange <span class="token keyword">from</span> <span class="token string">'https://unpkg.com/dual-range-input@latest/script.js'</span></code></pre>
<p>but also binds each invocation to a particular UI element - the set of browser-native <code>input</code>s.</p>
<h3>Dynamic Arguments</h3>
<p>You can do</p>
<pre class="language-js"><code class="language-js"><span class="token function">DualRangeInput</span><span class="token punctuation">(</span><span class="token string">".a-query-selector"</span><span class="token punctuation">)</span></code></pre>
<p>which is short for:</p>
<pre class="language-js"><code class="language-js"><span class="token function">DualRangeInput</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">selector</span><span class="token operator">:</span> <span class="token string">".a-query-selector"</span><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>I did this because I suspect most consumers will use the input without configuration.</p>
<h3>Inlined, Overridable CSS</h3>
<p>I wanted to avoid asking consumers to add a <code><link /></code> tag to import the custom styles required, and so I decided to just write the CSS inside the main script tag.</p>
<p>This has the added benefit (to me) of coupling the UI markup with the UI styles. That is, users don't
have control over the UI markup (yet), and so each new release can safely modify the markup and styles without worrying about breaking previous implementations.</p>
<h2>Looking Forward</h2>
<p>I'm super curious about adding in a third button, like a gradient generator might have and am wondering if I've made a mistake by adding "dual" to the package name from the start.</p>
<p>Interested in contributing? Check it out <a href="https://github.com/jshawl/dual-range-input">on GitHub</a>.</p>
Introducing Telephony.cc
2021-05-15T00:00:00Z
https://jesse.sh/introducing-telephonycc/
<p>We all have to start somewhere, right? This is my new side hustle: <a href="https://www.telephony.cc/">https://www.telephony.cc</a></p>
<a href="https://www.telephony.cc/">
<img src="https://jesse.sh/img/telephony.png" alt="marketing site home page" style="max-width:100%" />
</a>
<p>I'm feeling super optimistic about starting something new, and am placing
bets on the things that are important to me. I can't remember the last time
I called a business let alone answered a phone call. I almost always prefer sms so that I have time to think about my response and/or have a papertrail for reference in case I forget (to write it down). I am building a product that
scratches my own itch and attempts to invoke the change I want to see in the world.</p>
<p>I want to be able to text businesses:</p>
<ul>
<li>without it being one of the employees' personal phone numbers</li>
<li>immediately receive useful information after hours</li>
</ul>
<p>I want to be able to give out a local number:</p>
<ul>
<li>so that I look like a local</li>
<li>so that I don't give my personal number out to a business</li>
</ul>
<p>Telephony is an attempt to open up the world to sms'ing businesses.</p>
<p>It's worth mentioning too that Twilio will be an integral part of this
product, and that's important to me because it's a company I believe in.</p>
<p>They recently acquired segment and sendgrid, two companies I <em>also</em> believe in.</p>
<p>I think this product has the potential to expand into a marketing platform for sms, integrating customers with CRMs and chatbots.</p>
<p>sms is here to stay, but companies keep reimplementing chat clients. We will always (probably?) have our sms client on our handheld devices.</p>
<p>Curious about the future of Telephony? <a href="https://mailchi.mp/c69181416ea6/waitlist">Sign up for email updates.</a></p>
<p>Questions, comments, feedback? Shoot me an email: <a href="mailto:hello@telephony.cc">hello@telephony.cc</a></p>
npm pack
2021-01-23T00:00:00Z
https://jesse.sh/npm-pack/
<p>When publishing on npm, how do you know that what you <em>intend</em>
to publish is what ends up getting published?</p>
<p>Worst case scenario a bad build config publishes secrets like a
<code>.env</code> file.</p>
<p>Given the following project:</p>
<pre><code>$ tree -a
.
├── .env
└── package.json
0 directories, 2 files
</code></pre>
<p>and those files' contents:</p>
<pre><code>$ cat package.json
{
"name": "bad-npm",
"version": "1.0.0"
}
$ cat .env
SUPER_SECRET=true
IF_ANYONE_CAN_READ_THIS="I am in big trouble"
</code></pre>
<p>Ok let's see what npm is preparing to publish:</p>
<pre><code>$ npm pack --dry-run
npm notice
npm notice 📦 bad-npm@1.0.0
npm notice === Tarball Contents ===
npm notice 64B .env
npm notice 46B package.json
</code></pre>
<p>ah shit. Probably shouldn't publish that, but...</p>
<pre><code>$ npm publish
npm notice
npm notice 📦 bad-npm@1.0.0
npm notice === Tarball Contents ===
npm notice 64B .env
npm notice 46B package.json
</code></pre>
<p>and sure enough it's now publicly available:</p>
<p>"<a href="https://registry.npmjs.org/bad-npm/-/bad-npm-1.0.0.tgz">https://registry.npmjs.org/bad-npm/-/bad-npm-1.0.0.tgz</a>"</p>
<p>to fix this you could ignore <code>.env</code> files but I think the safest
fix is an allow list with <code>files</code>:</p>
<pre><code>$ cat package.json
{
"name": "bad-npm",
"version": "1.0.0",
"files": []
}
$ npm pack --dry-run
npm notice
npm notice 📦 bad-npm@1.0.0
npm notice === Tarball Contents ===
npm notice 61B package.json
</code></pre>
<p>nice! Seems like npm should default to error if there are no files specified.</p>
Recursive Vue Components
2017-02-01T00:00:00Z
https://jesse.sh/recursive-vue-components/
<p>In this post, I'll walk through my process of creating a recursive Vue.js component, that is, a component
which can render itself.</p>
<p><a href="https://jshawl.github.io/recursive-vue-component/">Demo</a><br />
<a href="https://github.com/jshawl/recursive-vue-component/blob/gh-pages/index.html">Completed Code</a></p>
<h3>Wait, What? Why?!</h3>
<p>I recently had a need for building a custom file-browser, which allowed users to click through any number of folders
and files stored on their Dropbox account.</p>
<p>A directory listing is a classic example of recursion.</p>
<p>Imagine the following output of the <code>tree</code> command (a fancier, more gui-y <code>ls</code>):</p>
<pre><code>.
├── dir1/
│ ├── fileA
│ ├── fileB
│ └── fileC
├── dir2/
├── dir3/
└── file1
</code></pre>
<p>If I can solve the problem of listing the highest-up directory:</p>
<pre><code>.
├── dir1/
├── dir2/
├── dir3/
└── file1
</code></pre>
<p>then I will have solved the problem of listing any child directory as well:</p>
<pre><code>.
├── fileA
├── fileB
└── fileC
</code></pre>
<p>To reuse a solution, I'll need to use the same bit of code, a Vue component.</p>
<h3>Getting Set Up (<a href="https://github.com/jshawl/recursive-vue-component/commit/acd19c5">acd19c5</a>)</h3>
<p>Here's all the code you'll need to get started writing your first Vue component:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>app<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">el</span><span class="token operator">:</span> <span class="token string">'#app'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<h3>Define a Tree Component (<a href="https://github.com/jshawl/recursive-vue-component/commit/88346c5">88346c5</a>)</h3>
<p>First, a simple hello world template to make sure everything's working:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>app<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tree</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tree</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>x-template<span class="token punctuation">'</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>tree<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div<span class="token operator">></span><br /> Hello<br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h4>Notes</h4>
<ul>
<li>The <code>script</code> tag must have a <code>type</code> other than <code>text/javascript</code>. Try removing
the type attribute, and you'll see why you need it. Without <code>x-template</code>, the browser
will attempt to execute the content inside the tags as Javascript.</li>
<li>The template contents (everything inside the template script tag) must contain
<strong>one and only one</strong> root element. One element is ok. Two elements are <em>not</em> ok. Zero elements are <em>not</em> ok.</li>
</ul>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'tree'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#tree'</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h4>Notes</h4>
<ul>
<li>You must define a component before the <code>new Vue({...</code></li>
</ul>
<h3>Mix in some data (<a href="https://github.com/jshawl/recursive-vue-component/commit/4a6e34a">4a6e34a</a>)</h3>
<p>Without a data method present, our Vue component method is pretty useless.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> files <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'folder'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'dir1'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'folder'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'dir2'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'file'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'file1'</span><span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<p>Add the data to the <code>tree</code> component:</p>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'tree'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#tree'</span><span class="token punctuation">,</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> files<br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>And try printing out that data from within the template:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>x-template<span class="token punctuation">'</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>tree<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div<span class="token operator">></span><br /> <span class="token punctuation">{</span><span class="token punctuation">{</span>files<span class="token punctuation">}</span><span class="token punctuation">}</span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>It's not pretty.... yet, but even a few directives can meaningfully display the top-level directory
specified in <code>files</code>.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>x-template<span class="token punctuation">'</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>tree<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div<span class="token operator">></span><br /> <span class="token operator"><</span>div v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"file in files"</span><span class="token operator">></span><br /> <span class="token punctuation">{</span><span class="token punctuation">{</span>file<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span>span v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"file.tag == 'folder'"</span><span class="token operator">></span><span class="token operator">/</span><span class="token operator"><</span><span class="token operator">/</span>span<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3>Handling Click Events (<a href="https://github.com/jshawl/recursive-vue-component/commit/022a9fc">022a9fc</a>)</h3>
<p>In order to expand a folder, we can add event listeners to a particular element using the
<code>v-on:click</code> directive:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file in files<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file.tag == <span class="token punctuation">'</span>folder<span class="token punctuation">'</span><span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">v-on:</span>click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>toggleExpand()<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> {{file.name}}<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-else</span><span class="token punctuation">></span></span><br /> {{file.name}}<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>Attempting to click on one of the folders will throw a <code>ReferenceError</code> about the function
not being defined.</p>
<p>Define it in the component:</p>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'tree'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#tree'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">toggleExpand</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'togglin!'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token comment">//...</span><br /> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Rather than just log to the console, it'd be nice if a property on each of the folders were toggled, to visually represent whether or not
a folder should be expanded to review its children.</p>
<p>Unfortunately, it's impossible to toggle properties on each individual file
without a component for each folder.</p>
<h3>Adding a <code><folder/></code> component (<a href="https://github.com/jshawl/recursive-vue-component/commit/9b4bc33">9b4bc33</a>)</h3>
<h4>Modify the existing template</h4>
<p>Instead of printing a div for folder entries:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file in files<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file.tag == <span class="token punctuation">'</span>folder<span class="token punctuation">'</span><span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">v-on:</span>click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>toggleExpand()<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> {{file.name}}/<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-else</span><span class="token punctuation">></span></span><br /> {{file.name}}<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>We'll print a component instance:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file in files<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>folder</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file.tag == <span class="token punctuation">'</span>folder<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>folder</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">v-else</span><span class="token punctuation">></span></span><br /> {{file.name}}<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<h4>Create a template for the folder component</h4>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-template<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>folder<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div v<span class="token operator">-</span>on<span class="token operator">:</span>click<span class="token operator">=</span><span class="token string">"toggleExpand()"</span><span class="token operator">></span><br /> folder here<br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h4>Define a new component</h4>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'folder'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#folder'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">toggleExpand</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'togglin!'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p><strong>Note:</strong> I've removed the <code>methods</code> property from the <code>tree</code> component, and placed it in the new <code>folder</code> component.</p>
<h4>Pass the folder name into the component as an attribute</h4>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>folder</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file.tag == <span class="token punctuation">'</span>folder<span class="token punctuation">'</span><span class="token punctuation">"</span></span> <span class="token attr-name">:file</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>file<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>folder</span><span class="token punctuation">></span></span></code></pre>
<p>In order to access the <code>file</code> object passed in as an attribute, we need
to add a <code>props</code> attribute onto the component definition:</p>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'folder'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#folder'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'file'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">toggleExpand</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'togglin!'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>And print it out in the template:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-template<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>folder<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div v<span class="token operator">-</span>on<span class="token operator">:</span>click<span class="token operator">=</span><span class="token string">"toggleExpand()"</span><span class="token operator">></span><br /> <span class="token punctuation">{</span><span class="token punctuation">{</span>file<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">/</span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3>Toggle the open state (<a href="https://github.com/jshawl/recursive-vue-component/commit/8d5fcf9">8d5fcf9</a>)</h3>
<p>In order to toggle whether a folder displays its children or not, let's add the <code>data</code>
method to the <code>folder</code> component, and initialize it to false:</p>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'folder'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#folder'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'file'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">toggleExpand</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'togglin!'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function-variable function">data</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">isOpen</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Modify <code>toggleExpand()</code> to flip that value:</p>
<pre class="language-js"><code class="language-js"><span class="token function-variable function">toggleExpand</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>isOpen <span class="token operator">=</span> <span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>isOpen<br /><span class="token punctuation">}</span></code></pre>
<p>and display the value of <code>isOpen</code> inside the template:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-template<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>folder<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div v<span class="token operator">-</span>on<span class="token operator">:</span>click<span class="token operator">=</span><span class="token string">"toggleExpand()"</span><span class="token operator">></span><br /> <span class="token punctuation">{</span><span class="token punctuation">{</span>file<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">/</span><br /> <span class="token operator"><</span>span v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">'isOpen'</span><span class="token operator">></span>show children<span class="token operator"><</span><span class="token operator">/</span>span<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3>Displaying folder contents (<a href="https://github.com/jshawl/recursive-vue-component/commit/4134c5a">4134c5a</a>)</h3>
<p>In order to display a folder's contents, let's first modify the <code>files</code> array
to have files inside of folders:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> files <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'folder'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'dir1'</span><span class="token punctuation">,</span> <span class="token literal-property property">files</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'file'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'fileA'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'file'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'fileB'</span><span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'folder'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'dir2'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span> <span class="token string">'file'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'file1'</span><span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<p>Next, modify the template to display the child files (reusing the <code>tree</code> component)
if the given file has a truthy <code>files</code> attribute <em>and</em> the <code>isOpen</code> value is true:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-template<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>folder<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div<span class="token operator">></span><br /> <span class="token operator"><</span>div v<span class="token operator">-</span>on<span class="token operator">:</span>click<span class="token operator">=</span><span class="token string">"toggleExpand()"</span><span class="token operator">></span><br /> <span class="token punctuation">{</span><span class="token punctuation">{</span>file<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">/</span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /> <span class="token operator"><</span>tree v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">'isOpen && file.files'</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>tree<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>Lastly, we need to give the <code>tree</code> component a set of files to display, as it currently
will continue displaying the top node of the <code>files</code> array. We can accomplish this
with an attribute on the tree component:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-template<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>folder<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token operator"><</span>div<span class="token operator">></span><br /> <span class="token operator"><</span>div v<span class="token operator">-</span>on<span class="token operator">:</span>click<span class="token operator">=</span><span class="token string">"toggleExpand()"</span><span class="token operator">></span><br /> <span class="token punctuation">{</span><span class="token punctuation">{</span>file<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">/</span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /> <span class="token operator"><</span>tree v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">'isOpen && file.files'</span> <span class="token operator">:</span>children<span class="token operator">=</span><span class="token string">'file.files'</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>tree<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>and add the <code>props</code> attribute onto the <code>tree</code> component definition:</p>
<pre class="language-js"><code class="language-js">Vue<span class="token punctuation">.</span><span class="token function">component</span><span class="token punctuation">(</span><span class="token string">'tree'</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'#tree'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'children'</span><span class="token punctuation">]</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">files</span><span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>children <span class="token operator">||</span> files<br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h3>Bells and whistles</h3>
<p>The rest is a little CSS and a few more levels of files and folders.</p>
<p><a href="https://jshawl.github.io/recursive-vue-component/">Demo</a><br />
<a href="https://github.com/jshawl/recursive-vue-component/blob/gh-pages/index.html">Completed Code</a></p>
Customize Rails Collection Checkboxes
2017-01-25T00:00:00Z
https://jesse.sh/customize-rails-collection-checkboxes/
<p>If you are using Rails' form helper <a href="http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes"><code>collection_check_boxes</code></a>, it's not entirely obvious how you might
go about customizing the label's html.</p>
<h2>The "Normal Way"</h2>
<p>...or what frequently shows up in <a href="http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes">the documentation</a>.</p>
<p>Say we have the following Ruby classes:</p>
<pre class="language-rb"><code class="language-rb"><span class="token keyword">class</span> <span class="token class-name">Post</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base<br /> has_and_belongs_to_many <span class="token symbol">:authors</span><br /><span class="token keyword">end</span><br /><span class="token keyword">class</span> <span class="token class-name">Author</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base<br /> has_and_belongs_to_many <span class="token symbol">:posts</span><br /> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">name_with_initial</span></span><br /> <span class="token string-literal"><span class="token string">"</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">first_name<span class="token punctuation">.</span>first</span><span class="token delimiter punctuation">}</span></span><span class="token string">. </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">last_name</span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span><br /> <span class="token keyword">end</span><br /><span class="token keyword">end</span></code></pre>
<p>The easiest way to create a group of checkboxes is with <code>collection_check_boxes</code>:</p>
<pre class="language-erb"><code class="language-erb"><span class="token comment"><!-- app/views/carts/new.html.erb --></span><br /><span class="token erb language-erb"><span class="token delimiter punctuation"><%=</span><span class="token ruby language-ruby"> collection_check_boxes<span class="token punctuation">(</span><span class="token symbol">:post</span><span class="token punctuation">,</span> <span class="token symbol">:author_ids</span><span class="token punctuation">,</span> Author<span class="token punctuation">.</span>all<span class="token punctuation">,</span> <span class="token symbol">:id</span><span class="token punctuation">,</span> <span class="token symbol">:name_with_initial</span><span class="token punctuation">)</span> </span><span class="token delimiter punctuation">%></span></span></code></pre>
<p>This generates the following HTML:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post_author_ids_1<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post[author_ids][]<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">checked</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checked<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post_author_ids_1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>D. Heinemeier Hansson<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post_author_ids_2<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post[author_ids][]<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post_author_ids_2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>D. Thomas<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post_author_ids_3<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post[author_ids][]<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post_author_ids_3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>M. Clark<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post[author_ids][]<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hidden<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<h2>The Not-so-normal Way</h2>
<p>If you don't have access to the <code>Author</code> class, as was my case when working with
the Stripe API, you have two options.</p>
<h3>Add to the open ruby class</h3>
<p>Opening a class definition on the class for which you don't have access allows you
to define new methods on a class:</p>
<pre class="language-rb"><code class="language-rb"><span class="token keyword">class</span> <span class="token class-name">Author</span><br /> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">name_with_initial</span></span><br /> <span class="token comment"># code accessing instance</span><br /> <span class="token keyword">end</span><br /><span class="token keyword">end</span></code></pre>
<p>...or</p>
<h3>Pass a block to the form helper</h3>
<pre class="language-rb"><code class="language-rb">collection_check_boxes<span class="token punctuation">(</span><span class="token symbol">:post</span><span class="token punctuation">,</span> <span class="token symbol">:author_ids</span><span class="token punctuation">,</span> Author<span class="token punctuation">.</span>all<span class="token punctuation">,</span> <span class="token symbol">:id</span><span class="token punctuation">,</span> <span class="token symbol">:name_with_initial</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>b<span class="token operator">|</span><br /> b<span class="token punctuation">.</span>check_box<br /> b<span class="token punctuation">.</span>label <span class="token keyword">do</span><br /> b<span class="token punctuation">.</span>object<span class="token punctuation">.</span>first_name<span class="token punctuation">.</span>first <span class="token operator">+</span> <span class="token string-literal"><span class="token string">". "</span></span> <span class="token operator">+</span> b<span class="token punctuation">.</span>object<span class="token punctuation">.</span>last_name<br /> <span class="token keyword">end</span><br /><span class="token keyword">end</span></code></pre>
<p>Boom! The instance for each of the radio buttons is available on the <code>object</code> method
as seen above.</p>
Cacheing API Calls
2017-01-04T00:00:00Z
https://jesse.sh/cacheing-api-calls/
<p>I recently made a few upgrades to <a href="https://updog.co/">updog.co</a> which have decreased the Time To First Byte
dramatically.</p>
<h2>Before:</h2>
<p><img src="https://dl.dropbox.com/s/e0xz24r8a6chozo/Screenshot%202017-01-04%2005.30.49.png?dl=0" alt="" /></p>
<h2>After:</h2>
<p><img src="https://dl.dropbox.com/s/m2onzm5puvw0n7p/Screenshot%202017-01-04%2005.39.47.png?dl=0" alt="" /></p>
<h2>Wait, What?! How?!</h2>
<p>Updog uses memcached as its cache store, and requests to the Dropbox API were cached for 5
seconds to avoid exceeding Dropbox's API rate limits.</p>
<p>Here's some pseudo-ruby code:</p>
<pre class="language-rb"><code class="language-rb">Rails<span class="token punctuation">.</span>cache<span class="token punctuation">.</span>fetch<span class="token punctuation">(</span>the_content<span class="token punctuation">,</span> <span class="token symbol">expires_in</span><span class="token operator">:</span> <span class="token number">5.</span>seconds<span class="token punctuation">)</span> <span class="token keyword">do</span><br /> <span class="token comment"># If nothing found in the cache execute the following line</span><br /> <span class="token comment"># and write to the cache</span><br /> get_from_api the_content<br /><span class="token keyword">end</span></code></pre>
<p>This meant that even if a site went viral on <a href="http://updog.co/">updog.co</a>, requests for any particular URI would
never exceed more than 1 API call every 5 seconds, which is well within Dropbox's limits.</p>
<p>Imagine another type of user whose site is only looked at every couple of days. Because the cache is
only held for 5 seconds, this user's content would have to be looked up from the Dropbox API on
nearly every load:</p>
<pre><code>User -> updog.co -> Dropbox API -> updog.co -> User
</code></pre>
<p>What I really wanted was a way to move the Dropbox API call to the background, so
that the user never has to wait on the response from Dropbox:</p>
<pre><code> Dropbox API
\ /
User -> updog.co -> User
</code></pre>
<p>The solution was to cache the response from the Dropbox API forever.</p>
<p>On every inbound request to <a href="http://updog.co/">updog.co</a>, check the site's last modified time. If more than
5 seconds have elapsed since the last update, fetch the content again from the Dropbox
API in the background:</p>
<pre class="language-rb"><code class="language-rb">Rails<span class="token punctuation">.</span>cache<span class="token punctuation">.</span>fetch<span class="token punctuation">(</span>the_content<span class="token punctuation">)</span> <span class="token keyword">do</span><br /> <span class="token comment"># If nothing found in the cache execute the following line</span><br /> <span class="token comment"># and write to the cache</span><br /> get_from_api the_content<br /><span class="token keyword">end</span><br /><span class="token keyword">if</span> site_not_updated_in_last_5_seconds<br /> get_from_api_in_background the_content<br /> and_then_modify_the_update_time<br /><span class="token keyword">end</span></code></pre>
<p>I was able to handle the background processing with <a href="https://github.com/mperham/sidekiq">sidekiq</a></p>
<p>Here's the caching code in total</p>
<p>The caching code in total is available <a href="https://github.com/jshawl/updog/blob/0a829523544ed3db32d036c9bbd7ea5ce2694a21/app/models/site.rb#L73-L78">on GitHub</a></p>
Let's build a CRUD app with the Fetch API
2016-07-22T00:00:00Z
https://jesse.sh/lets-build-a-crud-app-with-the-fetch-api/
<p><a href="https://jshawl.github.io/stopgap-demo/">View demo</a>
| <a href="https://github.com/jshawl/stopgap-demo/blob/c27a02b07af090b3497c27ab8c8539a9fe5d8490/index.html">View code</a></p>
<p>I recently built an application - <a href="https://stopgap.store/">https://stopgap.store/</a> which is a simple way to get
up and running with an API with almost zero configuration.</p>
<p>In an effort to demo its capabilities, I thought I'd explore working with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">fetch API</a> as well.</p>
<p>Fetch is an experimental technology!</p>
<p><img src="https://jesse.sh/img/caniuse-fetch.png" alt="" /></p>
<ul>
<li>TOC
{:toc}</li>
</ul>
<h2>Initial Setup</h2>
<p>We're gonna do this thing entirely in a single html file:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todo<span class="token punctuation">'</span></span><span class="token punctuation">></span></span>Todo:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>text<span class="token punctuation">'</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todo<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todos<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>And a little bit of JavaScript before the closing <code></body></code> tag:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> form <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><br /><span class="token keyword">var</span> input <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'todo'</span><span class="token punctuation">)</span><br /><span class="token keyword">var</span> todos <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.todos'</span><span class="token punctuation">)</span></code></pre>
<h3>Setting up the API</h3>
<p>In lieu of setting up a local API server to persist our data, or authenticating with a third
party provider like Firebase, we can generate a throw-away api at <a href="https://stopgap.store/">https://stopgap.store/</a></p>
<p>Click on "Create Project" and copy the URL:</p>
<p><img src="https://jesse.sh/img/create-project.gif" alt="" /></p>
<p>Add the url as a variable:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> apiUrl <span class="token operator">=</span> <span class="token string">'https://stopgap.store/48277/todos'</span></code></pre>
<p><strong>Note:</strong> You can add whatever resource name you like to the end of the url.
I decided to use "todos"</p>
<p>All together now!</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todo<span class="token punctuation">'</span></span><span class="token punctuation">></span></span>Todo:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>text<span class="token punctuation">'</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todo<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todos<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">var</span> apiUrl <span class="token operator">=</span> <span class="token string">'https://stopgap.store/48277/todos'</span><br /> <span class="token keyword">var</span> form <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> input <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'todo'</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> todos <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.todos'</span><span class="token punctuation">)</span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<h2>Create</h2>
<p>A good start would be <code>console.log</code>ging whatever's in the input
when the user submits the form:</p>
<pre class="language-js"><code class="language-js">form<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"submit"</span><span class="token punctuation">,</span> create<span class="token punctuation">)</span><br /><span class="token keyword">function</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>input<span class="token punctuation">.</span>value<span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>To create something on the server, we need to send a POST request, with a JSON
string of the content we want to send.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl<span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'POST'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> input<span class="token punctuation">.</span>value<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>But instead of logging to the console, we'll need to create a new <code><li></code> and
append it to the existing <code><ul></code></p>
<pre class="language-js"><code class="language-js"><span class="token comment">//...</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>li<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><br /><span class="token keyword">function</span> <span class="token function">li</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> l <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">)</span><br /> l<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> todo<span class="token punctuation">.</span>text<br /> todos<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<h2>Read</h2>
<p>When the page loads, fetch all the todos from the server, and create <code><li></code>s for each of them.</p>
<pre class="language-js"><code class="language-js"><span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl <span class="token operator">+</span> <span class="token string">".json"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">todos</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> todos<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>li<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p><strong>Note</strong>: the addition of ".json" is required here
because the apiUrl serves HTML as well.</p>
<h2>Update</h2>
<p>An easy way to allow the user to edit inidividual todos would be to make each li <code>contenteditable</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">li</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /><span class="token comment">//...</span><br /> l<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"contenteditable"</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span></code></pre>
<p>and tell the server when the content is no longer being changed on the <code>blur</code> event:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">li</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /><span class="token comment">//...</span><br /> l<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"contenteditable"</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><br /> l<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">,</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><br /> l<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"blur"</span><span class="token punctuation">,</span> update<span class="token punctuation">)</span><br /> todos<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">function</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> id <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">)</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> id<span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'PATCH'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>innerText<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<h2>Delete</h2>
<p>For each list item, we can add a link to delete, though this will require
a small change or to to our <code>li()</code> function and <code>PATCH</code> request.</p>
<p>First, create a <code><span></code> child of the <code><li></code> and make that contenteditable.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">li</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> l <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> span <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"span"</span><span class="token punctuation">)</span><br /> span<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"contenteditable"</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><br /> span<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">,</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><br /> span<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> todo<span class="token punctuation">.</span>text<br /> span<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"blur"</span><span class="token punctuation">,</span> update<span class="token punctuation">)</span><br /> l<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>span<span class="token punctuation">)</span><br /> todos<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>Then, create a link to delete:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// in function li()</span><br /><span class="token keyword">var</span> a <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><br />a<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">'&times;'</span><br />a<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> remove<span class="token punctuation">)</span><br />a<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">,</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><br />l<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span></code></pre>
<p>and the function to handle the removal:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">remove</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> id <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">)</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> id<span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'DELETE'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> todos<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>parentNode<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>Add a little css to space things out:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">span + a</span> <span class="token punctuation">{</span><br /> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span><br /> <span class="token property">margin-left</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span><br /> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<h2>Complete code</h2>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /> <span class="token selector">span + a</span> <span class="token punctuation">{</span><br /> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span><br /> <span class="token property">margin-left</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span><br /> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todo<span class="token punctuation">'</span></span><span class="token punctuation">></span></span>Todo:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>text<span class="token punctuation">'</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todo<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>todos<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">var</span> apiUrl <span class="token operator">=</span> <span class="token string">'https://stopgap.store/48277/todos'</span><br /> <span class="token keyword">var</span> form <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> input <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'todo'</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> todos <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.todos'</span><span class="token punctuation">)</span><br /> form<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"submit"</span><span class="token punctuation">,</span> create<span class="token punctuation">)</span><br /> <span class="token keyword">function</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl<span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'POST'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> input<span class="token punctuation">.</span>value<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>li<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">function</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> id <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">)</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> id <span class="token operator">+</span> <span class="token string">".json"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'PATCH'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>innerText<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">function</span> <span class="token function">li</span><span class="token punctuation">(</span><span class="token parameter">todo</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> l <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> span <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"span"</span><span class="token punctuation">)</span><br /> span<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"contenteditable"</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><br /> span<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">,</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><br /> span<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> todo<span class="token punctuation">.</span>text<br /> span<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"blur"</span><span class="token punctuation">,</span> update<span class="token punctuation">)</span><br /> l<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>span<span class="token punctuation">)</span><br /> <span class="token keyword">var</span> a <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><br /> a<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">'&times;'</span><br /> a<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> remove<span class="token punctuation">)</span><br /> a<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">,</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><br /> l<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><br /> todos<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">function</span> <span class="token function">remove</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token keyword">var</span> id <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"data-id"</span><span class="token punctuation">)</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> id<span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'DELETE'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> todos<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>parentNode<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl <span class="token operator">+</span> <span class="token string">".json"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">todos</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> todos<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>li<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
Express from Scratch
2016-04-20T00:00:00Z
https://jesse.sh/express-from-scratch/
<p>Here's a pre-recorded screencast of a talk I gave at the <a href="http://www.meetup.com/node-dc/events/230290575/">NodeDC Meetup</a></p>
<iframe src="https://player.vimeo.com/video/163548396" width="640" height="400" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
<p>And the code is on GitHub! - <a href="https://github.com/jshawl/express-from-scratch">https://github.com/jshawl/express-from-scratch</a></p>
<p><img src="https://jesse.sh/img/express-1.jpg" alt="" /></p>
<p><img src="https://jesse.sh/img/express-2.jpg" alt="" /></p>
Dependency Injection 101
2015-09-20T00:00:00Z
https://jesse.sh/dependency-injection-101/
<p>"Dependency injection" is a somewhat scary term to explain loading modules and making sure
memory footprint is minimal.</p>
<p>I've been thinking a lot about Angular's <code>$injector</code> and how I might build a dependency injector
from scratch.</p>
<h2>A thing that creates things</h2>
<p>À la angular, I wanted an <code>injector</code> as the global namespace, a <code>module</code> method that allows
me to both read and create modules. Let's start with creating a module.</p>
<p>We want the interface to be something like</p>
<pre class="language-js"><code class="language-js">injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">"egg"</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token function-variable function">hatch</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"chirp chirp"</span><span class="token punctuation">)</span> <br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Where the name of the module is the first argument, the second argument is
an array of dependencies, and the third argument is the module's prototype (methods and attributes).</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> injector <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// global namespace</span><br /> <span class="token function-variable function">module</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> dependencies<span class="token punctuation">,</span> prototype</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> module <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// a custom template for modules</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> name<span class="token punctuation">,</span><br /> <span class="token literal-property property">dependencies</span><span class="token operator">:</span> dependencies<br /> <span class="token punctuation">}</span><br /> <span class="token keyword">for</span><span class="token punctuation">(</span> <span class="token keyword">var</span> attr <span class="token keyword">in</span> prototype<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> module<span class="token punctuation">[</span>attr<span class="token punctuation">]</span> <span class="token operator">=</span> prototype<span class="token punctuation">[</span>attr<span class="token punctuation">]</span> <span class="token comment">// attach proto methods to module</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> module<br /> <span class="token keyword">return</span> module<br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">_modules</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> egg <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">'egg'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token function-variable function">hatch</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"chirp chirp"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><br />egg<span class="token punctuation">.</span><span class="token function">hatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>If there is only the first argument, the injector assumes the module has already been defined and returns the module's prototype.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> injector <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// global namespace</span><br /> <span class="token function-variable function">module</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> dependencies<span class="token punctuation">,</span> prototype</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>dependencies<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>name<span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">var</span> module <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// a custom template for modules</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> name<span class="token punctuation">,</span><br /> <span class="token literal-property property">dependencies</span><span class="token operator">:</span> dependencies<br /> <span class="token punctuation">}</span><br /> <span class="token keyword">for</span><span class="token punctuation">(</span> <span class="token keyword">var</span> attr <span class="token keyword">in</span> prototype<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> module<span class="token punctuation">[</span>attr<span class="token punctuation">]</span> <span class="token operator">=</span> prototype<span class="token punctuation">[</span>attr<span class="token punctuation">]</span> <span class="token comment">// attach proto methods to module</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> module<br /> <span class="token keyword">return</span> module<br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">_modules</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br />injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">"egg"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">hatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>Now that we can read and write modules, let's try seeing if a module's dependencies have been loaded before calling methods.</p>
<p>The angular-style of injecting dependencies is:</p>
<pre class="language-js"><code class="language-js">injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">'name of module'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'array'</span><span class="token punctuation">,</span><span class="token string">'of'</span><span class="token punctuation">,</span><span class="token string">'dependencies'</span><span class="token punctuation">,</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">array<span class="token punctuation">,</span> <span class="token keyword">of</span><span class="token punctuation">,</span> dependencies</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token comment">// if each module name in the array is defined and the last element</span><br /> <span class="token comment">// is a function, invoke the function with each dependency as an argument.</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<h2>Raising Errors when Dependencies Are Not Met</h2>
<p>If the specified dependency is not already defined in <code>injector._modules</code>, raise an error:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">for</span><span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> dependencies<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> dependency <span class="token operator">=</span> dependencies<span class="token punctuation">[</span>i<span class="token punctuation">]</span><br /> <span class="token keyword">var</span> loaded <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>dependency<span class="token punctuation">]</span> <span class="token operator">?</span> <span class="token boolean">true</span> <span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>loaded<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"Dependency not met: "</span> <span class="token operator">+</span> dependency<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p><a href="https://repl.it/BJrx/1">https://repl.it/BJrx/1</a></p>
<h2>Passing Modules to functions</h2>
<p>While in the loop, if the <code>typeof</code> the element is a function, we can assume it is the last
element of the array, and invoke it with each of the modules.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token keyword">typeof</span> dependency <span class="token operator">==</span> <span class="token string">"function"</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> modules <span class="token operator">=</span> dependencies<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">d</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// map names of modules to actual modules</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>d<span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// preserve context</span><br /> <span class="token keyword">return</span> <span class="token function">dependency</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> modules<span class="token punctuation">)</span> <span class="token comment">// unknown number of dependencies</span><br /><span class="token punctuation">}</span></code></pre>
<p>This code must come before the error-raising code above. That is, a <code>return</code> is necessary after the
function's invocation to prevent the "module not met" dependency error.</p>
<p>We now have an interface like this:</p>
<pre class="language-js"><code class="language-js">injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">'chicken'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'egg'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">egg</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> egg<span class="token punctuation">.</span><span class="token function">hatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p><a href="https://repl.it/BJrx/2">https://repl.it/BJrx/2</a></p>
<h2>Next Steps</h2>
<p>Now that we have a way to create, read, and depend on modules, where do we go from here?</p>
<p>I was thinking it would be nice to be able to asynchronously load modules, but I'll leave that for another day.</p>
<p>Here's the dependency injector all together:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> injector <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// global namespace</span><br /> <span class="token function-variable function">module</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> dependencies<span class="token punctuation">,</span> prototype</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>dependencies<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>name<span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">for</span><span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> dependencies<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> dependency <span class="token operator">=</span> dependencies<span class="token punctuation">[</span>i<span class="token punctuation">]</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token keyword">typeof</span> dependency <span class="token operator">==</span> <span class="token string">"function"</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">var</span> modules <span class="token operator">=</span> dependencies<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">d</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// map names of modules to actual modules</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>d<span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// preserve context</span><br /> <span class="token keyword">return</span> <span class="token function">dependency</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> modules<span class="token punctuation">)</span> <span class="token comment">// unknown number of dependencies</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">var</span> loaded <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>dependency<span class="token punctuation">]</span> <span class="token operator">?</span> <span class="token boolean">true</span> <span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>loaded<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"Dependency not met: "</span> <span class="token operator">+</span> dependency<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">var</span> module <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// a custom template for modules</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> name<span class="token punctuation">,</span><br /> <span class="token literal-property property">dependencies</span><span class="token operator">:</span> dependencies<br /> <span class="token punctuation">}</span><br /> <span class="token keyword">for</span><span class="token punctuation">(</span> <span class="token keyword">var</span> attr <span class="token keyword">in</span> prototype<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> module<span class="token punctuation">[</span>attr<span class="token punctuation">]</span> <span class="token operator">=</span> prototype<span class="token punctuation">[</span>attr<span class="token punctuation">]</span> <span class="token comment">// attach proto methods to module</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>_modules<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> module<br /> <span class="token keyword">return</span> module<br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">_modules</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br />injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">'egg'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">{</span><br /> <span class="token function-variable function">hatch</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"chirp chirp"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><br />injector<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">'chicken'</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'egg'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">egg</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> egg<span class="token punctuation">.</span><span class="token function">hatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p><a href="https://repl.it/BJrx/4">https://repl.it/BJrx/4</a> | <a href="https://github.com/jshawl/dependency-injection">https://github.com/jshawl/dependency-injection</a></p>
[book] The Art of Unix Programming
2015-06-02T00:00:00Z
https://jesse.sh/[book]-the-art-of-unix-programming/
<p>I just finished <a href="http://www.catb.org/esr/writings/taoup/html/">The Art of Unix Programming</a> and I highly recommend it. I ended up reading the book cover to cover but it's written in such a way that you can skip around if you like. Despite being last updated over 12 years ago, the book is surprisingly relevant today and I wanted to document some of my favorite parts:</p>
<h2>Configuration</h2>
<p>Over the years I've noticed patterns in both configuration files and command line options but couldn't quite put my finger on how they were related.</p>
<p>There are generally three ways to configure an application.</p>
<ol>
<li>In the /etc/ directory</li>
<li>Hidden file in $HOME</li>
<li>Command line options.</li>
</ol>
<h3>/etc/ configuration files</h3>
<p>Configuration files in the /etc/ directory are meant to be edited by the system administrator, not the user.</p>
<p>Examples include:</p>
<ul>
<li>/etc/nginx/nginx.conf
<ul>
<li>System wide configuration for the nginx web server</li>
</ul>
</li>
<li>/etc/profile
<ul>
<li>Similar to a system-wide bash profile</li>
</ul>
</li>
<li>/etc/mysql/my.cnf
<ul>
<li>System wide configuration for the MySQL database server</li>
</ul>
</li>
</ul>
<h3>.rc configuration files</h3>
<p>Configuration files in the users directory are often hidden and end with "rc", which stands for run control. User configuration files are meant to be edited infrequently and contain configuration options specific to the user running the application.</p>
<p>Examples include:</p>
<ul>
<li>~/.vimrc
<ul>
<li>User specific editor configuration</li>
</ul>
</li>
<li>~/.gitconfig
<ul>
<li>User specific aliases and output configuration</li>
</ul>
</li>
<li>~/.netrc
<ul>
<li>User login and initialization information</li>
</ul>
</li>
</ul>
<h3>Command line arguments</h3>
<p>Command line arguments are another way to configure an application at runtime with the understanding that the user will frequently change these options.</p>
<h4><code>-</code> vs <code>--</code></h4>
<p><code>-</code> options are usually followed by a single case letter</p>
<p>The single hyphen is an artifact from using teletypes - where holding down the shift key required serious effort! Instead of the more logical <code>+</code> choice for adding options, <code>-</code> is used to facilitate typing.</p>
<p><code>--</code> options are usually followed by a lowercase word, separated by hyphens</p>
<blockquote>
<p>The GNU style uses option keywords (rather than keyword letters) preceded by two hyphens. It evolved years later when some of the rather elaborate GNU utilities began to run out of single-letter option keys (this constituted a patch for the symptom, not a cure for the underlying disease). It remains popular because GNU options are easier to read than the alphabet soup of older styles.</p>
</blockquote>
Hoisting Can Be Dangerous
2015-04-27T00:00:00Z
https://jesse.sh/hoisting-can-be-dangerous/
<p>Hoisting is often convenient in JavaScript. It allows us to call functions before they’re defined:</p>
<pre class="language-js"><code class="language-js"><span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'oh hey'</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>If you define a function inside of a conditional, you run the risk of having a function being overwritten via hoisting:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">function</span> <span class="token function">truthy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> document<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token string">"true is true!"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span> <span class="token keyword">else</span><span class="token punctuation">{</span><br /> <span class="token keyword">function</span> <span class="token function">truthy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> document<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token string">"true is false!"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><span class="token function">truthy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// will always log "true is false!"</span></code></pre>
Testing Webhooks Locally
2015-02-28T00:00:00Z
https://jesse.sh/testing-webhooks-locally/
<p>I recently built an application - <a href="http://restful.link/">restful.link</a>
in order to inspect the data that was sent as a webhook, like information
about about any new pull request on GitHub.</p>
<p>I had to keep manually opening pull requests on <a href="http://github.com/">github.com</a> anytime I wanted to
process the data that was posted. This was time consuming, so I implemented
a new feature which allows users to proxy new requests and resend existing
requests to a local server.</p>
<p>Here’s how you can start testing webhooks locally, using GitHub as an example:</p>
<h2>Create a Restful Link</h2>
<p>Visit <a href="http://restful.link/">restful.link</a> and create a new link.</p>
<p><img src="https://jesse.sh/img/create-link.gif" alt="" /></p>
<h2>Set up a Webhook</h2>
<p>Visit the repository you’d like to listen on for events, and add the
URL that was just generated.</p>
<p><img src="https://jesse.sh/img/add-webhook.png" alt="" /></p>
<p>If you revisit the restful link you created, you’ll see that GitHub
sent a test event.</p>
<h2>Enable Proxy to Localhost</h2>
<p>Click on the Proxy to localhost checkbox, specify the port that
your local server is running on, and test out the connection.</p>
<p><img src="https://jesse.sh/img/proxy-new.gif" alt="" /></p>
<p>Be sure to enable cross origin resource sharing (from restful.link to localhost)
or you’ll get a "Connection refused" error even if your server is running
at the specified port.</p>
<h2>Create a new Pull Request</h2>
<p>Really, we just need to do anything that will trigger the configured webhook.</p>
<p>New requests will be sent to restful.link and automatically updated
in your browser, as well as sent to your local server.</p>
<h2>Resend requests</h2>
<p>If you’d like to resend any one request to your local server, click the "Resend to localhost"
link.</p>
<p><img src="https://jesse.sh/img/proxy-resend.gif" alt="" /></p>
Alias Your Referral Codes
2015-02-21T00:00:00Z
https://jesse.sh/alias-your-referral-codes/
<p>When recommending any new service like Dropbox or DigitalOcean,
I send my referral codes to the potential customer in hopes that
they’ll sign up, and my account will be credited accordingly.</p>
<p>Instead of having to look up these referral codes for each of these
interactions, I’ve created a few nginx aliases to make the links
easier to share.</p>
<p>In my domain’s nginx configuration file, I’ve added:</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># /etc/nginx/sites-enabled/jshawl.com</span><br /><br />server <span class="token punctuation">{</span><br /> listen <span class="token number">80</span><span class="token punctuation">;</span><br /> server_name jshawl.com<span class="token punctuation">;</span><br /><br /> location /do <span class="token punctuation">{</span><br /> rewrite ^/do?$ https://www.digitalocean.com/?refcode<span class="token operator">=</span>01b24a40b88f permanent<span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><br /><br /> location /db <span class="token punctuation">{</span><br /> rewrite ^/db?$ https://db.tt/y3JvNpU permanent<span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>You can also alias fequently used urls like the jQuery cdn:</p>
<pre class="language-bash"><code class="language-bash">location /jq <span class="token punctuation">{</span><br /> rewrite ^/jq?$ https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js permanent<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>The above url will even work in a script tag:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>http://jshawl.com/jq<span class="token punctuation">'</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>text/javascript<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>I can now share <a href="http://jshawl.com/do">jshawl.com/do</a>, <a href="http://jshawl.com/db">jshawl.com/db</a>, and <a href="http://jshawl.com/jq">jshawl.com/jq</a>
without having to lookup the actual urls.</p>
Undo `git clean`
2015-02-07T00:00:00Z
https://jesse.sh/undo-%60git-clean%60/
<p>Unfortunately, undoing a <code>git clean</code> is impossible, as C’s <code>unlink()</code> is <a href="https://github.com/git/git/blob/9874fca7122563e28d699a911404fc49d2a24f1c/builtin/clean.c#L987">called</a>
on each of the untracked files (and directories if <code>-d</code> is supplied).</p>
<p>I was working on a rails application earlier this week and wanted to implement
a third party login authentication system. So I created a feature branch and
began working on implementing the OAuth flow.</p>
<p>I created a new file to store all of my API keys and secrets, and quickly thereafter
added this file to my <code>~/.gitignore</code> so as not to track sensitive information.</p>
<p>Before I could complete the feature, I had to switch back to master to push a quick
bug-fix. In the heat of the moment, I wanted to clean my working directory and
ran <code>git clean -fd</code> to remove all untracked files and directories.</p>
<p>Unfortunately, this removed the file containing all the API keys and secrets, and
I had to visit each of the API provider’s sites to recover them.</p>
<p>To prevent this sort of thing from happening in the future, I created a git alias
named <code>git clear</code>.</p>
<p>If you add this to your <code>~/.gitconfig</code>, you won’t permanently lose your untracked files.</p>
<pre><code>[alias]
clear = stash --keep-index --include-untracked
</code></pre>
<p>This will stash all and only your untracked files, and the <code>--keep-index</code> option will retain
the state of the staging area.</p>
<p>If you accidentally "remove" untracked files with the new alias:</p>
<pre><code>$ git clear
</code></pre>
<p>You can retrieve them with <code>git stash pop</code>. If you’ve stashed a few things since
running <code>git clear</code>, you’ll have to specify the stash number, which you can find
with <code>git stash list</code>.</p>
Contributing to Ruby gems
2014-12-20T00:00:00Z
https://jesse.sh/contributing-to-ruby-gems/
<p>I recently came across a gem for handling user authentication with sinatra -
<a href="https://github.com/pmonfort/sinatra-simple-authentication">sinatra simple authentication</a>.
This gem was easy to install and use, but the log in and sign up views
were hardcoded in Haml.</p>
<p>This meant I couldn’t take advantage of Sinatra’s layout.erb global view, and
any shared markup (navigation, footer, etc.) would have to be copied and pasted
into each of the views, <em>and</em> be written in Haml!!</p>
<p>So I decided to fork the gem and convert the views to use erb.</p>
<h2>Fork and clone the repo</h2>
<p>First, you’ll need to fork the repo on <a href="https://github.com/">github</a> and clone your fork:</p>
<pre><code>$ git clone git@github.com:jshawl/sinatra-simple-authentication
</code></pre>
<h2>Build the gem locally</h2>
<p>First install dependencies:</p>
<pre><code>$ bundle install
</code></pre>
<p>And build the gem:</p>
<pre><code>$ gem build sinatra-simple-authentication.gemspec
</code></pre>
<h2>Require the local gem from another ruby app</h2>
<p>In a separate directory, you’ll need a ruby application with a gemfile and an app.rb
to see the changes that you make to the gem - i.e. link to the local one.</p>
<p>Here’s the one I’m working with - <a href="https://github.com/jshawl/sinatra-authentication/tree/6536b6732edcc37ac85fe6baa5330eecabf80a54">view on github</a></p>
<p>In the Gemfile, add the path to the local gem:</p>
<div class="highlight"><pre><span class="gh">diff --git a/Gemfile b/Gemfile</span>
<span class="gh">index 418bda9..770b9f6 100644</span>
<span class="gd">--- a/Gemfile</span>
<span class="gi">+++ b/Gemfile</span>
<span class="gu">@@ -7,5 +7,5 @@ gem 'activerecord'</span>
gem 'sinatra-contrib'
gem 'sinatra-activerecord'
gem 'pg'
<span class="gd">-gem 'sinatra-simple-authentication'</span>
<span class="gi">+gem 'sinatra-simple-authentication', :path => "~/Sites/sinatra-simple-authentication"</span>
gem 'rack-flash3'
</pre></div>
<p>Install the dependencies from this new Gemfile:</p>
<pre><code>$ bundle install
</code></pre>
<p>And run the application:</p>
<pre><code>$ bundle exec ruby app.rb
</code></pre>
<p>The bundle exec tripped me up a bit at first. Without it, I kept getting:</p>
<pre><code>kernel_require.rb:55:in `require': cannot load such file -- sinatra/simple-authentication (LoadError)
</code></pre>
<p>when trying to run my application with <code>ruby app.rb</code>.</p>
<p><code>bundle exec</code> is required to load the local gem we installed with <code>bundle install</code>. The <a href="http://bundler.io/man/bundle-exec.1.html">man page</a> says it perfectly:</p>
<blockquote>
<p>Essentially, if you would normally have run something like <code>rspec spec/my_spec.rb</code>, and you want to use the gems specified in the Gemfile(5) and installed via bundle install(1), you should run <code>bundle exec rspec spec/my_spec.rb</code>.</p>
</blockquote>
<h2>Make changes</h2>
<p>Now you can freely edit the files in your local gem's folder. Here are the changes I made to the sinatra simple
authentication gem - <a href="https://github.com/jshawl/sinatra-simple-authentication/compare/ff7064dcadde23ee3b7d6be3383a80f8f1679ac6...7f039d63758c0af6472b9bd1aeab260fcff024c5">view on github</a></p>
<h2>Pull request?</h2>
<p>If you push your changes back to github, others can use your fork of the gem by
specifying a git url and branch name in their Gemfile:</p>
<div class="highlight"><pre><span class="gh">diff --git a/Gemfile b/Gemfile</span>
<span class="gh">index 418bda9..39ae19b 100644</span>
<span class="gd">--- a/Gemfile</span>
<span class="gi">+++ b/Gemfile</span>
<span class="gu">@@ -7,5 +7,5 @@ gem 'activerecord'</span>
gem 'sinatra-contrib'
gem 'sinatra-activerecord'
gem 'pg'
<span class="gd">-gem 'sinatra-simple-authentication'</span>
<span class="gi">+gem 'sinatra-simple-authentication', :git => "https://github.com/jshawl/sinatra-simple-authentication.git", :branch => "master"</span>
</pre></div>
<p>If you think other users of this gem would benefit from your changes, submit a pull request on github!</p>
Mercurial for Git Evangelists
2014-12-08T00:00:00Z
https://jesse.sh/mercurial-for-git-evangelists/
<p>I’ve been playing around with mercurial as of late as a way to enhance what I know about git
and version control in general. These are my in-progress notes.</p>
<h2>A lot of things are familiar</h2>
<ul>
<li><code>hg help</code> behaves like <code>git help</code></li>
<li><code>hg init</code> behaves like <code>git init</code>.</li>
<li><code>hg status</code> behaves like <code>git status</code>.
<ul>
<li>but with more cryptic responses: <code>$ touch newfile && hg status #=> ? newfile</code></li>
<li>no output indicates a clean working directory</li>
<li><code>hg sum</code> tells you the state of the working directory</li>
</ul>
</li>
<li><code>hg add</code> behaves like <code>git add <file></code></li>
<li><code>hg commit</code> behaves like <code>git commit</code>.</li>
</ul>
<h2>Revisions, not SHA1s</h2>
<p><code>hg log</code> is similar to <code>git log</code>, with a few important differences.</p>
<pre><code>$ hg log --graph --style=compact
@ 2[tip] 3afb761704c7 2014-12-06 11:14 -0500 jesse
| third commit
|
o 1 2d9ef30807a4 2014-12-06 11:09 -0500 jesse
| second commit
|
o 0 ff70fcdc63f9 2014-12-06 11:07 -0500 jesse
initial commit
</code></pre>
<p>Instead of SHA1 hashes to label commits, mercurial uses revision numbers. This means reverting changes
is easy as <code>hg update 1</code> or <code>hg update 0</code>. The <code>@</code> symbol indicates which revision your working directory is in, ie
git’s <code>HEAD</code>.</p>
<h2>Multiple HEADs</h2>
<p><code>hg update</code> feels a bit like <code>git reset</code>, but is less dangerous in terms of its ability to rewrite history.</p>
Async API Calls with Pagination
2014-11-28T00:00:00Z
https://jesse.sh/async-api-calls-with-pagination/
<p>I recently created a landing page for my <a href="http://jshawl.github.io/">jshawl.github.io</a> domain
and wanted to get all of my public repos from the github api.</p>
<p>At first, I considered writing a little ruby script, triggered by cron job that would
fetch all of my repos and save the output as a JSON file.</p>
<p>This would allow me to reuse an existing <a href="http://codepen.io/jshawl/pen/fvAmk">codepen</a> to display
and filter all of my repos.</p>
<p>Instead, I thought I’d just use JavaScript™</p>
<h3>Thinking Async</h3>
<pre class="language-js"><code class="language-js">repos <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br />$<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'https://api.github.com/users/jshawl/repos'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span> <span class="token parameter">res</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> repos<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span> res <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> repos <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// or render( repos );</span></code></pre>
<p>This code won’t work as you might expect. Because the ajax call is asynchronous, the <code>console.log( repos )</code> is
called before the http request finishes its round trip.</p>
<p>I've got two options:</p>
<ol>
<li>Add the render logic to the ajax callback</li>
</ol>
<ul>
<li>hopefully, the pagination API calls will come back in the right order.</li>
</ul>
<ol start="2">
<li>Create my own callback to call a render method after everything is retrieved</li>
</ol>
<p>It will be easier to maintain this site and reuse parts of its code if I separate my
concerns and go with option 2. Also, using a callback means I'll be able to manipulate or sort
all of the data before rendering it on screen.</p>
<h3>Put a Callback on it</h3>
<pre class="language-js"><code class="language-js"><br /><span class="token keyword">function</span> <span class="token function">getRepos</span><span class="token punctuation">(</span> <span class="token parameter">url<span class="token punctuation">,</span> repos<span class="token punctuation">,</span> callback</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> $<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span> url<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span> <span class="token parameter">res</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> res<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span> <span class="token keyword">function</span><span class="token punctuation">(</span> <span class="token parameter">repo</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> repos<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span> repo <span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br />repos <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token function">getRepos</span><span class="token punctuation">(</span><span class="token string">'https://api.github.com/users/jshawl/repos'</span><span class="token punctuation">,</span> repos<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> repos <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /></code></pre>
<p>Setting up a function and an anonymous callback ensures I can keep my render method outside of the ajax's callback.
This is important because I don't want to call the render method each time an http request is successfully made.
Ideally, I'd like to sort or filter the data before displaying the entire response.</p>
<h3>Getting the next page</h3>
<p>Getting the next page is just a matter of looking in the headers for a link
with <code>rel="next"</code>. If one is found, recursively find the next set of repos. If one is
not found, call the callback to tell our first <code>getRepos()</code> call that all of the
requests have completed.</p>
<p>Notice I've added two optional arguments to <code>$.get</code>'s anonymous callback. <code>req</code> contains
the link for the next page.</p>
<pre class="language-js"><code class="language-js"><br /><span class="token keyword">function</span> <span class="token function">getRepos</span><span class="token punctuation">(</span> <span class="token parameter">url<span class="token punctuation">,</span> repos<span class="token punctuation">,</span> callback</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> $<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span> url<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span> <span class="token parameter">res<span class="token punctuation">,</span> textStatus<span class="token punctuation">,</span> req</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> res<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span> <span class="token keyword">function</span><span class="token punctuation">(</span> <span class="token parameter">repo</span> <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> repos<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span> repo <span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> nextUrl <span class="token operator">=</span> req<span class="token punctuation">.</span><span class="token function">getAllResponseHeaders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex"><(.*)>; rel="next"</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span> nextUrl <span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token function">getRepos</span><span class="token punctuation">(</span> nextUrl<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> repos<span class="token punctuation">,</span> callback <span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br />repos <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token function">getRepos</span><span class="token punctuation">(</span><span class="token string">'https://api.github.com/users/jshawl/repos'</span><span class="token punctuation">,</span> repos<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> repos <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /></code></pre>
Interactive Rebasing Considered Harmful
2014-10-31T00:00:00Z
https://jesse.sh/interactive-rebasing-considered-harmful/
<p>Submitting pull requests with few files changed and a clean history means cleaning up your commits. One common solution is to do an interactive rebase and squash commits to rewrite history.</p>
<p>While on a feature branch, I would <code>git rebase -i master</code>, which requires me to remember a few things:</p>
<ol>
<li>Have I pushed this branch to a shared remote?</li>
<li>What changed in each commit?</li>
</ol>
<p>I often forget the answer to both of these questions, and undoing an interactive rebase can be a total pain.</p>
<p>Instead of the interactive rebase, try <code>git merge --squash</code></p>
<p>While on your <code>master</code> branch:</p>
<p><code>$ git merge --squash branch-name</code></p>
<p>This will merge your feature branch into master, but stop before committing. Typing <code>git status</code> shows us that every addition and deletion
from the feature branch has been staged and is ready for commit.</p>
<p>You could unstage files one by one with <code>git reset HEAD <file></code>, though I prefer to unstage everything with <code>git reset HEAD</code>, and add the changes I need
back to the staging area.</p>
<p>Finally, make the commit. It might be helpful to reference where these changes came from, so I usually reference the feature branch name.</p>
<p><code>$ git commit -m "implement feature 2; merge branch feature-branch"</code></p>
<p>In effect, we're leaving our feature branch and all of its history intact should we need to reference past commits (yes even fix-typo commits).</p>
<p>Since we did not rewrite the history of the feature branch, there's no need to worry if you pushed that branch or not.</p>
Rails JSON Virtual Attributes
2014-10-30T00:00:00Z
https://jesse.sh/rails-json-virtual-attributes/
<p>I was working on a rails application that responded with the following JSON:</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><br /> <span class="token punctuation">{</span> <br /> <span class="token string-property property">"id"</span><span class="token operator">:</span><span class="token number">14</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"title"</span><span class="token operator">:</span><span class="token string">"post title"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"description"</span><span class="token operator">:</span><span class="token string">"my awesome description"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"user_id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"created_at"</span><span class="token operator">:</span><span class="token string">"2014-10-29T02:50:01.707Z"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"updated_at"</span><span class="token operator">:</span><span class="token string">"2014-10-29T02:51:38.481Z"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<p>Even though I have access to the <code>user_id</code> attribute of this post, I wanted to be able to show the user name or email without having to make another HTTP request.</p>
<p>To add a new attribute to this JSON, I needed to add a few methods to extend <code>ActiveRecord::Base</code>'s <code>as_json</code> method.</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">Post</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base<br /><br /> belongs_to <span class="token symbol">:user</span><br /><br /> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">as_json</span></span><span class="token punctuation">(</span>options<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token keyword">super</span><span class="token punctuation">.</span>as_json<span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">.</span>merge<span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token symbol">user_email</span><span class="token operator">:</span> get_user_email<span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token keyword">end</span><br /><br /> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">get_user_email</span></span><br /> <span class="token keyword">self</span><span class="token punctuation">.</span>user <span class="token operator">&&</span> <span class="token keyword">self</span><span class="token punctuation">.</span>user<span class="token punctuation">.</span>email<br /> <span class="token keyword">end</span><br /><br /><span class="token keyword">end</span></code></pre>
<p>The <code>super.as_json(</code>... adds whatever attributes you specify for each one of the records from your model.</p>
<p>Since these methods are in the <code>Post</code> class, we can access the current post with <code>self</code>.</p>
<p>If there is no user for a given post, <code>self.user.email</code> would throw an undefined method for <code>nil:NilClass</code>,
so I prepend that with <code>self.user &&</code> to make sure the user exists first.</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><br /> <span class="token punctuation">{</span> <br /> <span class="token string-property property">"id"</span><span class="token operator">:</span><span class="token number">14</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"title"</span><span class="token operator">:</span><span class="token string">"post title"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"description"</span><span class="token operator">:</span><span class="token string">"my awesome description"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"user_id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"created_at"</span><span class="token operator">:</span><span class="token string">"2014-10-29T02:50:01.707Z"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"updated_at"</span><span class="token operator">:</span><span class="token string">"2014-10-29T02:51:38.481Z"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"user_email"</span><span class="token operator">:</span><span class="token string">"jesse@jshawl.com"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
Link Workflow
2014-10-27T00:00:00Z
https://jesse.sh/link-workflow/
<p>I've recently removed the <code>/finds-links</code> url from my site, which was a jQuery plugin powered by pocket.</p>
<p>This project, "The Q" has come back to life as a rails application that lets users expose links saved in
their Pocket account. Check it out at <a href="http://the--q.herokuapp.com/">http://the--q.herokuapp.com/</a>. I might bring back the <code>/finds-links</code> page
by collecting <a href="http://the--q.herokuapp.com/jshawl/css.json">JSON from The Q</a>.</p>
<p>I wanted to share / document my workflow for finding and saving links.</p>
<p>Currently, most everything goes through Pocket.</p>
<p>I use Feedly and Twitter as my main sources of link-based news, and both of these applications
offer a "Save to Pocket" one-click solution.</p>
<p>But, I've become a full-time pinboard user as of late! <a href="https://pinboard.in/u:jshawl">https://pinboard.in/u:jshawl</a> It's worth way more
than the initial one-time sign up fee of ~$10, and I recommend Pinboard without hesitation. Heck, I even
went for the archival account! (an additional $25 per year).</p>
<blockquote>
<p>Are you on pinboard? If so let me know!</p>
</blockquote>
<p>To facilitate adding links to pinboard, I've set up an IFTTT recipe that saves all new Pocket items
to Pinboard and marks them "to read", Pinboard's version of Pocket's "read later".</p>
<p>Anything that I actually want to read later, or even just look at again for tagging purposes goes into
my Pinboard <a href="https://pinboard.in/u:jshawl/unread/">unread feed</a>.</p>
<p>I'm a bit worried now about saving too many links. That is, I don't want to have so many that I'm no longer able
to find just the link I was looking for. However, Pinboard's full text search feature has worked for me thus far
and so I'll continue saving just about everything I come across.</p>
<p>In the future, I'd like to allow others to write to my pinboard account and/or set up a JSON endpoint for my favorited
items to display on my site. Think a smaller curated list for my site without the user having to visit pinboard.</p>
devbattle
2014-10-22T00:00:00Z
https://jesse.sh/devbattle/
<p>Last night, <a href="https://twitter.com/verybadhello">Ramsay Lanier</a> and I competed in DC's first devbattle.</p>
<p><a href="https://github.com/jshawl/devbattle/blob/114be3a62ead6fa977bd40b0bd39e76ab62a7df5/01-rails-vs-meteor/readme.md">Our challenge</a> was to build a basic crud app with users, posts, and comments.</p>
<h2>The winner</h2>
<p>Was Jesse Shawl! I completed the challenge in a mere 19 minutes. <a href="https://github.com/jshawl/devbattle-01-rails">View the solution</a></p>
<p>Check out more of the action from Twitter:</p>
<p><a class="twitter-timeline" href="https://twitter.com/jshawl/timelines/524904654074028032" data-widget-id="524911319061757952">devbattle-01</a></p>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
Dropdown change events and selectedIndex
2014-07-03T00:00:00Z
https://jesse.sh/dropdown-change-events-and-selectedindex/
<p>Today I learned how to listen to <code><select></code> change events, and store
the selected option in plain-jane javascript:</p>
<p data-height="268" data-theme-id="788" data-slug-hash="phLAj" data-default-tab="js" class="codepen">See the Pen <a href="http://codepen.io/jshawl/pen/phLAj/">phLAj</a> by Jesse Shawl (<a href="http://codepen.io/jshawl">@jshawl</a>) on <a href="http://codepen.io/">CodePen</a>.</p>
<script async="" src="https://codepen.io/assets/embed/ei.js"></script>
<p>I had never heard of the <code>selectedIndex</code> method, so I checked out the documentation on the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Property/selectedIndex">mozilla developer network</a>.</p>
<p>I'm not sure this is super useful outside the context of dropdown menus, but it's a heck of a lot easier
than looking for <code>selected</code> attributes and removing them, which can be a pain to manage.</p>
<p>Instead, you can just set <code>selectNode.selectedIndex</code> to <code>-1</code>, which will deselect all options.</p>
Self hosted project pages
2014-06-30T00:00:00Z
https://jesse.sh/self-hosted-project-pages/
<p>Today I learned how to self-host gh-pages-esque project pages in an easy way
using an nginx alias.</p>
<p>I have my main domain: <a href="http://jshawl.com/">http://jshawl.com/</a> which lives in <code>/var/www/jshawl.com/</code></p>
<p>I wanted to be able to <code>cd</code> into that directory, clone any new repos, and if
there is a gh-pages branch, serve it with the url as <a href="http://jshawl.com/project-name/">http://jshawl.com/project-name/</a>.</p>
<p>Normally, this URL structure doesn't play nicely with jekyll sites, as jekyll
compiles the site to the <code>_site/</code> folder by default. The <code>index.html</code> file in
<code>/var/www/jshawl.com/project-name/</code> is the yaml-filled jekyll-ready index file,
not the compiled file with all of my posts listed out.</p>
<p>Essentially, I wanted to set the document root of <a href="http://jshawl.com/project-name/">http://jshawl.com/project-name/</a> to <code>/var/www/jshawl.com/project-name/_site/</code></p>
<p>Here's how I accomplished this task with an nginx alias:</p>
<pre class="language-text"><code class="language-text">server {<br /> listen 80;<br /> root /var/www/jshawl.com;<br /> server_name jshawl.com;<br /> index index.html;<br /><br /> location /project-name/ {<br /> alias /var/www/jshawl.com/project-name/_site/;<br /> }<br />} </code></pre>
<p>In a later post, I'd like to explore the possibility of doing this automatically, perhaps
with a <code>try_files</code> directive.</p>
Jekyll 2.1.0 is out, and I'm a contributor
2014-06-30T00:00:00Z
https://jesse.sh/jekyll-210-is-out-and-im-a-contributor/
<p>Jekyll 2.1.0 was recently released, and my name is listed
under the contributors! <a href="http://jekyllrb.com/news/2014/06/28/jekyll-turns-21-i-mean-2-1-0/">http://jekyllrb.com/news/2014/06/28/jekyll-turns-21-i-mean-2-1-0/</a></p>
<p>It was only a <a href="https://github.com/jekyll/jekyll/pull/2429">small css change</a>, but it's the thought
that counts, right?!</p>
Linode and The 10 Dollar Plan
2014-06-17T00:00:00Z
https://jesse.sh/linode-and-the-10-dollar-plan/
<p>Linode has recently expanded their hosting offering to include a $10 a month plan. The stats look
good, and I almost binge-bought a new virtual private server, to return to my linode roots.</p>
<p>I used to be a linode user and evangelist until Digital Ocean popped up seemingly out of nowhere
with their solid state drive and cheaper hosting options.</p>
<p>I decided to dig in deeper and compare the two $10 plans from each provider:</p>
<p><img src="http://jshawl.com/linode-vs-do.png" alt="" /></p>
<p>With Linode, you get 6GB fewer disk space for storage, but otherwise appear to be identical.</p>
<p>I'm not too concerned about network speeds. Most of my sites are static and minimally dynamic, with
low (to nil) traffic.</p>
<h2>The Verdict</h2>
<p>I'll be keeping my Digital Ocean droplets but will keep a close eye on Linode as they continue to grow.
I remember them randomly bumping specs like RAM and brandwidth throughout my subscription, which
is always good news.</p>
<h2>The Obligatory Referral Link</h2>
<p>If I encouraged you in any way to try out Digital Ocean, please consider using my referral link:</p>
<p><a href="https://www.digitalocean.com/?refcode=01b24a40b88f">https://www.digitalocean.com/?refcode=01b24a40b88f</a></p>
sudo npm install -g is an antipattern
2014-06-12T00:00:00Z
https://jesse.sh/sudo-npm-install-g-is-an-antipattern/
<p><img src="http://imgs.xkcd.com/comics/sandwich.png" alt="" /></p>
<p>Earlier this week, I began working on my first npm package. I've published it,
but only to understand how packages are distributed via <a href="http://npmjs.org/">npmjs.org</a>.</p>
<p>I built a simple little command line tool to upload files to my server and print out the url to the
resource in the console.</p>
<p>Since I wanted to use this command without specifying a path to the executable,
I knew I had to install it globally.</p>
<p>Normally I would just</p>
<pre><code>$ sudo npm install -g package-name
</code></pre>
<p>But I noticed while reading the docs that this is quite dangerous to let <em>anyone</em> (yes, that includes me)
install code on your system with super user privileges. <a href="https://www.npmjs.org/doc/files/npm-folders.html">https://www.npmjs.org/doc/files/npm-folders.html</a></p>
<h2>The Solution</h2>
<pre><code>$ sudo chown -R `whoami` /usr/local/lib/node_modules
</code></pre>
<p>This will allow you to globally edit npm packages without <code>sudo</code>. i.e. <code>npm install -g package-name</code>.</p>
<h2>Update 11-3-2014</h2>
<p>According to <a href="http://stackoverflow.com/a/21712034/850825">this stackoverflow answer</a>, another option
for avoiding using <code>sudo</code> with <code>npm install</code> is to change the directory where npm installs
global packages:</p>
<pre><code>$ npm config set prefix ~/npm
</code></pre>
<p>And add this directory to your <code>$PATH</code>. Add <code>export PATH="$PATH:$HOME/npm/bin"</code> to the end of your
<code>~/.bashrc</code> or <code>~/.zshrc</code> file.</p>
Clean up pull requests with git merge --squash
2014-05-27T00:00:00Z
https://jesse.sh/clean-up-pull-requests-with-git-merge-squash/
<p>Sometimes, I will receive a pull request to one of my repositories that has commits
that affect several files.</p>
<p>I want to merge the changes to one or two of those files across several commits,
but ignore the changes to the other files.</p>
<h2>Getting the latest changes.</h2>
<p>First, create a new branch and pull in the commits submitted in the pull request:</p>
<pre><code>$ git checkout -b pull-request-23
$ git pull git@github.com:otheruser/somerepo.git master
</code></pre>
<p>If I were to <code>git checkout master</code> and <code>git merge pull-request-23</code>, this
would manually merge the pull request, as GitHub describes it:</p>
<p><img src="https://jesse.sh/img/merge-pull-request-from-command-line.png" alt="" /></p>
<h2>git merge --squash prevents the merge commit</h2>
<p>Instead of merging right into master with all of those changes,
I can <code>git merge --squash</code>, which will add all of the changes to the index
on <code>master</code>.</p>
<pre><code>$ git checkout master
$ git merge --squash pull-request-23
</code></pre>
<p>At this point, the changes from pull-request-23 will be staged
while on master, and I can unstage the files that I don't want by using <code>git reset HEAD <path></code></p>
<pre><code>$ git reset HEAD files-i-dont-want/ ignore-me.rb
</code></pre>
<p>To get rid of the unstaged changes:</p>
<pre><code>$ git checkout -- .
</code></pre>
<p>And to remove untracked files and directories:</p>
<pre><code>$ git clean -fd
</code></pre>
<p>Now, your index should only contain changes to the files that you want
to merge in via the pull request. Finally, make the commit.</p>
<pre><code>$ git commit -m "Merge the cleaned up pull-request-23; fixes #23"
</code></pre>
<p>Notice the "fixes #23" at the end of the commit message? GitHub allows you to close
issues from a commit! - <a href="https://help.github.com/articles/closing-issues-via-commit-messages">https://help.github.com/articles/closing-issues-via-commit-messages</a>.</p>
<p>And push!</p>
<pre><code>$ git push origin master
</code></pre>
<p>GitHub will add a comment to the pull request thread showing that you've closed
the pull request:</p>
<p><img src="https://jesse.sh/img/close-pr-merge-squash.png" alt="" /></p>
<p>Now you don't need to ask your pull-request-submitters to clean up
their changes, and you can merge in the important stuff right away!</p>
Persistent git log with git loglive
2014-05-24T00:00:00Z
https://jesse.sh/persistent-git-log-with-git-loglive/
<p>I recently watched a talk by <a href="http://timberglund.com/">Tim Berglund</a> on advanced git - <a href="http://vimeo.com/49444883">http://vimeo.com/49444883</a>
wherein he demos some git plumbing commands with a <code>git log</code> that keeps refreshing.</p>
<p>It looks like this:</p>
<p><img src="https://jesse.sh/img/git-loglive.gif" alt="" /></p>
<p>This is incredibly useful for visualizing what's happening
to your repository as you switch between and modify branches.</p>
<p>The code below has been reproduced from his <a href="https://gist.github.com/tlberglund/3714970">original gist</a></p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><br /><br /><span class="token keyword">while</span> <span class="token builtin class-name">:</span><br /><span class="token keyword">do</span><br /> <span class="token function">clear</span><br /> <span class="token function">git</span> --no-pager log --graph --pretty<span class="token operator">=</span>oneline --abbrev-commit --decorate --all <span class="token variable">$*</span><br /> <span class="token function">sleep</span> <span class="token number">1</span><br /><span class="token keyword">done</span> </code></pre>
<p>I had to create a new file with the above contents somewhere in my <code>PATH</code>. I created a file called
<code>git-loglive</code> in <code>/usr/local/bin/</code></p>
<p>If your commit history is longer than your terminal window is tall,
you might need to specify that you only want the top 10 commits or so.</p>
<pre><code>$ git loglive -10
</code></pre>
Repo Stats from the command line
2014-05-23T00:00:00Z
https://jesse.sh/repo-stats-from-the-command-line/
<p>Did you know?! You can see <a href="https://github.com/jekyll/jekyll/graphs/contributors">github-style commit stats</a> from the command line.</p>
<pre><code>$ git shortlog -sn | head -5
</code></pre>
<p>This will summarize the output of git log, sorted by number of commits per author, with only the author's name
and total commits.</p>
<pre><code>~/jekyll-source git:(master) git shortlog -ns | head -10
1654 Parker Moore
409 Tom Preston-Werner
395 Matt Rogers
145 maul.esel
96 Anatol Broder
85 Nick Quaranto
56 Coby Chapple
51 zachgersh
47 Kris Brown
39 Ben Balter
</code></pre>
<p>It's interesting that these numbers do not correspond to the contributors tab on github - <a href="https://github.com/jekyll/jekyll/graphs/contributors">https://github.com/jekyll/jekyll/graphs/contributors</a>
Maybe <code>shortlog</code> includes merge commits?</p>
<p>I mostly run this command to boost my ego when I know I've been hard at work committing to a repository, and now you can too!</p>
Clean up Commits with git cherry-pick -n
2014-05-23T00:00:00Z
https://jesse.sh/clean-up-commits-with-git-cherry-pick-n/
<p>Sometimes I binge code for like 3+ hours and work on several unrelated things.
Since I'm so in the zone, I often forget to commit regularly, and am left
with a bunch of changes not yet staged for commit when I take a break:</p>
<p><img src="https://dl.dropboxusercontent.com/u/2241201/Screenshot%202014-05-23%2015.50.33.png" alt="" /></p>
<p>ffffffaaaacccckkkkkk!!!!!!!</p>
<p>First, I stage the changes:</p>
<pre><code>$ git add .
</code></pre>
<p>And then create a new branch and prefix the branch name with <code>wip</code></p>
<pre><code>$ git checkout -b wip/clean-me
</code></pre>
<p>This produces a git log that looks like:</p>
<pre><code>* 5a639f3 (HEAD, wip/clean-me) 48 seconds ago Jesse shawl
| Please clean this branch|
| five | 0
| four | 0
| one | 0
| seven | 0
| six | 0
| three | 0
| two | 0
| 7 files changed, 0 insertions(+), 0 deletions(-)
|
* 24784a5 (master) 6 hours ago Jesse shawl
initial commit
readme.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
</code></pre>
<p>I can always come back to this point as long as I commit, but in this post, I'll walk
through the process of making sense of this otherwise meaningless commit.</p>
<h2>Getting only the changes you want/need</h2>
<p>Let's say that <code>5a639f3</code> contains a few hot fixes that should be added to master. I want to pull
in the file changes to one, two, and three, but not four, five, six, and seven.</p>
<p>To move a commit to a new branch, you normally <code>git cherry-pick 5a639f3</code> while on the master branch. <em>But</em>, this
will just add a new commit on master that's identical to the <code>wip/clean-me</code> branch. This doesn't do much for us.</p>
<p>Instead, we can <code>git cherry-pick -n 5a639f3</code> to cherry-pick the changes without making a commit. This will
allow us to edit the index (or staging area) before making the next commit.</p>
<pre><code>$ git cherry-pick -n 5a639f3
</code></pre>
<p>After running this command, <code>git status</code> will tell me that I have changes that need to be committed:</p>
<pre><code>On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: five
new file: four
new file: one
new file: seven
new file: six
new file: three
new file: two
</code></pre>
<p>At this point, I reset my <code>HEAD</code> to move these changes out of the staging area. This means that I can add back in the changes
I want with <code>git add <path></code> and/or view the diffs without having to use the <code>--staged</code> option.</p>
<pre><code>$ git reset HEAD
</code></pre>
<p>You can now add the files you want to the staging area individually:</p>
<pre><code>$ git add one two three
</code></pre>
<p>And finally, make the commit.</p>
<p>This leaves us with a git log that looks like:</p>
<pre><code>* 07ac794 (HEAD, master) 6 seconds ago Jesse shawl
| Add hotfixes that should be deployed asap|
| one | 0
| three | 0
| two | 0
| 3 files changed, 0 insertions(+), 0 deletions(-)
| * 5a639f3 (wip/please-clean-me) 17 minutes ago Jesse shawl
|/ Please clean this branch|
| five | 0
| four | 0
| one | 0
| seven | 0
| six | 0
| three | 0
| two | 0
| 7 files changed, 0 insertions(+), 0 deletions(-)
* 24784a5 7 hours ago Jesse shawl
initial commit
readme.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
</code></pre>
<p>At this point, we've added the important changes from the <code>please-clean-me</code> branch to <code>master</code>. The changes still exist
on your existing branch if you need to go back later and make changes.</p>
A Better Git Log
2014-05-23T00:00:00Z
https://jesse.sh/a-better-git-log/
<p>The standard <code>git log</code> is pretty useless:</p>
<p><img src="https://dl.dropboxusercontent.com/u/2241201/Screenshot%202014-05-23%2014.42.30.png" alt="" /></p>
<p>It tells me nothing about my current branch and/or how it differs from its upstream
counterpart.</p>
<p>I personally use:</p>
<pre><code>$ git log --all --decorate --graph --pretty=format:"%C(yellow)%h%Creset %C(auto)%d%Creset %Cblue%ar%Creset %Cred%an%Creset %n%w(72,1,2)%s"
</code></pre>
<p><img src="https://dl.dropboxusercontent.com/u/2241201/Screenshot%202014-05-23%2014.47.04.png" alt="" /></p>
<p>It shows all of my current local and remote branches, commit times in "time-ago" formats, author names,
and even displays all of this information in an attractive and readable graph.</p>
<p>You can either add the whole git log line as an alias in your <code>~/.bash_profile</code> or as a git alias in your <code>~/.gitconfig</code>.</p>
<p>Do you use something different? Let's see it!</p>
<p>Looking for the available options? Try running <code>git help log</code> in the terminal. Or you can check out the relevant pages
from "Pro Git" - <a href="http://git-scm.com/book/en/Git-Basics-Viewing-the-Commit-History">http://git-scm.com/book/en/Git-Basics-Viewing-the-Commit-History</a></p>
Warning! push.default is not set
2014-05-21T00:00:00Z
https://jesse.sh/warning!-pushdefault-is-not-set/
<p>Have you seen this before when running <code>git push</code> with no remote or branch name specified?</p>
<pre><code>$ git push
warning: push.default is unset; its implicit value is changing in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the current behavior after the default changes, use:
git config --global push.default matching
To squelch this message and adopt the new behavior now, use:
git config --global push.default simple
See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)
</code></pre>
<p>I confess, I've been seeing this message for weeks (months)? and have done nothing to squelch
the warning message.</p>
<p>Let's break it down and see what it means and what we can do to prevent it from cluttering
our terminal.</p>
<p>The first step is to run <code>git help config</code> and search for <code>push.default</code>.</p>
<pre><code>$ git help config
</code></pre>
<p>When the help page pops up, it looks like this:</p>
<p><img src="https://dl.dropboxusercontent.com/u/2241201/Screenshot%202014-05-21%2021.32.33.png" alt="" /></p>
<p>You can search for a word, namely 'push.default' by typing <code>/push.default</code>.</p>
<p>I had to look for the next search result by typing <code>n</code> a few times, but found what I was looking
for.</p>
<hr />
<p><strong>push.default</strong></p>
<p>Defines the action git push should take if no refspec is explicitly given.
Different values are well-suited for specific workflows; for instance, in
a purely central workflow (i.e. the fetch source is equal to the push
destination), upstream is probably what you want. Possible values are:</p>
<ul>
<li>
<p><strong>nothing</strong> - do not push anything (error out) unless a refspec is explicitly
given. This is primarily meant for people who want to avoid mistakes by
always being explicit.</p>
</li>
<li>
<p><strong>current</strong> - push the current branch to update a branch with the same name on the receiving end. Works in
both central and non-central workflows.</p>
</li>
<li>
<p><strong>upstream</strong> - push the current branch back to the branch whose changes are usually integrated into the
current branch (which is called @{upstream}). This mode only makes sense if you are pushing to the same
repository you would normally pull from (i.e. central workflow).</p>
</li>
<li>
<p><strong>simple</strong> - in centralized workflow, work like upstream with an added safety to refuse to push if the
upstream branch's name is different from the local one.</p>
<p>When pushing to a remote that is different from the remote you normally pull from, work as current. This
is the safest option and is suited for beginners.</p>
<p>This mode will become the default in Git 2.0.</p>
</li>
<li>
<p><strong>matching</strong> - push all branches having the same name on both ends. This makes the repository you are
pushing to remember the set of branches that will be pushed out (e.g. if you always push maint and
master there and no other branches, the repository you push to will have these two branches, and your
local maint and master will be pushed there).</p>
<p>To use this mode effectively, you have to make sure all the branches you would push out are ready to be
pushed out before running git push, as the whole point of this mode is to allow you to push all of the
branches in one go. If you usually finish work on only one branch and push out the result, while other
branches are unfinished, this mode is not for you. Also this mode is not suitable for pushing into a
shared central repository, as other people may add new branches there, or update the tip of existing
branches outside your control.</p>
<p>This is currently the default, but Git 2.0 will change the default to simple.</p>
</li>
</ul>
<hr />
<p>Basically, the old git would push all branches that have the same name locally and on the remote. New branches will
not be created on the remote.</p>
<p>Git 2.0 will switch to "simple", which will refuse a push when you run <code>git push</code> unless an upstream branch is set:</p>
<pre><code>fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin master
</code></pre>
<p>I personally prefer <code>matching</code> as my default. I like to think of it as keeping all of my branches in sync between
the local and the remote.</p>
<p>Since I'm a rebaser, people often warn me to not push branches that might be rebased in the future. The fix? -
prefix your work-in-progress branches with "wip": <code>wip/new-awesome-design</code> or <code>wip/fixing-bootstrap</code>.</p>
<p>Any other co-committers who are basing work off of a branch prefixed with 'wip' probably deserve the
complications that might arise should you rebase and do a force push.</p>
Practical Applications of Pseudo Elements and Classes
2013-12-12T00:00:00Z
https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/
<style>
.ps-hover {
-moz-transition: all 1s ease;
-o-transition: all 1s ease;
-webkit-transition: all 1s ease;
transition: all 1s ease;
display: block;
font-family: 'Avenir-Black';
position: relative;
padding: .5em;
width: 100px;
height: 100px;
-moz-transform-origin: 50% 50% 50%;
-ms-transform-origin: 50% 50% 50%;
-webkit-transform-origin: 50% 50% 50%;
transform-origin: 50% 50% 50%;
text-align: center;
}
.ps-hover span {
padding: 2em;
height: 0;
margin-left: 1em;
position: absolute;
font-family: Georgia;
width: 100%;
top: 0;
line-height: 1.5;
font-size: 0;
text-align: left;
left: 100%;
border-radius: 3px;
}
.ps-hover span:before {
content: ' ';
display: block;
position: absolute;
height: 1em;
width: 1em;
left: 0;
background: #fbfbfb;
border-color: #A7AFB3 transparent transparent #A7AFB3;
border-width: 1px;
border-radius: 3px 0 0 0;
border-style: solid;
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
-moz-transform-origin: 0 100% 50%;
-ms-transform-origin: 0 100% 50%;
-webkit-transform-origin: 0 100% 50%;
transform-origin: 0 100% 50%;
}
.ps-hover:hover {
background: #B4D88B;
color: #B4D88B;
height: 250px;
line-height: 150px;
text-decoration: none;
width: 250px;
border-radius: 100%;
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
.ps-hover:hover span {
-webkit-animation: show 1.5s ease forwards;
-moz-animation: show 1.5s ease forwards;
-ms-animation: show 1.5s ease forwards;
-o-animation: show 1.5s ease forwards;
animation: show 1.5s ease forwards;
font-size: .9em;
height: auto;
color: #000;
border: 1px solid #A7AFB3;
}
@keyframes "show" {
0% {
opacity: 0;
}
90% {
opacity: 0;
margin-left: 0;
}
100% {
opacity: 1;
margin-left: 1em;
}
}
@-moz-keyframes show {
0% {
opacity: 0;
}
90% {
opacity: 0;
margin-left: 0;
}
100% {
opacity: 1;
margin-left: 1em;
}
}
@-webkit-keyframes "show" {
0% {
opacity: 0;
}
90% {
opacity: 0;
margin-left: 0;
}
100% {
opacity: 1;
margin-left: 1em;
}
}
@-ms-keyframes "show" {
0% {
opacity: 0;
}
90% {
opacity: 0;
margin-left: 0;
}
100% {
opacity: 1;
margin-left: 1em;
}
}
@-o-keyframes "show" {
0% {
opacity: 0;
}
90% {
opacity: 0;
margin-left: 0;
}
100% {
opacity: 1;
margin-left: 1em;
}
}
.ps-active {
transition: all .2s ease;
border: none;
background: #B4D88B;
color: #fff;
display: block;
padding: 1em 2em;
width: 100%;
max-width: 10em;
font-size: 1.5em;
font-family: 'Avenir-Black';
margin: auto;
box-shadow: 0 10px 0 #8DC63F;
position: relative;
}
.ps-active:focus {
outline: none;
}
.ps-active:active {
outline: none;
transition: 0s ease;
-moz-transform: translateY(9px);
-ms-transform: translateY(9px);
-webkit-transform: translateY(9px);
transform: translateY(9px);
box-shadow: 0 1px 0 #8DC63F;
}
.ps-checked input {
visibility: hidden;
}
.ps-checked div {
background: #B4D88B;
box-shadow: 8px 8px #8DC63F;
display: block;
font-family: 'Avenir-Black';
float: left;
margin: 1em;
height: 100px;
width: 100px;
line-height: 100px;
text-align: center;
position: relative;
-moz-transition: all 0.2s ease;
-o-transition: all 0.2s ease;
-webkit-transition: all 0.2s ease;
transition: all 0.2s ease;
}
.ps-checked :checked + div {
background: #B4D88B;
-moz-transform: translate(7px, 7px);
-ms-transform: translate(7px, 7px);
-webkit-transform: translate(7px, 7px);
transform: translate(7px, 7px);
color: #fff;
box-shadow: 1px 1px 0px #8DC63F;
}
.ps-focus {
border: 1px solid red;
}
.ps-focus label {
display: block;
position: relative;
-moz-transform: translateY(1em);
-ms-transform: translateY(1em);
-webkit-transform: translateY(1em);
transform: translateY(1em);
color: #fff;
}
.ps-focus label:focus {
border: 10px solid red;
}
.ps-focus input {
border: 1px solid #ddd;
padding: 2em;
border-radius: 3px;
}
.ps-nth-child {
border: 1px solid #ddd;
border-radius: 3px;
padding: 0 !important;
margin: 0;
list-style-type: none !important;
}
.ps-nth-child li {
list-style-type: none !important;
}
.ps-nth-child li:nth-child(2n) {
background: #8DC63F;
color: #fff !important;
margin: 0;
}
.ps-github {
position: relative;
text-indent: -999em;
width: 1em;
font-size: 1.5em;
clear: both;
display: block;
}
.ps-github:before {
position: absolute;
left: 0;
text-indent: 0;
color: #000;
}
:target {
-webkit-animation: target 2s ease;
-moz-animation: target 2s ease;
-ms-animation: target 2s ease;
-o-animation: target 2s ease;
animation: target 2s ease;
}
@-webkit-keyframes target {
0% {
background: #B4D88B;
}
100% {
background: #fff;
}
}
/* The CSS being applied to
this example is on Codepen so as not to inherit
cascading styles. Check it out there!
http://codepen.io/jshawl/pen/qzhfI */
.ps-widget {
padding: 1em;
width: 200px;
margin: 50px;
border-radius: 5px;
font-weight: 300;
font-size: .8em;
border: 1px solid #eee;
}
.ps-widget :first-child {
margin-top: 0;
}
.ps-widget :last-child {
margin-bottom: 0;
}
.ps-first-letter:first-letter {
font-size: 5em;
height: .75em;
display: block;
float: left;
padding-right: .125em;
line-height: 1;
font-weight: bold;
color: #B4D88B;
text-shadow: 4px 4px 0 #8DC63F;
}
#this-link {
padding-top: 5em;
}
[id*=__] {
padding-top: 3em;
}
</style>
<link href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="https://jesse.sh/css/pages/practical-pseudo-elements-classes.css" />
<h3>A note about browser support:</h3>
<p>There's some pretty crazy stuff going on in this post. The support for pseudo classes and elements
is quite good, and you can start using them in production now. The code examples in this post
show properties without vendor prefixes for readability purposes. If you're not using <a href="http://compass-style.org/">Compass</a>
or <a href="https://github.com/ai/autoprefixer">Autoprefixer</a> to deal with vendor prefixes,
you can always reference <a href="http://caniuse.com/">Can I Use</a> to see which vendor prefixes, if any,
are required.</p>
<p>Understanding what pseudo classes and pseudo elements are used for can be a bit confusing at first.
What are they? Why are they pseudo?! You're probably already familiar with a few of them. In any introductory CSS class, the <code>:hover</code> state for anchor links is a popular example.</p>
<p><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#" class="ps-hover">Hover Me <span>
At nclud, we totally dig open source. All of the examples in this post are on GitHub. Click the icon below to view the Sass used.</span> </a></p>
<p><a href="https://github.com/nclud/sketchbook/blob/74a801ff6be9532e0a2bdcc2ac07caa33b51b78b/app/_scss/pages/practical-pseudo-elements-classes.scss" class="fa fa-github ps-github">Github</a></p>
<p>Here are some practical use cases for these pseudo web things:</p>
<ul>
<li>
<p>Pseudo Classes</p>
<ul>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#class__active">:active</a></li>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#class__checked">:checked</a></li>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#class__first-last-child">:first-child and :last-child</a></li>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#class__nth-child">:nth-child</a></li>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#class__target">:target</a></li>
</ul>
</li>
<li>
<p>Pseudo Elements</p>
<ul>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#element__before-after">:before & :after</a></li>
<li><a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#element__first-letter">:first-letter</a></li>
</ul>
</li>
</ul>
<h2>Pseudo Classes</h2>
<h3 id="class__active">:active</h3>
<p>The <code>:active</code> pseudo class is useful for representing skeuomorphic behavior in web interfaces.</p>
<p><button class="ps-active">Press Me</button></p>
<p>I've used a button in this example and transformed the <code>translateY()</code> to offset the box shadow. It has the visual effect of the button being pressed or clicked.</p>
<pre class="language-css"><code class="language-css"><br /><span class="token selector">button</span><span class="token punctuation">{</span><br /> <span class="token property">transition</span><span class="token punctuation">:</span>all .2s ease<span class="token punctuation">;</span><br /> <span class="token property">position</span><span class="token punctuation">:</span>relative<span class="token punctuation">;</span><br /> <span class="token property">box-shadow</span><span class="token punctuation">:</span>0 10px 0 #8DC63F<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token selector">button:active</span><span class="token punctuation">{</span><br /> <span class="token property">transform</span><span class="token punctuation">:</span><span class="token function">translateY</span><span class="token punctuation">(</span>9px<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token property">box-shadow</span><span class="token punctuation">:</span>0 1px 0 #8DC63F<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /></code></pre>
<h3 id="class__checked">:checked</h3>
<p>One cool use of the <code>:checked</code> pseudo-class is custom radio buttons or checkboxes. Given the following markup:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>ps-checked<span class="token punctuation">'</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>radio<span class="token punctuation">'</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>radio<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><br /> Custom Radio Content<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></code></pre>
<p>We can pull off totally semantic custom radio buttons without having to use JavaScript.
Notice how the label wraps both the input and the custom radio content? Doing this
allows us to use the adjacent sibling selector. Anytime the label is clicked, the
input takes on the <code>checked</code> property. Then, we apply the styles to an div that's
adjacent to the <code>checked</code> input.</p>
<form>
<label class="ps-checked">
<input type="radio" name="radio" />
<div>
Option 1
</div>
</label>
<label class="ps-checked">
<input type="radio" name="radio" />
<div>
Option 2
</div>
</label>
<label class="ps-checked">
<input type="radio" name="radio" />
<div>
Option 3
</div>
</label>
<br style="clear:both;" />
</form>
<pre class="language-css"><code class="language-css"><br /><span class="token selector">label input</span><span class="token punctuation">{</span><br /> <span class="token property">visibility</span><span class="token punctuation">:</span>hidden<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token selector">label div</span><span class="token punctuation">{</span><br /> <span class="token property">box-shadow</span><span class="token punctuation">:</span>8px 8px #8DC63F<span class="token punctuation">;</span><br /> <span class="token property">position</span><span class="token punctuation">:</span>relative<span class="token punctuation">;</span><br /> <span class="token property">transition</span><span class="token punctuation">:</span>all .2s ease<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token selector">label input:checked + div</span> <span class="token punctuation">{</span><br /> <span class="token property">transform</span><span class="token punctuation">:</span><span class="token function">translate</span><span class="token punctuation">(</span>7px<span class="token punctuation">,</span> 7px<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token property">box-shadow</span><span class="token punctuation">:</span>1px 1px 0px #8DC63F<span class="token punctuation">;</span><br /> <span class="token property">color</span><span class="token punctuation">:</span>#fff<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /></code></pre>
<p>If we hide the radio button input with <code>visibility:hidden</code>, we are free to style
the contents of the <code><label></code> container as we please.</p>
<p>This is an elegant solution for toggle states in web applications. Instead of
toggling a class, or worse, changing the CSS via JavaScript (jQuery included),
we can maintain styles that reflect state changes in the DOM all in one place.</p>
<h3 id="class__first-last-child">:first-child and :last-child</h3>
<p>A popular use case for the first and last children of a container is the widget. Here's some markup:</p>
<pre><code><div class='widget'>
<h1>The Widget Title</h1>
<p>A brief summary of the what The Widget Title is all about.
Maybe it's even the opening paragraph.</p>
<p>Often there will be more than one paragraph in a widget</p>
</div>
</code></pre>
<p>In our widget, we want to have equal padding for the <code>widget</code> container, but ignore the margin-top of the <code><h1></code> and the margin-bottom of the <code><p></code>.
This is a common design pattern, where the container has equal spacing all around the
elements. If the <code>h1</code> or <code>p</code> were to retain its margin when directly next to
the widget's container, it would look a bit funky.</p>
<p data-height="450" data-theme-id="790" data-slug-hash="qzhfI" data-user="jshawl" data-default-tab="result" class="codepen">See the Pen <a href="http://codepen.io/jshawl/pen/qzhfI">qzhfI</a> by Jesse Shawl (<a href="http://codepen.io/jshawl">@jshawl</a>) on <a href="http://codepen.io/">CodePen</a></p>
<script src="https://codepen.io/assets/embed/ei.js"> </script>
<pre><code>.widget{
padding:1em;
}
.widget :first-child{
margin-top:0
}
.widget :last-child{
margin-bottom:0
}
</code></pre>
<h3 id="class__nth-child">:nth-child</h3>
<p>Alternating colors! It's an effective technique that takes a very small amount of code:</p>
<ul class="ps-nth-child">
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
<li>Item Four</li>
<li>Item Five</li>
</ul>
<pre><code>li:nth-child(2n){
background:#8DC63F;
}
</code></pre>
<p>Before <code>:nth-child</code> was available, you'd have to either hardcode classes into
the alternating rows or use a JavaScript solution to color the odd-numbered rows.
Using a CSS solution allows us to keep styles together, which ultimately decouples
our front end code.</p>
<h3 id="class__target">:target</h3>
<p>The <code>:target</code> pseudo class is useful for highlighting hash-linked sections
on web pages. This is a great usability technique. Normally the page will jump
to the anchor on click and position the element with the corresponding ID at the
top of the page. If the linked-to element is towards the bottom of the page, and there
isn't enough room at the bottom for the element to be all the way at the top of the
page, the only way to identify what was linked to will be to use the <code>:target</code>
pseudo class to highlight the important info.</p>
<p id="this-link" class="">
Clicking on <a href="https://jesse.sh/practical-applications-of-pseudo-elements-and-classes/#this-link">this link</a> will make the window jump to the anchor, and will highlight the
linked-to line.
</p>
<pre><code>:target{
animation:target 2s ease;
}
@keyframes target{
0%{
background:#B4D88B;
}
100%{
background:#fff;;
}
}
</code></pre>
<p>Here are some other examples of using the <code>:target</code> pseudo selector:</p>
<ol>
<li><a href="http://alistapart.com/article/offline-first#section2">A List Apart</a></li>
<li><a href="http://stackoverflow.com/questions/20496117/overlapping-and-centering-divs-in-html-css/20496298#20496298">Stack Overflow</a></li>
</ol>
<h3>The difference between pseudo-classes and pseudo-elements</h3>
<p>When both use the <code>:</code> syntax to denote pseudo-ness, it's difficult to remember
the difference between a class and an element. The CSS3 spec provides a syntax
for distinguishing between pseudo classes and pseudo elements: <code>:class</code> and <code>::element</code>.</p>
<p>Pseudo classes are used to select elements which cannot be selected
using a class or id. I like to think of pseudo elements
as being able to add content to a node in the DOM without
adding more markup, like all the cool things on <a href="http://one-div.com/">one-div.com</a>.</p>
<p>Most of the examples in this post reference pseudo classes, but Chris Coyier
has a great round up of all the cool things you can do with pseudo elements over
at <a href="http://css-tricks.com/pseudo-element-roundup/">CSS Tricks</a>.</p>
<h3 id="element__before-after">:before and :after</h3>
<p>My most frequent use of the <code>:before</code> pseudo element is the micro clearfix. In Sass,
you can extend a clearfix place holder:</p>
<p>Micro Clearfix adapted from <a href="http://nicolasgallagher.com/micro-clearfix-hack/">Nicholas Gallagher</a>.</p>
<pre><code>%clearfix{
:after{
content: ' ';
display: table;
clear: both;
}
}
</code></pre>
<h3 id="element__first-letter">:first-letter</h3>
<p>There's no need to use images or hard code div's with a class if you
want to pull off the drop cap technique:</p>
<p class="ps-first-letter">Jesse Shawl is a Front End Developer at nclud - whose focus intersects web performance and cutting edge browser interactivity. He's an open source advocate who enjoys building tools for the web with third party API's and mesmerizing CSS animations.</p>
<pre><code>p:first-letter{
font-size: 5em;
height: .75em;
display: block;
float: left;
padding-right:.125em;
line-height: 1;
font-weight:bold;
color:#B4D88B;
text-shadow:4px 4px 0 #8DC63F;
}
</code></pre>
When to Rebase
2013-12-03T00:00:00Z
https://jesse.sh/when-to-rebase/
<p>I've come to appreciate that scary git command - <code>git rebase</code>. I've found two incredible uses for it:</p>
<ol>
<li>Updating feature branches with progress made on master.
<ul>
<li>Avoids those nasty merge commits.</li>
</ul>
</li>
<li>Cleaning up feature branches before merging with master
<ul>
<li>There's no need to keep <a href="https://github.com/jshawl/jshawl-v3/commit/b543e75c2fc05cb93b1ab77231fad3d3298de3e0">'fix typo' commits</a> in our git history.</li>
</ul>
</li>
</ol>
<!-- more -->
<h2>Updating feature branches</h2>
<p>Say you've branched from master to work on a new topic. Since you've started making your edits,
your team members have pushed a commit or two to master. A <code>git log</code> will demonstrate the scenario:</p>
<pre><code>* a1caf92 (master) Some updates your team members have made.
| * c4b61f8 (HEAD, feature-branch) Started working on a feature.
|/
| * 7884fb6 Common ancestor commit.
</code></pre>
<p>If you want to pull in the changes made on master, while in the feature-branch branch:</p>
<pre><code>$ git rebase master
</code></pre>
<p>I like to think of this rebase as peeling off your changes from the history, moving up the master branch,
and then laying your changes back down on top of master.</p>
<p>By updating your feature branch with the progress made on master, you can avoid merge commits that clutter
the git history and contribute (most often) meaningless commit messages.</p>
<p>After the rebase, your git tree will show the feature branch on top of <code>master</code>, which means the common ancestor commit
is the latest update on master.</p>
<p>Running <code>git log</code> will give the following graph:</p>
<pre><code>* c4b61f8 (HEAD, feature-branch) Started working on a feature.
* a1caf92 (master) Some updates your team members have made.
* 7884fb6 Common ancestor commit.
</code></pre>
<p>Since the changes you're pulling in from master are all wrapped up into one commit, you could have just as easily used <code>git cherry-pick</code> to merge the changes
from that commit into your feature branch.</p>
<h2>Cleaning up feature branches</h2>
<p>Quite often, my feature branches consist of several commits. They don't have to be pretty.
I commit as often as possible, and try to leave a note about what a change does as often as I make
a significant change, or have encountered a state I might want to return to in the future.</p>
<p>Imagine the following tree:</p>
<pre><code>* f3e1236 (HEAD, feature-branch) Remove unused files from repo
* 8be8878 Cleanup CSS
* 0a16b8c Complete testing for feature-branch
* b42147c Add syntax theming and type size changes
* 08e20d1 Add some more code related to feature
* 9715882 Start working on feature.
* 8ec539e (master) added indenting with vim post
</code></pre>
<p><code>feature-branch</code> is complete and ready for merge into master, but it's littered with some rather embarassing commits.
Knowing that I cleaned up css or removed left over files in a repo inspires a curiosity to view the project just one
commit before.</p>
<p>But as savvy git users, we know better. We can remove the commits related to cleaning up, and the end result will be a git history
that looks like we just code perfectly without any mistakes along the way.</p>
<p>Given the previous git tree, we can clean up the feature branch into only 1 commit. While in the <code>feature-branch</code> branch, run:</p>
<pre><code>$ git rebase -i master
</code></pre>
<p>This will open up a Vim window unless you've configured another editor:</p>
<pre><code>pick f3e1236 Remove unused files from repo
squash 8be8878 Cleanup CSS
squash 0a16b8c Complete testing for feature-branch
squash b42147c Add syntax theming and type size changes
squash 08e20d1 Add some more code related to feature
squash 9715882 Start working on feature.
squash 8ec539e added indenting with vim post
</code></pre>
<p>Change every 'pick' to 'squash' for each commit that you want to squash into the previous one.</p>
<p>When prompted to add a commit message for this new super clean and short rebased branch, be sure to write something meaningful. Something like "Implement Feature One"
will suffice.</p>
<p>Running <code>git log</code> now will show our clean and readable history:</p>
<pre><code>* f3e1236 (HEAD, feature-branch) Implement Feature One
* 8ec539e (master) added indenting with vim post
</code></pre>
<p>Merging <code>feature-branch</code> into master will update master without leaving a merge commit.</p>
<pre><code>* f3e1236 (HEAD, master, feature-branch) Implement Feature One
* 8ec539e added indenting with vim post
</code></pre>
<p>Be sure not to push any branches that you plan on rebasing in the future. Rewriting the history will surely
raise complications for anyone else who might be working with the history you've pushed. If you want to push them so you can
access the changes from a machine while out of the office, be sure to communicate that those branches are unstable and should not be used for new branches.</p>
<h2>A note about merge conflicts while in a rebase</h2>
<p>It can be totally scary. When manipulating the history, it feels like you could easily mess up your entire project. Fortunately,
git helps us out during a merge conflict.</p>
<p>If you encounter a merge conflict, git will prompt you to:</p>
<ol>
<li>Fix the changes</li>
<li><code>git add</code> the files to which you made changes</li>
<li><code>git rebase --continue</code></li>
</ol>
<p>That's it! If for some reason you've got merge conflicts out the wozz, you can always abort the rebase entirely with
<code>git rebase --abort</code>.</p>
Rethinking Dynamic Page Replacing Content
2012-12-30T00:00:00Z
https://jesse.sh/rethinking-dynamic-page-replacing-content/
<p>In May of 2012, Chris updated a previous post about <a href="http://css-tricks.com/dynamic-page-replacing-content/">dynamic page replacing content</a>. This article is an update to that update, which uses the HTML5 history API for a better user experience.</p>
<p>Here's a quick recap of the best practices:</p>
<ol>
<li>Works fine with JavaScript disabled.</li>
<li>It is possible to "deep link" to specific content.</li>
<li>The browsers back button and forward button work as expected.</li>
</ol>
<h3>The problem with URL hashes</h3>
<p>For one individual user, the <a href="http://css-tricks.com/examples/DynamicPage/">existing demo</a> meets the criteria just fine, but URL's are permanent addresses, and they're going to be shared.</p>
<p>Consider the following scenario:</p>
<ol>
<li>
<p>I've got a fancy browser with Javascript enabled. I'm browsing the demo site, and I find a great product I'd like to share with a friend.</p>
</li>
<li>
<p>I copy the url '<a href="http://example.com/#awesome-product">http://example.com/#awesome-product</a>', and send it to my friend.</p>
</li>
<li>
<p>My friend doesn't have javascript enabled. (S)he opens the link in her browser, and is confused that the awesome product doesn't load as expected.</p>
</li>
<li>
<p>(S)he gets confused/frustrated and swears never to visit <a href="http://example.com/">example.com</a> again.</p>
</li>
</ol>
<p>THIS IS BAD UX!!</p>
<p>Today, we'll be improving the existing demo such that the dynamic page replacing content needn't rely on the hash.</p>
<p><a href="http://sudojesse.github.com/dynamic-page/">demo</a>
<a href="https://github.com/sudojesse/dynamic-page/archive/master.zip">download files</a></p>
<h3>Modernizr for progressive enhancement</h3>
<p><em>Note: The following examples build upon the previous demo. Download the files <a href="http://css-tricks.com/examples/DynamicPage.zip">here</a> to follow along.</em></p>
<p>If you're not using <a href="http://modernizr.com/">Modernizr</a> yet, go get it (I'll wait). It's the easiest way to detect browser features with javascript.</p>
<p>Since we'll be playing with the HTML5 history api, we only need to check the 'History' checkbox. Download the custom build <a href="https://raw.github.com/sudojesse/dynamic-page/master/js/modernizr.js">here</a>.</p>
<p>Include it in the <code><head></code> of our html file:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>text/javascript<span class="token punctuation">'</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>js/modernizr.js<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>Testing for HTML5 history support is super easy:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// dynamicpage.js</span><br /><br /><span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span>Modernizr<span class="token punctuation">.</span>history<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token comment">//history is supported; do magical things</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token comment">//history is not supported; nothing fancy here</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>First, we're going to set up everything to manipulate the browser's history, and then we'll add all the fancy loading provided from the previous demo.</p>
<h3>Manipulate the history with HTML5 history API</h3>
<p>The HTML5 history.pushState() method allows us to:</p>
<ol>
<li>Change the URL
<ul>
<li>without a hash</li>
<li>without a page refresh (this is where the dynamic page replacing content happens)</li>
</ul>
</li>
<li>Update the browser's history stack
<ul>
<li>so we can navigate through the history with back and forward button clicks.</li>
</ul>
</li>
</ol>
<p>The pushState() method takes three parameters:</p>
<pre class="language-js"><code class="language-js">history<span class="token punctuation">.</span><span class="token function">pushState</span><span class="token punctuation">(</span>stateObject<span class="token punctuation">,</span> <span class="token string">'title'</span><span class="token punctuation">,</span> <span class="token constant">URL</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>We're only going to be supplying the URL in this example, but you can learn more about the history API over at the <a href="https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history">Mozilla Developer Network</a>.</p>
<p>After changing the URL, we'll want to set up a function to load the content - loadContent() seems like a good name.</p>
<pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">if</span><span class="token punctuation">(</span>Modernizr<span class="token punctuation">.</span>history<span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token comment">//history is supported; do magical things</span><br /> <br /> <span class="token comment">//hijack the nav click event</span><br /> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'nav'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">delegate</span><span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> _href <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <br /> <span class="token comment">// change the url without a page refresh and add a history entry.</span><br /> history<span class="token punctuation">.</span><span class="token function">pushState</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> _href<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <br /> <span class="token comment">// load the content</span><br /> <span class="token function">loadContent</span><span class="token punctuation">(</span>_href<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// fear not! we're going to build this function in the next code block</span><br /> <br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token comment">//history is not supported; nothing fancy here</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>And now, we just need to code up the loadContent() function, which is a matter of taking code from the original example.</p>
<p>Code dump!</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// set up some variables</span><br /><span class="token keyword">var</span> $mainContent <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#main-content"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> $pageWrap <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#page-wrap"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> baseHeight <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> $el<span class="token punctuation">;</span><br /><br /><span class="token comment">// calculate wrapper heights to prevent jumping when loading new content</span><br />$pageWrap<span class="token punctuation">.</span><span class="token function">height</span><span class="token punctuation">(</span>$pageWrap<span class="token punctuation">.</span><span class="token function">height</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />baseHeight <span class="token operator">=</span> $pageWrap<span class="token punctuation">.</span><span class="token function">height</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> $mainContent<span class="token punctuation">.</span><span class="token function">height</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">function</span> <span class="token function">loadContent</span><span class="token punctuation">(</span><span class="token parameter">href</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> $mainContent<br /> <span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">"#guts"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">fadeOut</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// fade out the content of the current page</span><br /> $mainContent<span class="token punctuation">.</span><span class="token function">hide</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>href <span class="token operator">+</span> <span class="token string">" #guts"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// load the contents of whatever href is</span><br /> $mainContent<span class="token punctuation">.</span><span class="token function">fadeIn</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> $pageWrap<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">height</span><span class="token operator">:</span> baseHeight <span class="token operator">+</span> $mainContent<span class="token punctuation">.</span><span class="token function">height</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"px"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"nav a"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">removeClass</span><span class="token punctuation">(</span><span class="token string">"current"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>href<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"nav a[href$="</span><span class="token operator">+</span>href<span class="token operator">+</span><span class="token string">"]"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addClass</span><span class="token punctuation">(</span><span class="token string">"current"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span></code></pre>
<h3>Handle browser back and forward button clicks</h3>
<p>At this point, content is loaded in a fancy ajaxy way, but clicking on your back button won't take us back... yet.
The history API gives us access to the 'popstate' event, which fires everytime the history stack changes (read: back and/or forward buttons are clicked.) Anytime this event fires, we just need to call our loadContent() function:</p>
<pre class="language-js"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token string">'popstate'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <br /> _link <span class="token operator">=</span> location<span class="token punctuation">.</span>pathname<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^.*[\\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//get filename only</span><br /> <span class="token function">loadContent</span><span class="token punctuation">(</span>_link<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3>A little homework assignment</h3>
<p>At the time of this writing, the popstate event happens on page load in Chrome. This means two requests are being made:</p>
<ol>
<li>The original http request for whateverpage.html</li>
<li>The request made by $.load in our loadContent() function</li>
</ol>
<p>There are a couple of different ways to handle this, but I'll let you decide which works best.</p>