Will Koehler2024-01-02T19:20:44-05:00http://willkoehler.net/atom.xmlWill Koehlerhttp://willkoehler.netAlternate Route Options For The Olentangy Trail2014-12-22T00:00:00-05:002015-01-05T12:00:00-04:00http://willkoehler.net/2014/12/22/alternate-route-options-for-the-olentangy-trail<p>The Olentangy trail will be closed under the 315/270 interchange for approximately 12 months while
ODOT <a href="http://www.dot.state.oh.us/projects/I-270/Pages/WhatWeAreDoing.aspx#315">rebuilds part of the 315/270 interchange</a>.</p>
<p>Inconvenience and detours come along with any road construction, and the 315/270 project is no exception.
With that said, the north section of the Olentangy Trail is one of the busiest multi-use trails in the
state, with a volume of over 700 users per day. Some of this traffic is discretionary and will taper off
during the trail closure. But for a significant number of cyclists, this section of trail is a vital
transportation corridor connecting the northern suburbs to Worthington, Clintonville, OSU and downtown.</p>
<p>This document outlines options to create a safe alternate route for trail traffic while the Olentangy Trail
is closed under the 315/270 interchange.</p>
<p>The routes proposed will not only serve to keep bike traffic flowing safely during this
construction but will create new connections between neighborhoods and the Olentangy Trail, providing
valuable service well beyond the scope of the 315/270 project.</p>
<h2 id="route-options">Route Options</h2>
<p><strong>Wilson Bridge Road Option</strong> <span class="muted">(marked in red)</span>: This is the recommended route. It’s
both safe and cost effective.</p>
<p><strong>161 / Olentangy River Road Option</strong> <span class="muted">(marked in blue)</span>: While this route has benefits,
and should be kept under consideration for long-term improvements, it has significant safety challenges.
Improvements required for this route may be beyond the scope of the project.</p>
<div class="map_container full">
<a class="overlay" href="https://mapsengine.google.com/maps/d/viewer?mid=zEAPhiHiDgA8.kJ15f3nMBWE8" target="_blank"></a>
<iframe src="https://mapsengine.google.com/map/u/0/embed?mid=zEAPhiHiDgA8.kJ15f3nMBWE8&z=14"></iframe>
</div>
<h2 id="wilson-bridge-road">Wilson Bridge Road</h2>
<h4 id="add-new-bike-and-pedestrian-park-entrance">Add new bike and pedestrian park entrance</h4>
<p>Build trail spur from south side of skate park to a new bike and pedestrian entrance at the bottom
of the hill on Wilson Bridge Road <span class="muted smaller">(‘A’ on map)</span>. Adding a new
entrance has several advantages over using the existing park entrance:</p>
<ul>
<li>Reduces distance travelled on Wilson Bridge by half.</li>
<li>Eliminates conflict between bike and car traffic at the park entrance.</li>
<li>Removes the hill.</li>
</ul>
<h4 id="add-bike-lanes">Add bike lanes</h4>
<p>Reduce lane width on Wilson Bridge Road from 12’ to 11’, making room for 4’ bike lanes. Add green
paint to bike lanes along the curved sections to increase visibility and discourage cars from
drifting into the bike lane (taking the “racing line”).</p>
<p>Ring up utility covers, bringing them up to even height with the road.</p>
<p>Add bike-friendly grids to the
<a href="https://www.flickr.com/photos/108988395@N05/15989372446/in/set-72157647422248523">drainage grates</a>
on the bridge over 315, bringing them up to even height with the road. <span class="muted smaller">(‘G’ on map)</span></p>
<h4 id="cut-vegetation-to-improve-sight-lines">Cut vegetation to improve sight lines</h4>
<p>Cut back honeysuckle along inside of curve to improve sight lines. This will help eastbound cars
see bikes turning into the lower park entrance. <span class="muted smaller">(‘B’ on map)</span></p>
<h4 id="add-treatments-for-left-turn-at-park-entrance">Add treatments for left turn at park entrance</h4>
<p>Add treatments to help bikes merge into the travel lane and make the left turn into the park
<span class="muted smaller">(‘A’ on map)</span>. Suggested treatments are sharrows showing the
left-turn movement and a sign along the eastbound lane indicating cars should yield to left-turning
bikes. See image below.</p>
<p>Because of the high volume of traffic on Wilson Bridge, merging into the lane to turn left may be
challenging, even with the above treatments. A left-turn refuge box should be added to give
bikes a safe spot to wait if they are unable to make the turn.</p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/sharrow-left-turn-wilson-bridge.jpg" /></div>
<h2 id="linworth-road">Linworth Road</h2>
<h4 id="add-bike-lanes-to-linworth-road-north-of-bluffway-dr">Add bike lanes to Linworth Road north of Bluffway Dr</h4>
<p>Pave shoulder and add bike lanes between Bluffway Dr. and Hard Road New pavement should be smooth
with minimal seam between new and existing pavement.</p>
<h4 id="add-bike-boxes-at-hard-road-intersection">Add bike boxes at Hard Road intersection</h4>
<p><a href="http://urbanmilwaukee.com/2014/10/24/bike-czar-tosa-installs-first-bike-box-in-metro-area/">Bike boxes</a>
allow bikes to move up to the front of stopped traffic during the red light cycle and get a head start
into the intersection when the light turns green. <span class="muted smaller">(‘C’ on map)</span>
This reduces right hooks and other conflicts that occur when bikes and cars are mixed going into an
intersection.</p>
<p>There is also an ongoing problem with cars passing and cutting off bikes to turn right on Hard Road from
Linworth Road. Adding a 50ft section of green paint in the Linworth bike lane approaching the intersection
may help highlight the presence of bikes. See image below.</p>
<p>Since bike boxes are fairly new in Columbus, an education campaign is recommended to teach cyclists and
drivers <a href="https://www.portlandoregon.gov/transportation/article/185112">how to use the bike boxes</a>.</p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/linworth-road-bike-boxes.jpg" /></div>
<h4 id="add-treatments-for-left-turn-onto-linworth-road-e">Add treatments for left turn onto Linworth Road E.</h4>
<p>Add treatments to help bikes merge into the travel lane and make the left turn onto Linworth Road E.
<span class="muted smaller">(‘D’ on map)</span> One possibility is to add sharrows showing the
left-turn movement and a sign along the southbound lane indicating cars should yield to left-turning
bikes. See image below. Another possibility is to add a 4-way stop at the intersection.</p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/sharrow-left-turn-linworth.jpg" /></div>
<h2 id="neighborhood-roads">Neighborhood Roads</h2>
<p>For the remainder of the route into Worthington Hills (Sefton Park Dr, Rutherglen Dr, Southview Dr,
Clubview Blvd) add sharrows and signs indicating shared use road. Speed bumps may also be added
in the future since there is a high volume of cut-through traffic along this route.</p>
<h2 id="olentangy-river-road--161-option">Olentangy River Road / 161 Option</h2>
<p>The second route option requires treatments along Olentangy River Road and State Route 161. Although
this route is not recommended for the current project, it should be kept under consideration for a
future project. For completeness the findings are documented below.</p>
<h2 id="olentangy-river-road">Olentangy River Road</h2>
<p>There is a multi-use trail along the south half of Olentangy River Road. The trail ends just
south of Snouffer Road The northern section of Olentangy River Road is narrow with no shoulder and
limited sight lines. <span class="muted smaller">(‘H’ on map)</span> Widening Olentangy River Road
would require a major reconstruction. Extending the multi-use trail is equally challenging.</p>
<p>Adding sharrows to this section would probably not achieve a sufficient level of safety.
There are limited places to pass along this section of road. Even if the speed limit
were reduced from 45 mph to 35 mph, there would still be a significant speed difference
between cars and bikes, creating pressure for cars to pass bikes where it’s not safe.</p>
<h2 id="state-route-161">State Route 161</h2>
<h4 id="315-southbound-exit-ramp">315 southbound exit ramp</h4>
<p>There is near continuous movement of cars turning right on red from the right-only lane. Drivers are
looking left for other cars and are not expecting bikes, especially contraflow bike traffic from the
right. As a result, even with the walk light, it’s unsafe to cross this exit ramp while cars are present
in the right-only lane. See photo below. <span class="muted smaller"> (‘E’ on map)</span></p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/315-southbound-exit.jpg" /></div>
<p>Adding “No right on red” would help significantly, but seems heavy-handed given current levels of bike
traffic. Another possibility is to install a lighted “Caution bikes / No turn on red” sign that’s
activated by the pedestrian crossing button. This sign could be supplemented with crossing treatment
to raise awareness of the bike crossing. See image below for one possibility.</p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/315-southbound-exit-treatment.jpg" /></div>
<h4 id="315-northbound-entrance-ramp">315 northbound entrance ramp</h4>
<p>This entrance ramp has numerous problems. There are conflicts between bikes crossing the entrance
ramp and cars turning onto the entrance ramp from both the right and the left. Drivers are focused on
movement of other cars and often do not see bikes, especially contraflow bike traffic from the west.
In addition the exit of the bike underpass is located partway down the entrance ramp with poor
visibility and no safety zone to wait for a safe opportunity to cross. See photo below. <span class="muted smaller">(‘F’ on map)</span></p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/315-northbound-entrance.jpg" /></div>
<p>The recommended solution is to add a bike-only signal phase with diagonal crossing. The signal phase
would be activated by cyclists, either with a button or in-ground detectors. During the bike-only
signal phase, bikes would have a green signal. All other directions would have a red signal, allowing
bikes to cross diagonally. See image below. A lighted “Caution bikes / No turn on red” sign would also
be needed for the 315 southbound exit ramp. This sign would be lit during the bike-only signal phase.</p>
<p><a href="https://www.google.com/maps/@45.5285205,-122.6657424,3a,75y,340.97h,73.06t/data=!3m4!1e1!3m2!1sq47qxmXLP7pKCH7bdCtmnQ!2e0!5m1!1e3?hl=en">Similar designs</a>
are used in other cities.</p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/161-diagonal-crossing.jpg" /></div>
<h4 id="sidewalk-between-315-entrance-ramps">Sidewalk between 315 entrance ramps</h4>
<p>Differential settling has <a href="https://www.flickr.com/photos/108988395@N05/15989238176/in/set-72157647422248523">created seams</a>
along the line of travel. Sections of the sidewalk should be replaced to fix the seams.</p>
<p>Ideally a concrete safety wall should be added similar to the wall on the Olentangy Trail at
<a href="https://www.google.com/maps/@40.051834,-83.030313,3a,75y,266.81h,82.94t/data=!3m4!1e1!3m2!1sqKjCx_ut2DXOukymzJCERg!2e0!5m1!1e3?hl=en">Henderson Road</a>.</p>
<h2 id="wayfinding-signage">Wayfinding Signage</h2>
<p>Add wayfinding signage along route. Follow Columbus’s current bike route sign standards. Refer to signs
between Henderson Road side path and Olentangy trail for an example.</p>
<div class="full rounded shadow"><img src="/images/olentangy-trail/bike-route-wayfinding-sign.jpg" /></div>
<h2 id="bike-lane-maintenance">Bike lane maintenance</h2>
<p>Bike tires are susceptible to punctures from road debris that builds up in bike lanes. As debris builds up,
cyclists will start to avoid the bike lanes and ride in the travel lanes.</p>
<p>Bi-weekly sweeping is recommended to keep the bike lanes free of debris.</p>
<h2 id="credits">Credits</h2>
<p>The recommendations in this document are the results of a reconnaissance ride and meeting on Dec 13, 2014
attended by ODOT representatives and cycling advocates. Thank you, ODOT, for helping us review the route
options. Thank you, Rick Jordon, for taking <a href="https://www.flickr.com/photos/108988395@N05/sets/72157647422248523/">photos of the ride</a>.</p>
<p>Will Koehler <span class="muted">- Cycling advocate</span><br />
Chris Fischer <span class="muted">- Cycling advocate</span><br />
Paul Dorothy <span class="muted">- Worthington Bike/Ped Committee</span><br />
Rachael Dorothy <span class="muted">- Worthington City Council</span><br />
Rick Jordon <span class="muted">- Photographer</span><br />
Kris Anderson <span class="muted">- RIDEHome Bike Shop</span></p>
<h2 id="feedback">Feedback</h2>
<p>ODOT is accepting public feedback on the trail closure through January 9th. If you like the
recommendations in this document, or you have ideas of your own, contact Brian Davidson at ODOT:
<a href="mailto:brian.davidson@dot.state.oh.us?subject=Olentangy Trail Closure">brian.davidson@dot.state.oh.us</a></p>
<p>You can read more about the Olentangy Trail closure and the North Side Fix on
<a href="http://www.dot.state.oh.us/projects/I-270/Pages/WhatWeAreDoing.aspx#315">ODOT’s site</a></p>
Client-Side Solution For Downloading Highcharts Charts as Images2014-11-07T00:00:00-05:002016-01-29T12:00:00-04:00http://willkoehler.net/2014/11/07/client-side-solution-for-downloading-highcharts-charts-as-images<p><a href="http://www.highcharts.com">Highcharts</a> is an excellent web-based charting package. But, because it’s a
client-side solution, downloading charts as images is tricky. The default solution is to use
Highcarts’ <a href="http://www.highcharts.com/docs/export-module/export-module-overview">export module</a>,
which posts the chart’s SVG code to Highcharts’ export server. The export server converts the chart
to an image and then sends it back to the browser.</p>
<p>The export server is a well-executed solution and should work for most situations. However for my latest project,
the charts contain sensitive data. Sending them to a third-party server is not possible. I considered setting
up my own export server, or adding an endpoint to my web application. But both these solutions add complexity
to the server for a task that theoretically can be handled in the client.</p>
<p>In this blog post, I explore various client-side solutions to downloading Highcharts as images.</p>
<h2 id="strategy">Strategy</h2>
<p>Highcharts are rendered as <a href="http://www.sitepoint.com/svg-scalable-vector-graphics-overview/">SVG (scalable vector graphics)</a>.
The basic steps to download a chart as an image are:</p>
<ul>
<li>Get the chart’s SVG code</li>
<li>Render the SVG onto a <code class="language-plaintext highlighter-rouge"><cavas></code> element</li>
<li>Use toDataURL() to extract the canvas contents as an image</li>
<li>Save this image to the local filesystem</li>
</ul>
<h2 id="the-example-code">The Example Code</h2>
<p>Let’s start with a basic chart and download button.</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><div</span> <span class="na">id=</span><span class="s">'container'</span><span class="nt">></div></span>
<span class="nt"><button</span> <span class="na">id=</span><span class="s">'save_btn'</span><span class="nt">></span>Save Chart<span class="nt"></button></span></code></pre></figure>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nf">#container</span> <span class="p">{</span>
<span class="nl">max-width</span><span class="p">:</span> <span class="m">800px</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">400px</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">1em</span> <span class="nb">auto</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">save_chart</span><span class="p">(</span><span class="nx">chart</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// TODO</span>
<span class="p">}</span>
<span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#container</span><span class="dl">'</span><span class="p">).</span><span class="nx">highcharts</span><span class="p">({</span>
<span class="na">exporting</span><span class="p">:</span> <span class="p">{</span>
<span class="na">enabled</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">},</span>
<span class="na">title</span><span class="p">:</span> <span class="p">{</span>
<span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Client-Side Download Example</span><span class="dl">'</span>
<span class="p">},</span>
<span class="na">chart</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">area</span><span class="dl">'</span>
<span class="p">},</span>
<span class="na">xAxis</span><span class="p">:</span> <span class="p">{</span>
<span class="na">categories</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">Jan</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Feb</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Mar</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Apr</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">May</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Jun</span><span class="dl">'</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">Jul</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Aug</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Sep</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Oct</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Nov</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Dec</span><span class="dl">'</span><span class="p">]</span>
<span class="p">},</span>
<span class="na">series</span><span class="p">:</span> <span class="p">[{</span>
<span class="na">data</span><span class="p">:</span> <span class="p">[</span><span class="mf">29.9</span><span class="p">,</span> <span class="mf">71.5</span><span class="p">,</span> <span class="mf">106.4</span><span class="p">,</span> <span class="mf">129.2</span><span class="p">,</span> <span class="mf">144.0</span><span class="p">,</span> <span class="mf">176.0</span><span class="p">,</span> <span class="mf">135.6</span><span class="p">,</span> <span class="mf">148.5</span><span class="p">,</span> <span class="mf">216.4</span><span class="p">,</span> <span class="mf">194.1</span><span class="p">,</span> <span class="mf">95.6</span><span class="p">,</span> <span class="mf">54.4</span><span class="p">]</span>
<span class="p">}]</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#save_btn</span><span class="dl">'</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">save_chart</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#container</span><span class="dl">'</span><span class="p">).</span><span class="nx">highcharts</span><span class="p">());</span>
<span class="p">});</span>
<span class="p">});</span></code></pre></figure>
<h2 id="1-get-the-charts-svg-code">1. Get the chart’s SVG code</h2>
<p>Highcharts has an exporting module that adds functions for downloading and printing charts. This module
must be included separately.</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><script </span><span class="na">src=</span><span class="s">"http://code.highcharts.com/modules/exporting.js"</span><span class="nt">></script></span></code></pre></figure>
<p>The exporting module adds the function <a href="http://api.highcharts.com/highcharts#Chart.getSVG">getSVG</a>
which extracts the chart’s SVG code and sanitizes it for export.</p>
<p>NOTE: Because we don’t specify a hard-coded chart width in our css, we need to specify sourceWidth and
sourceHeight in the call to <code class="language-plaintext highlighter-rouge">getSVG</code>. This seems like a bug to me. <code class="language-plaintext highlighter-rouge">getSVG</code> should be able to calculate
the chart dimensions itself. (If we can get the dimensions from chart.chartWidth / chart.chartHeight,
<code class="language-plaintext highlighter-rouge">getSVG</code> could too)</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">svg</span> <span class="o">=</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">getSVG</span><span class="p">({</span>
<span class="na">exporting</span><span class="p">:</span> <span class="p">{</span>
<span class="na">sourceWidth</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">chartWidth</span><span class="p">,</span>
<span class="na">sourceHeight</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">chartHeight</span>
<span class="p">}</span>
<span class="p">});</span></code></pre></figure>
<p>NOTE: Alternatively, you could grab the chart’s SVG code directly.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">svg</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#container .highcharts-container</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">()</span></code></pre></figure>
<p>While this eliminates the need for the exporting module, it also skips the sanitizing code. The most
obvious problem I ran into is that the resulting image may contain tooltips, which you probably don’t
want in your downloaded chart image.</p>
<h2 id="2-render-the-charts-svg-onto-a-canvas-element">2. Render the chart’s SVG onto a <code class="language-plaintext highlighter-rouge"><canvas></code> element</h2>
<p>Rendering SVG onto a <code class="language-plaintext highlighter-rouge"><canvas></code> element is easy. You can put SVG into the <code class="language-plaintext highlighter-rouge">src</code> attribute of an <code class="language-plaintext highlighter-rouge"><img></code>
tag. The browser will render the SVG into the image. Once the image has been rendered, you render this
image onto the canvas element. You don’t even need to insert the image into the DOM for this to work.</p>
<p>To show how this works, we’ll create a preliminary version of <code class="language-plaintext highlighter-rouge">save_chart</code> that renders the chart’s SVG
onto a <code class="language-plaintext highlighter-rouge"><canvas></code> element.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">EXPORT_WIDTH</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">save_chart</span><span class="p">(</span><span class="nx">chart</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">render_width</span> <span class="o">=</span> <span class="nx">EXPORT_WIDTH</span><span class="p">;</span>
<span class="nx">render_height</span> <span class="o">=</span> <span class="nx">render_width</span> <span class="o">*</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">chartHeight</span> <span class="o">/</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">chartWidth</span>
<span class="c1">// Get the cart's SVG code</span>
<span class="kd">var</span> <span class="nx">svg</span> <span class="o">=</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">getSVG</span><span class="p">({</span>
<span class="na">exporting</span><span class="p">:</span> <span class="p">{</span>
<span class="na">sourceWidth</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">chartWidth</span><span class="p">,</span>
<span class="na">sourceHeight</span><span class="p">:</span> <span class="nx">chart</span><span class="p">.</span><span class="nx">chartHeight</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// Create a canvas</span>
<span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">canvas</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">render_height</span><span class="p">;</span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">render_width</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span>
<span class="c1">// Create an image and draw the SVG onto the canvas</span>
<span class="kd">var</span> <span class="nx">image</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Image</span><span class="p">;</span>
<span class="nx">image</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">'</span><span class="s1">2d</span><span class="dl">'</span><span class="p">).</span><span class="nx">drawImage</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">render_width</span><span class="p">,</span> <span class="nx">render_height</span><span class="p">);</span>
<span class="p">};</span>
<span class="nx">image</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">data:image/svg+xml;base64,</span><span class="dl">'</span> <span class="o">+</span> <span class="nb">window</span><span class="p">.</span><span class="nx">btoa</span><span class="p">(</span><span class="nx">svg</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>NOTE: Since we typically want to download a chart in a higher resolution than it’s displayed on the page,
we use EXPORT_WIDTH to control the size of the exported image.</p>
<p>See <a href="http://jsfiddle.net/willkoehler/1p81fbzs/">JSFiddle demo</a></p>
<h2 id="3-use-todataurl-to-extract-the-canvas-contents-as-an-image">3. Use <code class="language-plaintext highlighter-rouge">toDataURL</code> to extract the canvas contents as an image</h2>
<p>The canvas element has a handy function <code class="language-plaintext highlighter-rouge">toDataURL</code> that extracts its content as an image.
<code class="language-plaintext highlighter-rouge">toDataURL</code> returns a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs">data URI</a> with
the image encoded in base64. This URI can then be downloaded as an image to the user’s computer.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="dl">"</span><span class="s2">image/png</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">download</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">filename</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">.png</span><span class="dl">'</span><span class="p">)</span> <span class="c1">// (still need to define this)</span></code></pre></figure>
<p>NOTE: We don’t need to to add the canvas element to the DOM in order to render and extract the
image data. So we can remove <code class="language-plaintext highlighter-rouge">document.body.appendChild(canvas)</code>.</p>
<h2 id="4-save-the-image-to-the-local-filesystem">4. Save the image to the local filesystem</h2>
<p>This is where things get a little hacky. There is no standard way to save data to the user’s
local filesystem. The most widely used technique is to create an <code class="language-plaintext highlighter-rouge"><a></code> tag and then send
it a click event.</p>
<p>The <code class="language-plaintext highlighter-rouge">download</code> attribute of the <code class="language-plaintext highlighter-rouge"><a></code> tag tells the browser to present the target (our chart
image) as a downloadable file with a given filename. Pretty nice, but it’s currently only
supported in Chrome and Firefox. Other browsers will just open the image in the current window
instead of presenting it as a download. Not ideal, but it’s the best we can do for now.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">download</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">download</span> <span class="o">=</span> <span class="nx">filename</span><span class="p">;</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">data</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<p>And that’s everything. See this <a href="http://jsfiddle.net/willkoehler/hc6aa7yg/">JSFiddle demo</a>
for the complete code.</p>
<h2 id="not-quite-so-fast">Not quite so fast</h2>
<p>So everything looks good… except it doesn’t work in IE. The first problem is that IE throws a
“SecurityError” on the call to <code class="language-plaintext highlighter-rouge">toDataURL</code>. This is because IE considers the canvas to be
“tainted” once an SVG image has been rendered. SVG code can reference content from the user’s
local file system. Once that’s in the canvas element it would be security vulnerability
to allow the canvas contents to be extracted. Otherwise a malicious site could read data from
the user’s file system and send it on to an external server.</p>
<p><a href="https://connect.microsoft.com/IE/feedback/details/809823/draw-svg-image-on-canvas-context">This forum message</a>
is the only reference I can find discussing the issue in IE. Either Chrome, Firefox, and Safari have
ways of sanitizing SVG content before rendering onto a canvas, or they don’t allow SVG code to
reference local content, or they are not concerned about this vulnerability.</p>
<h2 id="ie-work-around-1-use-canvg">IE work-around #1: use canvg</h2>
<p>We can work-around the canvas tainting issue with the <a href="https://github.com/gabelerner/canvg">canvg library</a>.
canvg is a full SVG parser and renderer. It can render SVG onto a canvas element with all of the features
of the browser’s built-in SVG render. It’s an impressive piece of code, and works flawlessly. But it’s
also 2800 lines of javascript that are entirely unnecessary in any modern browser, because the browser
itself can render SVG onto a canvas element. However, because canvg parses the SVG code by hand, manually
rendering each path, box, quadratic curve, gradient, pattern etc. it sidesteps the IE “SecurityError”.</p>
<p>Replace the <code class="language-plaintext highlighter-rouge">var image = new Image...</code> code with this:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">canvg</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">svg</span><span class="p">,</span> <span class="p">{</span>
<span class="na">scaleWidth</span><span class="p">:</span> <span class="nx">render_width</span><span class="p">,</span>
<span class="na">scaleHeight</span><span class="p">:</span> <span class="nx">render_height</span><span class="p">,</span>
<span class="na">ignoreDimensions</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="dl">"</span><span class="s2">image/png</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">download</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">filename</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">.png</span><span class="dl">'</span><span class="p">);</span></code></pre></figure>
<h2 id="still-not-working-url-limit">Still not working… URL limit</h2>
<p>With the “SecurityError” fix in place, we hit the next barrier. IE stops with the error
“The data area passed to a system call is too small” on the call to <code class="language-plaintext highlighter-rouge">a.click()</code>. I <em>think</em>
this is because IE has a <a href="http://support.microsoft.com/kb/208427">2083 character URL limit</a>.
The data URIs generated by <code class="language-plaintext highlighter-rouge">toDataURL</code> are much longer than 2083 characters.</p>
<h2 id="ie-work-around-2-use-mssaveoropenblob">IE work-around #2: use <code class="language-plaintext highlighter-rouge">msSaveOrOpenBlob</code></h2>
<p>I’m calling this a work-around. But it’s actually an improvement over the <code class="language-plaintext highlighter-rouge"><a></code> tag hack.
IE has a function <a href="http://msdn.microsoft.com/en-us/library/ie/hh779016(v=vs.85).aspx">msSaveOrOpenBlob</a>
that presents client-side data to the user as if it were downloaded from the internet. It’s
exactly what we need. Let’s add logic to use <code class="language-plaintext highlighter-rouge">msSaveOrOpenBlob</code> in IE and use the <code class="language-plaintext highlighter-rouge"><a></code> tag hack everywhere else.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">download</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">download_in_ie</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> <span class="o">||</span> <span class="nx">download_with_link</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">filename</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Works in IE10 and newer</span>
<span class="kd">function</span> <span class="nx">download_in_ie</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">(</span><span class="nb">navigator</span><span class="p">.</span><span class="nx">msSaveOrOpenBlob</span> <span class="o">&&</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">msSaveOrOpenBlob</span><span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">msToBlob</span><span class="p">(),</span> <span class="nx">filename</span><span class="p">));</span>
<span class="p">}</span>
<span class="c1">// Works in Chrome and FF. Safari just opens image in current window, since</span>
<span class="c1">// .download attribute is not supported</span>
<span class="kd">function</span> <span class="nx">download_with_link</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="nx">filename</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">)</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">download</span> <span class="o">=</span> <span class="nx">filename</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="dl">"</span><span class="s2">image/png</span><span class="dl">"</span><span class="p">)</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<p>Here’s the <a href="http://jsfiddle.net/willkoehler/vushe780/">JSFiddle demo</a> putting it all together.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>It took a while to get here, but the final solution isn’t bad. My main reservation is pulling in
canvg. But it’s a stable library and seems like a necessary tradeoff for IE support.</p>
<p>A few other libraries that I tested along the way:</p>
<p><a href="https://github.com/eligrey/FileSaver.js/">FileSaver.js</a> - If you want a fully-supported,
cross-browser “just save it” solution, this is a good library. At its core, FileSaver.js uses
the same <code class="language-plaintext highlighter-rouge"><a></code> tag hack I used above. But FileSaver.js has a lot of additional complexity
to handle edge cases that I didn’t feel was needed for this situation. I ultimately decided
not to use it. But it’s a solid library and Eli Grey has put a lot of work into making it work
flawlessly. Definitely work a look.</p>
<p><a href="https://github.com/eligrey/canvas-toBlob.js">canvas-toBlob.js</a> - If you use FileSaver.js, you will
need this library to pull the blob data out of the canvas element. It implements the standard
<a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toBlob">canvas.toBlob()</a>
function for browsers that don’t support it yet. It’s another solid piece of code by Eli Grey.</p>
Deploy a Rails App with AWS OpsWorks2014-10-15T00:00:00-04:002015-02-08T12:00:00-04:00http://willkoehler.net/2014/10/15/deploy-a-rails-app-with-aws-opsworks<p>Many of the Rails apps I build for my clients are custom, in-house tools with limited traffic. To
keep costs down and maximize performance, I run everything (web app, database, background jobs) on
a single server. Although Amazon OpsWorks is capable of managing applications running on multi-instance
clusters, it is equally capable of setting up a single-server application. This post describes
the steps I use to get a basic Rails app up and running on a single server.</p>
<h2 id="1-prepare-your-app">1. Prepare your app</h2>
<h4 id="add-unicorn-to-the-gemfile">Add “unicorn” to the Gemfile</h4>
<p>OpsWorks supports Apache/Passenger and Nginx/Unicorn. They are both good solutions but I prefer
the simplicity and performance of Nginx. So for this project, we are using Nginx/Unicorn. Add
“unicorn” to the project Gemfile.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">group</span> <span class="ss">:production</span> <span class="k">do</span>
<span class="n">gem</span> <span class="s1">'unicorn'</span>
<span class="k">end</span></code></pre></figure>
<h4 id="precompile-assets-on-deployment">Precompile assets on deployment</h4>
<p>There are lots of ways to serve Rails assets in production. OpsWorks doesn’t provide any direct
support for the Rails asset pipeline, leaving the solution up to you.</p>
<p>For a single-server applications, I prefer to precompile the assets on the server during
deployment and serve them with Nginx from public/assets. The precompile step can be run from a chef
<a href="http://docs.aws.amazon.com/opsworks/latest/userguide/workingcookbook-extend-hooks.html">deploy hook</a>.
Create <code class="language-plaintext highlighter-rouge">deploy/before_symlink.rb</code> in your app and add the contents below.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># Precompile assets. Assets are compiled into shared/assets and shared between deploys.</span>
<span class="n">rails_env</span> <span class="o">=</span> <span class="n">new_resource</span><span class="p">.</span><span class="nf">environment</span><span class="p">[</span><span class="s2">"RAILS_ENV"</span><span class="p">]</span>
<span class="n">shared_path</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">new_resource</span><span class="p">.</span><span class="nf">deploy_to</span><span class="si">}</span><span class="s2">/shared"</span>
<span class="c1"># create shared directory for assets, if it doesn't exist</span>
<span class="n">directory</span> <span class="s2">"</span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/assets"</span> <span class="k">do</span>
<span class="n">mode</span> <span class="mo">0770</span>
<span class="n">action</span> <span class="ss">:create</span>
<span class="n">recursive</span> <span class="kp">true</span>
<span class="k">end</span>
<span class="c1"># symlink current deploy's asset folder to shared assets each deploy</span>
<span class="n">link</span> <span class="s2">"</span><span class="si">#{</span><span class="n">release_path</span><span class="si">}</span><span class="s2">/public/assets"</span> <span class="k">do</span>
<span class="n">to</span> <span class="s2">"</span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/assets"</span>
<span class="k">end</span>
<span class="c1"># precompile assets into public/assets (which is symlinked to shared assets folder)</span>
<span class="n">execute</span> <span class="s2">"rake assets:precompile"</span> <span class="k">do</span>
<span class="n">cwd</span> <span class="n">release_path</span>
<span class="n">command</span> <span class="s2">"bundle exec rake assets:precompile"</span>
<span class="n">environment</span> <span class="s1">'RAILS_ENV'</span> <span class="o">=></span> <span class="n">rails_env</span>
<span class="k">end</span></code></pre></figure>
<p>Check in your changes and push them to the repo/branch you will deploy from.</p>
<h2 id="2-create-a-stack">2. Create a stack</h2>
<p>An AWS stack is the container that houses all the other components (layers, instances, and apps)
and allows you to manage them as a unit.</p>
<p>Login to the <a href="https://aws.amazon.com/console/">AWS management console</a> and choose OpsWorks from the
Services menu. Click “Add Your First Stack”. All settings can be left at their default except:</p>
<ul>
<li>
<p><strong>Name:</strong> Choose a name for your stack.</p>
</li>
<li>
<p><strong>Default operating system</strong>: Choose <code class="language-plaintext highlighter-rouge">Ubuntu 14.04 LTS</code> Rails needs a Javascript runtime to
precompile assets on the server. The Ubuntu nodejs package is the easiest way to install a
Javascript runtime.</p>
</li>
<li>
<p><strong>Default SSH key:</strong> A SSH key is required to login to instances via SSH later. Follow
<a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html">these steps</a>
to create a new keypair and then select that keypair here.</p>
</li>
</ul>
<div class="full shadow rounded"><img src="/images/ops-works/stack.png" /></div>
<p>Click “Add Stack” to create the stack.</p>
<h2 id="3-create-a-rails-app-server-layer">3. Create a “Rails App Server” layer</h2>
<p>An OpsWorks layer is a collection of packages, scripts, and settings to configure an
instance for a particular purpose (such as running a Rails app). In our case, we are going to
create two layers: “Rails App Server” and “MySQL”. Both these layers will be assigned to the
same instance, which creates a single server running the Rails app and a MySQL database.</p>
<p>Click “Add a layer” and choose <code class="language-plaintext highlighter-rouge">Rails App Server</code> as the layer type. Choose <code class="language-plaintext highlighter-rouge">Nginx and Unicorn</code>
as the rails stack. Leave all other settings at their default.</p>
<div class="full shadow rounded"><img src="/images/ops-works/create_rails_app_layer.png" /></div>
<p>Click “Add Layer” to create the layer.</p>
<p>After creating the layer, we need to edit it to add the nodejs package. Click “Recipes” to
show the layer’s recipes. Then click “Edit”. Add the <code class="language-plaintext highlighter-rouge">nodejs</code> package under “OS Packages”.</p>
<div class="full shadow rounded"><img src="/images/ops-works/edit_rails_app_layer.png" /></div>
<p>Click “Save” to save your changes.</p>
<p>If your stack is running in a Virtual Private Cloud, you will need to configure the layer to
allocate a public IP address. By default, instances created in a VPC don’t get a public IP.
Click “Network”, then click “Edit” and enable “Elastic IP addresses”.</p>
<div class="full shadow rounded"><img src="/images/ops-works/edit_rails_app_layer_2.png" /></div>
<p>Click “Save” to save your changes.</p>
<h2 id="4-create-a-mysql-layer">4. Create a MySQL layer</h2>
<p>Use the Navigation menu to go back to the Layers page. Add a new layer by clicking “+ Layer”
and choose <code class="language-plaintext highlighter-rouge">MySQL</code> as the layer type. Leave all settings as their default. You don’t need to
remember the MySQL root user password. OpsWorks will manage everything for you, including
creating your <code class="language-plaintext highlighter-rouge">database.yml</code> file.</p>
<div class="full shadow rounded"><img src="/images/ops-works/create_mysql_layer.png" /></div>
<p>Click “Add Layer” to create the layer.</p>
<h2 id="5-create-an-instance">5. Create an instance</h2>
<p>We’ve configured the software that will run on the server. Next we create the instance
itself, which is the server that will run our application. Use the Navigation menu to go to
the Instances page. Click “Add an instance” under “Rails App Server”. Leave all the settings
at their defaults.</p>
<div class="full shadow rounded"><img src="/images/ops-works/create_instance.png" /></div>
<p>Click “Add Instance” to create the instance.</p>
<p>We are now going to add the MySQL layer to the instance we just created, so both the
Rails app and MySQL will run on the same instance. Click “Add an instance”
under “MySQL”. Choose the “Existing” tab and check the box next to your instance (<code class="language-plaintext highlighter-rouge">rails-app1</code>
in this case).</p>
<div class="full shadow rounded"><img src="/images/ops-works/add_mysql_to_instance.png" /></div>
<p>Click “Add Instance” to assign the MySQL layer to the Rails app instance.</p>
<h2 id="6-add-an-application">6. Add an application</h2>
<p>We’ve finished configuring our server, now it’s time to setup the application we are going
to run on the server. Use the Navigation menu to go to the Apps page. Click “Add an app” to
create an application. Leave settings at their default except:</p>
<ul>
<li>
<p><strong>Name:</strong> Choose a name for your application.</p>
</li>
<li>
<p><strong>Data source type:</strong> Choose “OpsWorks”.</p>
</li>
<li>
<p><strong>Database instance:</strong> Choose the instance we just created.</p>
</li>
<li>
<p><strong>Application Source:</strong> There are many options to give OpsWorks access to your application’s
source code. I use GitHub with a <a href="https://developer.github.com/guides/managing-deploy-keys/#deploy-keys">deploy key</a>.
You can also use Subversion, HTTP, or S3.</p>
</li>
<li>
<p><strong>Environment Variables:</strong> Add any environment variables needed by your app here. Environment
variables are <a href="http://12factor.net/config">one solution</a> for managing Rails app secrets.</p>
</li>
<li>
<p><strong>Domain name:</strong> You can leave this blank since we only have one app in the stack.</p>
</li>
</ul>
<div class="full shadow rounded"><img src="/images/ops-works/create_app.png" /></div>
<p>Click “Add App” to create the application.</p>
<h2 id="7-adjust-innodb_buffer_pool_size">7. Adjust innodb_buffer_pool_size</h2>
<p>The MySQL recipe in OpsWorks is optimized to run MySQL on a dedicated server. Out of the box a
majority of the server memory will be allocated to MySQL. Depending on the instance size, the
combination of Rails workers and MySQL may exhaust memory and the instance will fail to start.</p>
<p>To fix this, we need to reduce <code class="language-plaintext highlighter-rouge">innodb_buffer_pool_size</code>. Use the Navigation menu to go to the
Stack page. Click “Stack Settings” and then “Edit”. Under “Configuration Management” add the
following Custom JSON</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nl">"mysql"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"tunable"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"innodb_buffer_pool_size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"100M"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>The specific size of <code class="language-plaintext highlighter-rouge">innodb_buffer_pool_size</code> depends on your application and instance size.
For a small, single-server application 100 megabytes should be plenty.</p>
<div class="full shadow rounded"><img src="/images/ops-works/stack_configuration_1.png" /></div>
<p>Click “Save” to save your changes.</p>
<h2 id="8-enable-far-future-cache-headers">8. Enable far-future cache headers</h2>
<p>For best performance, it’s recommended to set “far-future” headers on your assets, effectively
instructing the browser to cache the assets forever. Unfortunately OpsWorks doesn’t do this out of the
box and there’s no clean way to add it. The only solution I’ve found is to create a custom cookbook,
copy <code class="language-plaintext highlighter-rouge">nginx_unicorn_web_app.erb</code> from the AWS cookbook and customize it to add the correct asset headers.</p>
<p>To incorporate this updated recipe into our application, we need to edit the stack settings and add
a custom cookbook. Use the Navigation menu to go to the Stack page. Click “Stack Settings” and then
“Edit”. Under “Configuration Management”, turn on “Use custom Chef cookbooks”
and point it to my custom OpsWorks cookbook <a href="https://github.com/willkoehler/opsworks-cookbooks.git">https://github.com/willkoehler/opsworks-cookbooks.git</a></p>
<p>This cookbook also disables <code class="language-plaintext highlighter-rouge">delete_cached_copy</code> so that a cached copy of the app code is kept
between deploys, speeding up deployment.</p>
<div class="full shadow rounded"><img src="/images/ops-works/stack_configuration_2.png" /></div>
<p>Click “Save” to save your changes.</p>
<h2 id="9-start-the-instance">9. Start the instance</h2>
<p>Now everything is ready to go. Let’s start our instance. This will provision the server, boot it,
and install all the software needed to run our app.</p>
<p>Use the Navigation menu to go to the Instances page. Click “Start All Instances”. It will take ~10
minutes to boot and configure the server.</p>
<div class="full shadow rounded"><img src="/images/ops-works/start_instance.png" /></div>
<h2 id="10-deploy-the-app">10. Deploy the app</h2>
<p>Once the instance is online, we can deploy the app. Use the Navigation menu to go to the Apps
page. Click “Deploy” under Actions for your app. Enable “Migrate database” so OpsWorks sets
up the database structure during the deployment.</p>
<div class="full shadow rounded"><img src="/images/ops-works/deploy.png" /></div>
<p>Click “Deploy” to start the deployment.</p>
<h2 id="11-login-via-ssh">11. Login via SSH</h2>
<p>We’re almost done. But, depending on your app, there may be a few steps that need to
be done by hand via SSH after the first deploy.</p>
<p>For example, my app has database seeds. OpsWorks creates the database structure
but it doesn’t do anything else. To seed the database you must login via SSH.</p>
<p>You will need the SSH key you assigned to the stack earlier. You also need the public
IP address of the instance, which you can get from the Instances page.</p>
<p>SSH into the instance:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text">ssh -i your-private-key ubuntu@your_domain_or_ip_address</code></pre></figure>
<p>OpsWorks creates a <code class="language-plaintext highlighter-rouge">deploy</code> user to deploy and manage the Rails application. Any application
work you do on the server should be done as the <code class="language-plaintext highlighter-rouge">deploy</code> user. In this case I’m going to seed
the database.</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text">sudo su deploy
cd /srv/www/your_app_name/current/
RAILS_ENV=production bundle exec rake db:seed</code></pre></figure>
<h2 id="ready-to-go">Ready to go</h2>
<p>That’s everything. Point your browser to the public IP and your app should be live. If you have
problems, check the production log: <code class="language-plaintext highlighter-rouge">/srv/www/your_app_name/shared/log/production.log</code></p>
<h2 id="future-deployments">Future deployments</h2>
<p>Future deployments are easy. Check in your code and push to your deploy repo/branch.
Use the Navigation menu to go to the Apps page. Click “Deploy” under Actions for your app.</p>
<h2 id="upcoming-posts">Upcoming posts</h2>
<p>I wanted to keep this post focused on the core steps to deploying a Rails app. But there’s more
to cover. I have two more posts coming up:</p>
<ul>
<li>Use Snapshots for MySQL Backup in AWS OpsWorks</li>
<li>Manage Your App’s secrets.yml with AWS OpsWorks</li>
</ul>
Vertically Align Content With CSS Using This One Weird Trick2014-08-28T00:00:00-04:002014-09-01T12:00:00-04:00http://willkoehler.net/2014/08/28/vertically-align-content-with-css-using-this-one-weird-trick<p>Phil Karlton famously said “there are only two hard things in computer science: cache invalidation
and naming things”. I would add a third: vertically aligning content with CSS.</p>
<h2 id="the-scenario">The scenario</h2>
<p>We want to vertically align variable height content within a wrapper div.</p>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.wrapper</span> <span class="p">{</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">150px</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#CCC</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.content</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#493ACC</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><div</span> <span class="na">class=</span><span class="s">'wrapper'</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
This should be<span class="nt"><br></span>
vertically centered
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
Also this
<span class="nt"></div></span>
<span class="nt"></div></span></code></pre></figure>
<div class="wrapper">
<div class="content">
This should be<br />
vertically centered
</div>
<div class="content">
Also this
</div>
</div>
<h2 id="just-use-vertical-alignmiddle">Just use vertical-align:middle?</h2>
<p>Most people (myself included) expect that adding <code class="language-plaintext highlighter-rouge">vertical-align:middle</code> to the wrapper will
do this trick. But <code class="language-plaintext highlighter-rouge">vertical-align</code> doesn’t set the alignment of content within a wrapper, it sets the
alignment of inline elements relative to each other.</p>
<p>Let’s try some variations of <code class="language-plaintext highlighter-rouge">vertical-align</code> and see how it works.</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><span</span> <span class="na">class=</span><span class="s">'text big'</span><span class="nt">></span>BIG text<span class="nt"></span></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">'text small'</span><span class="nt">></span>SMALL text<span class="nt"></span></span></code></pre></figure>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.text.big</span> <span class="p">{</span> <span class="nl">font-size</span><span class="p">:</span> <span class="m">40px</span><span class="p">;</span> <span class="p">}</span>
<span class="nc">.text.small</span> <span class="p">{</span> <span class="nl">font-size</span><span class="p">:</span> <span class="m">18px</span><span class="p">;</span> <span class="p">}</span>
<span class="nc">.text</span> <span class="p">{</span> <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">baseline</span><span class="p">;</span> <span class="p">}</span> <span class="c">/* this is the default */</span></code></pre></figure>
<div class="boundary">
<span class="text big">BIG text</span>
<span class="text small">SMALL text</span>
</div>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.text</span> <span class="p">{</span> <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">top</span><span class="p">;</span> <span class="p">}</span></code></pre></figure>
<div class="boundary">
<span class="text big" style="vertical-align:top">BIG text</span>
<span class="text small" style="vertical-align:top">SMALL text</span>
</div>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.text</span> <span class="p">{</span> <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">bottom</span><span class="p">;</span> <span class="p">}</span></code></pre></figure>
<div class="boundary">
<span class="text big" style="vertical-align:bottom">BIG text</span>
<span class="text small" style="vertical-align:bottom">SMALL text</span>
</div>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.text</span> <span class="p">{</span> <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span> <span class="p">}</span></code></pre></figure>
<div class="boundary">
<span class="text big" style="vertical-align:middle">BIG text</span>
<span class="text small" style="vertical-align:middle">SMALL text</span>
</div>
<h2 id="making-vertical-alignmiddle-work-for-you">Making vertical-align:middle work for you</h2>
<p>Going back to the original scenario, instead of applying <code class="language-plaintext highlighter-rouge">vertical-align:middle</code> to the wrapper,
let’s apply <code class="language-plaintext highlighter-rouge">vertical-align:middle</code> to the content. Seems counter-intuitive, but bear with me.</p>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.content</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#493ACC</span><span class="p">;</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<div class="wrapper">
<div class="content" style="vertical-align:middle">
This should be<br />
vertically centered
</div>
<div class="content" style="vertical-align:middle">
Also this
</div>
</div>
<p>We’re getting closer. Let’s add a third element with 100% height</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><div</span> <span class="na">class=</span><span class="s">'wrapper'</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span> <span class="na">style=</span><span class="s">'height:100%'</span><span class="nt">></span><span class="ni">&nbsp;</span><span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
I would like this<span class="nt"><br></span>
to be vertically centered
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
Also this
<span class="nt"></div></span>
<span class="nt"></div></span></code></pre></figure>
<div class="wrapper">
<div class="content" style="vertical-align:middle;height:100%"> </div>
<div class="content" style="vertical-align:middle">
This should be<br />
vertically centered
</div>
<div class="content" style="vertical-align:middle">
Also this
</div>
</div>
<p>This is starting to look promising!</p>
<h2 id="make-css-do-the-work-for-us">Make CSS do the work for us</h2>
<p>Manually adding a third element to vertically align things is a burden. So let’s
have css do that for us with a <code class="language-plaintext highlighter-rouge">:before</code> pseudo element.</p>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.wrapper</span><span class="nd">:before</span> <span class="p">{</span>
<span class="nl">content</span><span class="p">:</span> <span class="s2">''</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="no">red</span><span class="p">;</span> <span class="c">/* so we can see what's going on */</span>
<span class="p">}</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><div</span> <span class="na">class=</span><span class="s">'wrapper'</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
I would like this<span class="nt"><br></span>
to be vertically centered
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
Also this
<span class="nt"></div></span>
<span class="nt"></div></span></code></pre></figure>
<div class="wrapper with_centering visible_centering broken_wrapping">
<div class="content" style="vertical-align:middle">
This should be<br />
vertically centered
</div>
<div class="content" style="vertical-align:middle">
Also this
</div>
</div>
<p>It’s working! Now remove the red border from the pseudo element and… Voila</p>
<div class="wrapper with_centering">
<div class="content" style="vertical-align:middle">
This should be<br />
vertically centered
</div>
<div class="content" style="vertical-align:middle">
Also this
</div>
</div>
<h2 id="edge-cases">Edge cases</h2>
<p>Well this is awesome. But you knew there would be edge cases right? Consider this scenario:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><div</span> <span class="na">class=</span><span class="s">'wrapper'</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
This text should wrap as needed
<span class="nt"></div></span>
<span class="nt"></div></span></code></pre></figure>
<div class="wrapper with_centering">
<div class="content" style="vertical-align:middle">
This text should wrap as needed
</div>
</div>
<p>Your customer views the page on a small device, or resizes their browser and…</p>
<div style="height: 250px">
<div class="wrapper with_centering broken_wrapping" style="width:200px">
<div class="content" style="vertical-align:middle">
This text should wrap as needed
</div>
</div>
</div>
<p>What happened?? The text wraped, but the content dropped below the wrapper. Let’s make
our before pseudo element visible again.</p>
<div style="height: 250px">
<div class="wrapper with_centering broken_wrapping visible_centering" style="width:200px">
<div class="content" style="vertical-align:middle">
This text should wrap as needed
</div>
</div>
</div>
<p>Now we can see what happened. The <code class="language-plaintext highlighter-rouge">:before</code> pseudo element is an inline-block element. So
it’s getting wrapped as well. We can fix that with another trick. Add a negative right
margin to the <code class="language-plaintext highlighter-rouge">:before</code> pseudo element so the pseudo element doesn’t take up any horizontal
space.</p>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.wrapper</span><span class="nd">:before</span> <span class="p">{</span>
<span class="nl">content</span><span class="p">:</span> <span class="s2">''</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">-.35em</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>Now the text is wrapping like it should.</p>
<div class="wrapper with_centering" style="width:200px">
<div class="content" style="vertical-align:middle">
This text should wrap as needed
</div>
</div>
<h2 id="all-together-now">All together now</h2>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.wrapper</span> <span class="p">{</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">150px</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#CCC</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.wrapper</span><span class="nd">:before</span> <span class="p">{</span>
<span class="nl">content</span><span class="p">:</span> <span class="s2">''</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">-.35em</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.content</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#493ACC</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><div</span> <span class="na">class=</span><span class="s">'wrapper'</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
This should be<span class="nt"><br></span>
vertically centered
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">'content'</span><span class="nt">></span>
Also this
<span class="nt"></div></span>
<span class="nt"></div></span></code></pre></figure>
<h2 id="conclusion">Conclusion</h2>
<p>I’ve used many tricks over the years to vertically align content. They all have tradeoffs. Some
add a lot of noise to the CSS. Others (like using <code class="language-plaintext highlighter-rouge">display:table-cell</code>) have side effects. I’ve just
recently discovered this trick, but I think it’s going to be my new go-to solution. In terms of
compatibility, it works on all modern browsers + IE8.</p>
<p>Thanks to Chris Coyler for the <a href="http://css-tricks.com/centering-in-the-unknown/">idea</a> and
Gary Turner for the <a href="http://gtwebdev.com/workshop/vcenter/vcenter-inline-css.php">original work</a>.</p>
<!-- styles for this post -->
<style>
.vaswc .wrapper {
height: 150px;
border: 1px solid #CCC;
text-align: center;
margin-bottom: 2em;
}
.vaswc .wrapper.with_centering:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -.35em;
}
.vaswc .wrapper.with_centering.visible_centering:before {
border: 1px solid red;
}
.vaswc .wrapper.with_centering.broken_wrapping:before {
margin-right: 0em;
}
.vaswc .content {
display: inline-block;
border: 1px solid #493ACC;
text-align: center;
padding: 10px;
}
.vaswc .boundary {
border: 1px solid #CCC;
padding: 0 15px;
margin-bottom: 2em;
}
.vaswc .text {
color: #666;
vertical-align: baseline;
}
.vaswc .text.big {
font-size: 2.3em;
margin-right: 10px;
}
.vaswc .text.small {
font-size: 1em;
}
</style>
Save 50 Hours Setting up Your Jekyll Blog2014-08-26T00:00:00-04:002014-10-17T12:00:00-04:00http://willkoehler.net/2014/08/26/save-50-hours-setting-up-your-jekyll-blog<p>When I decided to start blogging, I explored all the major blogging platforms. I quickly came to the conclusion
that, in order to get exactly what I wanted, I would need to have full control of the design and code.</p>
<p>As a Ruby developer, <a href="http://jekyllrb.com">Jekyll</a> was a natural choice. Jekyll is a static site generator that’s
“blogging aware”. “Blogging aware” means that Jekyll has conventions for managing your blog posts. You put all your
posts in the <code class="language-plaintext highlighter-rouge">_posts/</code> folder. Jekyll generates pages for them and gives you template variables to index through
the pages.</p>
<p>In addition to supporting blogs out of the box, Jekyll had everything else I wanted:</p>
<ul>
<li>Good <a href="http://jekyllthemes.org">themes</a></li>
<li>Support for source-code highlighting</li>
<li>Posts can be written in <a href="http://daringfireball.net/projects/markdown/syntax">Markdown</a></li>
<li>Easy <a href="https://help.github.com/articles/using-jekyll-with-pages">integration</a> with Github pages (free hosting!)</li>
<li>Support for layouts, partials, and other features that make it a pleasure to use.</li>
<li>Easy to get your head around the entire site. No magic. Nothing is hidden.</li>
</ul>
<h2 id="jekyll-incorporated-theme">Jekyll Incorporated Theme</h2>
<p>The next thing I needed was a theme. I quickly settled on <a href="http://incorporated.sendtoinc.com">Jekyll Incorporated</a>.
It’s clean, simple, and very “Medium-like”. Thanks <a href="https://sendtoinc.com/">Kippt, Inc.</a> for sharing this awesome theme
with the community.</p>
<p>Jekyll Incorporated was built to be easily customized via <code class="language-plaintext highlighter-rouge">config.yml</code> and <code class="language-plaintext highlighter-rouge">main.css</code>. So I thought I would
just fork the theme and customize it for my needs. It would be easy, right?</p>
<p>In theory that was probably right. But I noticed a few minor problems with the responsive design of the
theme. The blog cover images weren’t resizing nicely as they were in the demo site. I realized that this was
because Kippt had made improvements to their blog after open-sourcing the theme.</p>
<h2 id="improving-on-jekyll-incorporated-theme">Improving on Jekyll Incorporated Theme</h2>
<p>I figured I would fix the cover images and then start writing posts. But that one fix revealed other
issues in the code. One change led to another. I ended up making 40 commits in all. To make this harder
on myself, I didn’t just tear into the code. I made all the changes in a way that could be merged back
upstream, preserving the design and original intent of the theme.</p>
<p>None of this is to take away from the original work Kippt, Inc put into the theme. They created an awesome
foundation for me to build on. There’s no way I could have done this on my own.</p>
<h3 id="remove-bootstrapcss">Remove Bootstrap.css</h3>
<p>I noticed that the theme included <code class="language-plaintext highlighter-rouge">bootstrap.scss</code>, containing all of Twitter Bootstrap. But none of the
Bootstrap features like the grid, forms, or typography were being used. <code class="language-plaintext highlighter-rouge">bootstrap.scss</code> was only
providing a CSS reset (a cross-browser foundation to build the rest of the css on top of) and a few scattered,
random styles.</p>
<p>To fix this, I pulled in my favorite css reset: <a href="http://necolas.github.io/normalize.css/">normalize.css</a>. I
then methodically went through bootstrap.scss, commenting out chunks, and looking for rules that were
still being used by comparing the before and after pages in two browser tabs.</p>
<p>When I was done, I removed <code class="language-plaintext highlighter-rouge">bootstrap.scss</code> and moved the rules that were still being using into into <code class="language-plaintext highlighter-rouge">base.scss</code>.
Below are the rules I needed to save</p>
<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="o">//</span> <span class="nt">Use</span> <span class="nt">border-box</span> <span class="nt">sizing</span> <span class="nt">for</span> <span class="nt">everything</span><span class="o">.</span>
<span class="o">*,</span>
<span class="o">*</span><span class="nd">:before</span><span class="o">,</span>
<span class="o">*</span><span class="nd">:after</span> <span class="p">{</span>
<span class="nl">-webkit-box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span>
<span class="nl">-moz-box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span>
<span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="err">...</span> <span class="err">(other</span> <span class="err">stuff</span> <span class="err">that</span> <span class="err">was</span> <span class="err">already</span> <span class="err">in</span> <span class="err">base.scss)</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">1.428571429</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">img</span> <span class="p">{</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="m">0</span> <span class="m">10px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span><span class="o">,</span> <span class="nt">h3</span><span class="o">,</span> <span class="nt">h4</span><span class="o">,</span> <span class="nt">h5</span> <span class="p">{</span>
<span class="err">...</span> <span class="err">(other</span> <span class="err">stuff</span> <span class="err">that</span> <span class="err">was</span> <span class="err">already</span> <span class="err">in</span> <span class="err">base.scss)</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">1.1</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>The net change was 1647 lines of css removed. Here’s <a href="https://github.com/willkoehler/my_blog/commit/460ca42a011c075d0fc2d63f86cc6940dac3d4ca#diff-1003e390ef395b0f33d4f1a90fb9e6c6R42">the commit</a></p>
<h3 id="improve-source-code-highlighting">Improve source-code highlighting</h3>
<p>Out of the box, Jekyll supports source-code highlighting using <a href="http://pygments.org">pygments</a>.
You don’t need to know anything about pygments. Wrap your source code in <code class="language-plaintext highlighter-rouge">highlight</code> tags and
it “just works”.</p>
<figure class="highlight"><pre><code class="language-liquid" data-lang="liquid"><span class="p">{%</span><span class="w"> </span><span class="nt">highlight</span><span class="w"> </span>ruby<span class="w"> </span>linenos<span class="w"> </span><span class="p">%}</span>
class User < ActiveRecord::Base
# Wow this Ruby source code highlighting looks great!
has_many :user_assignments, :dependent => :destroy
has_many :hospitals, :through => :user_assignments
end
<span class="p">{%</span><span class="w"> </span><span class="nt">endhighlight</span><span class="w"> </span><span class="p">%}</span></code></pre></figure>
<p>Is rendered as</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="c1"># Wow this Ruby source code highlighting looks great!</span>
<span class="n">has_many</span> <span class="ss">:user_assignments</span><span class="p">,</span> <span class="ss">:dependent</span> <span class="o">=></span> <span class="ss">:destroy</span>
<span class="n">has_many</span> <span class="ss">:hospitals</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:user_assignments</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The theme already included Github’s syntax highlighting stylesheet:
<a href="http://jekyllrb.com/docs/templates/#stylesheets-for-syntax-highlighting">syntax.css</a>.
I added a few rules to clean up the look and prevent long lines from wrapping.</p>
<p>Before improvements</p>
<div class="before_syntax_fixes">
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:user_assignments</span><span class="p">,</span> <span class="ss">:dependent</span> <span class="o">=></span> <span class="ss">:destroy</span>
<span class="n">has_many</span> <span class="ss">:hospitals</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:user_assignments</span>
<span class="c1"># Ugly, but 5x faster than op2 and op3</span>
<span class="c1"># Takes 2ms</span>
<span class="n">scope</span> <span class="ss">:grid_fields</span><span class="p">,</span> <span class="o">-></span> <span class="p">{</span> <span class="n">joins</span><span class="p">(</span><span class="s1">'LEFT JOIN user_assignments ON user_id = users.id LEFT JOIN hospitals ON hospitals.id = hospital_id'</span><span class="p">).</span>
<span class="nf">group</span><span class="p">(</span><span class="s1">'users.id'</span><span class="p">).</span><span class="nf">select</span><span class="p">([</span><span class="ss">:id</span><span class="p">,</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:role</span><span class="p">,</span> <span class="s1">'min(hospitals.name) as first_hospital_name'</span><span class="p">])</span> <span class="p">}</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
</div>
<p>After improvements</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:user_assignments</span><span class="p">,</span> <span class="ss">:dependent</span> <span class="o">=></span> <span class="ss">:destroy</span>
<span class="n">has_many</span> <span class="ss">:hospitals</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:user_assignments</span>
<span class="c1"># Ugly, but 5x faster than op2 and op3</span>
<span class="c1"># Takes 2ms</span>
<span class="n">scope</span> <span class="ss">:grid_fields</span><span class="p">,</span> <span class="o">-></span> <span class="p">{</span> <span class="n">joins</span><span class="p">(</span><span class="s1">'LEFT JOIN user_assignments ON user_id = users.id LEFT JOIN hospitals ON hospitals.id = hospital_id'</span><span class="p">).</span>
<span class="nf">group</span><span class="p">(</span><span class="s1">'users.id'</span><span class="p">).</span><span class="nf">select</span><span class="p">([</span><span class="ss">:id</span><span class="p">,</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:role</span><span class="p">,</span> <span class="s1">'min(hospitals.name) as first_hospital_name'</span><span class="p">])</span> <span class="p">}</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Thanks <a href="http://demisx.github.io/jekyll/2014/01/13/improve-code-highlighting-in-jekyll.html">Dmitri Moore</a>
for the ideas. Here’s <a href="https://github.com/willkoehler/my_blog/commit/198b31ea605fdc959ba23114f9292bddc939b9a7">the commit</a></p>
<h3 id="clean-up-configyml">Clean up config.yml</h3>
<p>My philosophy on code and config is: the less the better. I went through config.yml line by line, referencing
the Jekyll documentation and Googling information as needed. Almost everything could go. Most of the config
items were simply re-stating Jekyll’s defaults. I also removed references to alternative markdown parsers,
such as rdiscount, maruku, and redcarpet. Jekyll defaults to Kramdown for parsing markdown. Unless you’re
doing something really specialized, there’s no need to deviate from this default.</p>
<p>The net change was 56 lines of config removed (the remaining config was almost all theme-specific)</p>
<p>Here’s <a href="https://github.com/willkoehler/my_blog/commit/f7ea94058a797b969724dd35bb0a75ce829fcc7e">the commit</a></p>
<h3 id="fix-feedxml">Fix feed.xml</h3>
<p>I don’t know much about RSS. I don’t even know if anyone will use it. But since RSS was already in
the theme, I decided to keep it and fix it. Most of the fixes were obvious. I used the
<a href="http://validator.w3.org/feed/">W3C Feed Validation Service</a> to check the feed and Googled the errors
I couldn’t figure out on my own.</p>
<p>Here’s <a href="https://github.com/willkoehler/my_blog/commit/1aa854cea9e856d7c07c58c3e0be5dbfe51b939a">the commit</a>.
Update: I’ve made <a href="https://github.com/willkoehler/my_blog/commit/2d8cfb7629cde63dc31a8bbbb11d9e5e62b90d18">more changes</a>.</p>
<h3 id="add-github-icon">Add Github icon</h3>
<p>I wanted to have a link to my Github profile in the social icons. The hard work was already done for me
by <a href="https://github.com/kippt/jekyll-incorporated/pull/17">@vdragsic</a>. I tweaked the icon size to make
it easier to see. This led to some weird rendering behavior and strange css that had to be cleaned up.</p>
<p>Before tweaks. Notice Github icon is small and not immediately recognizable. All social icons are too low.</p>
<p><img src="http://willkoehler.net/images/social-icons-before-tweaks.png" alt="Social icons before tweaks" style="width:239px;" /></p>
<p>After tweaks.</p>
<p><img src="http://willkoehler.net/images/social-icons-after-tweaks.png" alt="Social icons after tweaks" style="width:239px;" /></p>
<p>I broke this change into two commits: <a href="https://github.com/willkoehler/my_blog/commit/f461bd5dec11bfe12c6fbb6cd79eaf12af690219">Add Github icon</a>
and <a href="https://github.com/willkoehler/my_blog/commit/9604230187353995e81db3fda21e289c63661023">Tweak styling</a>.</p>
<h3 id="remove-unused-css-rules">Remove unused css rules</h3>
<p>I went through and cleaned up all the unused css rules. Unused rules add to page weight and, more importantly,
add to the cognitive load reading the css.</p>
<p>Tracking down the unused rules wasn’t hard. Since I wanted a better understanding of the css,
I did it by hand. I searched for each rule in the css, deleted the ones that weren’t used in the HTML,
making sure I didn’t break anything along the way.</p>
<p>These changes are in two commits:
<a href="https://github.com/willkoehler/my_blog/commit/00ff93d79cffd6b8291de335b8a850cf65bbcd3e">Commit 1</a> and
<a href="https://github.com/willkoehler/my_blog/commit/86e17a527661910e6a52924df9c620bd1b2a3b7b">Commit 2</a></p>
<h3 id="much-more">Much more…</h3>
<p>There are a lot more fixes and refinements. You can see all the detail in the <a href="https://github.com/willkoehler/my_blog/commits/open_source">open_source branch</a>.
Feel free to use this as a starting point for your blog. Maybe the changes will be merged back into
Jekyll Incorporated. I submitted a <a href="https://github.com/kippt/jekyll-incorporated/pull/24">pull request</a>. But it’s a lot of
changes to merge back upstream.</p>
<h2 id="development-continues">Development continues</h2>
<p>In the meantime, I’ve moved ahead with my blog and have started making non-compatible changes to customize
the theme for my needs. These include more css fixes and design refinements. If it’s something that looks useful,
feel free to fork the <a href="https://github.com/willkoehler/my_blog">master branch</a> to get the latest.</p>
<h2 id="additional-resources">Additional resources</h2>
<p>Mike Greiling has some great articles on Jekyll on his blog (PixelCog).</p>
<p><a href="http://pixelcog.com/blog/2013/jekyll-from-scratch-introduction/">Jekyll From Scratch - Getting Started</a><br />
<a href="http://pixelcog.com/blog/2013/jekyll-from-scratch-core-architecture/">Jekyll From Scratch - Core Architecture</a><br />
<a href="http://pixelcog.com/blog/2013/jekyll-from-scratch-extending-jekyll/">Jekyll From Scratch - Extending Jekyll</a></p>
<!-- styles to demo source-code highlighting before fixes -->
<style>
.before_syntax_fixes .highlight table td.gutter {
padding: 0;
color: #fff;
border-right: none;
}
.before_syntax_fixes .highlight table td.code {
padding: 0;
}
.before_syntax_fixes .highlight table pre {
margin: 1em 0;
font-family: monospace, monospace;
}
</style>