tag:blogger.com,1999:blog-24537442929665110272024-03-13T12:09:15.629-07:00Cow Jumped over the Commodore64Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-2453744292966511027.post-57905066456127123362013-12-10T15:47:00.001-08:002013-12-10T15:49:20.865-08:00Optimize Your Rails Fragment Cache By Lazily Loading your ObjectsI was looking into the performance of our my portfolio page the other day which was supposedly cached. Here's what I noticed within the controller.<br />
<br />
<br />
<script src="https://gist.github.com/kbaum/7902146.js"></script><br />
<br />
<br />
And here was our view:<br />
<br />
<script src="https://gist.github.com/kbaum/7902189.js"></script><br />
<br />
<p>This being a page that show properties and spaces, we are attempting to cache the entire page based on some keys that get updated whenever any of the spaces or properties within the user's portfolio is updated. But here's the problem, our queries are doing some eager loading from the database. Even though we were fragment caching the entire page, each cached hit was still hitting the database, creating a bunch of active record objects and then never actually using them. Even on a cached hit, this is what I saw in the logs:<br />
</p><br />
<script src="https://gist.github.com/kbaum/7902365.js"></script><br />
<br />
<p>To fix this, we moved all of the query logic out of the controller show method and into a our query object where it is safely memoized</p><br />
<br />
<script src="https://gist.github.com/kbaum/7902401.js"></script><br />
<br />
<p>Now instead of accessing instance variables created within the controller show method, our view accesses helper methods lazily from within the cache fragment.<br />
</p><br />
<script src="https://gist.github.com/kbaum/7902465.js"></script><br />
<br />
<p>Our cached my portfolio page no longer needlessly loads dozens of active record objects.</p><br />
<br />
<br />
<br />
<br />
<br />
<br />
Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com0tag:blogger.com,1999:blog-2453744292966511027.post-21512315869810137042013-09-17T11:54:00.003-07:002013-09-17T12:06:38.095-07:00Testing SSL under capybaraIf your website runs under ssl than your integration tests should too. This post outlines how to set up testing over https with capybara 2.1 and a few popular capybara drivers. The majority of this info was borrowed from <a href="https://github.com/jnicklas/capybara/issues/1121" target="_blank">this capybara github issue</a>.<br />
<br />
In order to run your capybara test server under ssl, you will need to <a href="https://devcenter.heroku.com/articles/ssl-certificate-self" target="_blank">generate a self signed certificate</a>.<br />
You'll want to check the certificate and private key into your project. I added them to my spec/support directory.<br />
<br />
Now that the certificate is all set, start capybara with ssl enabled using your certificates. Hint: If you are using rspec, the following snippets can be pasted into your spec_helper.rb.<br />
<br />
<script src="https://gist.github.com/kbaum/6598770.js"></script><br />
<br />
Next tell capybara, to make requests using https:<br />
<br />
<script src="https://gist.github.com/kbaum/6598892.js"></script><br />
Now you'll need to instruct your web driver to automatically accepted your self signed certificate even though you are running from localhost.<br />
<br />
<script src="https://gist.github.com/kbaum/6598919.js"></script><br />
<br />
Finally, add the following monkey patch capybara to make http with ssl enabled:<br />
<br />
<script src="https://gist.github.com/kbaum/6598936.js"></script><br />
<br />
I am not proud of the last step, and <a href="https://github.com/jnicklas" target="_blank">jnicklas</a> indicated in this<a href="https://github.com/jnicklas/capybara/issues/1121#issuecomment-21196476" target="_blank"> closed issue</a> that this should not be needed, but not sure how else to get it done. Plan on following up with him and updating the post.<br />
<br />
<br />
<br />Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com5tag:blogger.com,1999:blog-2453744292966511027.post-1718911996047450822013-07-09T08:25:00.000-07:002013-10-30T14:30:03.094-07:00Scaling Heavy Web Requests with Sidekiq and Pusher<h3>Long Running Web Requests</h3><br />
At <a href="http://www.viewthespace.com/jobs?utm_source=cowjumpedovercommodore64&utm_medium=link&utm_campaign=sidekiq_pusher" target="_blank">View The Space</a>, many of our pages deliver real time data over extended time periods. The database queries producing this data can take significant time to run and since the reporting is meant to be real time, caching is not an option. Our application servers consist of a bunch of single threaded unicorn processes sitting on top of Heroku. Tying up our web requests with long running heavy queries would be a bad idea as all other web requests would queue up causing bad performance for our entire app. <br />
<br />
<h3>Push it in the Background with Sidekiq</h3><br />
Instead of performing long queries during the course of the rails web request, we push that heavy lifting to the background with <a href="https://github.com/mperham/sidekiq" target="_blank">Sidekiq</a>. Sidekiq is a multi-threaded background job queueing library built on top of redis and by default each Sidekiq process leverages 25 threads. When a request is made to our building traffic dashboard, a simple html page is quickly rendered to the user followed by an ajax json request. That second ajax json request simply pushes a critical job onto a sidekiq queue:<br />
<br />
<script src="https://gist.github.com/kbaum/5957449.js"></script><br />
That first job pushes another critical query job for each query to be performed for the dashboard. Since sidekiq is multi-threaded, each query should be performed in parallel.<br />
<br />
<script src="https://gist.github.com/kbaum/199bec43e918bc9848b4.js"></script><br />
<br />
Once the query is complete, the job pushes the result to the browser using <a href="http://pusher.com/websockets" target="_blank">Pusher</a> and the <a href="https://github.com/pusher/pusher-gem" target="_blank">pusher-gem</a>. As a side note, Pusher is a service that makes using websockets easy and it supports all major browsers including Internet Explorer. <br />
<br />
Pusher publishes the result to a unique user channel based on the user's database id and rails session id. The query result should be some simple raw data within a hash or an array that can easily be converted into json.<br />
<br />
<script src="https://gist.github.com/kbaum/3b71699e22ff375ca721.js"></script><br />
<br />
<h3>Rendering the Data within the Browser</h3><br />
Now that the data has been pushed from the server side sidekiq job, we need some client side pusher javascript code listening for the results from the browser. The following coffee script initializes pusher with the unique channel we published to on the server side and listens for each event. Once we know the pusher subscription has succeeded, we submit the ajax form which sets the whole sidekiq to pusher process into motion.<br />
<br />
<script src="https://gist.github.com/kbaum/5958155.js"></script><br />
<br />
<h3>Summary</h3><div>Heavy lifting such as real time data reporting is not something you want to execute during your rails web requests. Instead perform that expensive data crunching in the background with Sidekiq and push the results to your users' browsers with Pusher.</div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com2tag:blogger.com,1999:blog-2453744292966511027.post-84073520070356499372013-03-14T11:32:00.001-07:002013-03-14T14:03:48.991-07:00What's Missing from Heroku Add-onsToday I got burned by my redis service for the last time. I am not going to name names, but they are listed within the <a href="https://addons.heroku.com/">heroku add-ons</a>. Everybody needs a redis instance so I had to pick a new service to replace my existing, but there are currently 5 redis providers listed as add-ons. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-K4fwH9rOrrE/UUI6lREoieI/AAAAAAAAmxU/ur59uzSTfh4/s1600/Screen+Shot+2013-03-14+at+4.59.36+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="295" src="http://2.bp.blogspot.com/-K4fwH9rOrrE/UUI6lREoieI/AAAAAAAAmxU/ur59uzSTfh4/s320/Screen+Shot+2013-03-14+at+4.59.36+PM.png" width="320" /></a></div>
<br />
<br />
I had no way of deciding which one to pick. Yeah yeah, i could have done some research, but my existing redis instance was just flat out not working and parts of our app was down. I was in a hurry. In the end, like the seasoned engineer I am, I picked the one with the prettiest logo. My point is, why doesn't Heroku have some type of transparency within their add ons section? It should look like the itunes store. When choosing between heroku add-ons, I should be able to see how many other projects are using the add-on and some reviews would also be nice. Can anyone think of a reason why it doesn't work this way today?Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com5tag:blogger.com,1999:blog-2453744292966511027.post-44170518493950082692013-03-12T15:33:00.002-07:002013-03-14T15:23:09.845-07:00Setting the Rails Timezone to the User's Browser TimezoneTimezones confuse me.<br />
<br />
At VTS we have lots of reports and charts that group data by the day. These reports should look different based on the user's configured timezone. I did not want to force all of our users to configure their timezone within our app when they already configured it within their operating system and hence their browser. The only problem... the browser does not pass the timezone to the server by default. You have to take care of it yourself.<br />
<br />
The solution is pretty straight forward:<br />
<ol>
<li>Set each users timezone as a cookie</li>
<li>Each request sets the Rails timezone to the value of that cookie</li>
</ol>
<br />
<script src="https://gist.github.com/kbaum/5147609.js"></script><br />
The above coffee script code uses <a href="https://bitbucket.org/pellepim/jstimezonedetect">jsTimezoneDetect</a> to set the browsers timezone within a cookie that will live for the next 365 days. All that is left is setting the timezone during each request.<br />
<br />
<script src="https://gist.github.com/kbaum/5147653.js"></script><br />
The above code would go into your ApplicationController. Simple around filter for each request that sets the timezone if it's present. Notice that it sets the Time.zone back to the original default timezone after each request and, yes, the Time.zone method is actually thread safe.<br />
<br />
<h4>
Slight Caveat</h4>
The timezone is set after each page request on document ready. That means the very first request will not have the cookie set for the server side around_filter and will not set the timezone. I have seen some people out there getting fancy with ajax redirects but this is probably a bit overkill for most use cases. I start to have nightmares involving infinite redirects.<br />
<br />
Since I extracted all of the above into a Rails Engine, you can ignore all of my rambling and just install the <a href="https://github.com/kbaum/browser-timezone-rails">browser-timezone-rails</a> gem. <br />
<br />
<br />Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com1tag:blogger.com,1999:blog-2453744292966511027.post-3171358979995763992012-05-08T06:44:00.000-07:002012-05-08T11:15:18.116-07:00Your app ain't working and you don't even know it.<p>I am a nerd. This and my kids is what keeps me up at night. This post is about how I find a way to get to sleep without over-medicating.</p><h3>Airbrake</h3><p>This probably isn't that enlightening, but when an error happens in your app, you should probably know about it. <a href="http://airbrake.io/pages/home">Airbrake</a> takes care of this for you in a smart way.</p><h3>Still Alive</h3><p>There are plenty of services out there that ping your site but what does that prove? Is your site doing what it's supposed to be doing? <a href="https://stillalive.com/">Still Alive</a> is like Cucumber for your live app. Here's what we have running every minute:</p><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-ntNgih0wzgs/T6ka19n8gtI/AAAAAAAAmnM/lQXOH51uYeg/s1600/Screen+shot+2012-05-08+at+9.07.35+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="201" src="http://4.bp.blogspot.com/-ntNgih0wzgs/T6ka19n8gtI/AAAAAAAAmnM/lQXOH51uYeg/s320/Screen+shot+2012-05-08+at+9.07.35+AM.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"></div><br />
<p>Still not satisfied? This is what we have running every 15 minutes:</p><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-ZRP9YIDpLVo/T6kbTHNAj8I/AAAAAAAAmnU/h-9rtmMu_2k/s1600/Screen+shot+2012-05-08+at+9.10.02+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="http://2.bp.blogspot.com/-ZRP9YIDpLVo/T6kbTHNAj8I/AAAAAAAAmnU/h-9rtmMu_2k/s320/Screen+shot+2012-05-08+at+9.10.02+AM.png" width="320" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><br />
<p>Notice this is actually shares stuff which sends emails, but are we sure our emails are actually being delivered?</p><br />
<h3>Email Monitoring Job</h3><br />
<p>Every 15 minutes we run a resque job that checks our test email account to see if it's received an email in the last 30 minutes.</p><br />
<script src="https://gist.github.com/2634403.js">
</script> <br />
<p>If no recent email is found, it raises an error. That error gets picked up by airbrake since we have this within our resque initializer:</p><script src="https://gist.github.com/2634448.js?file=resque.rb">
</script> <br />
How did those emails get there you ask? Remember our Still Alive script sends email every 15 minutes during it's test run. <br />
<br />
<h3>I'm getting alerts, what's going on?</h3><br />
<p>I'm not going to pretend that 99% of you reading this post don't know about <a href="http://newrelic.com/">newrelic</a>, but this is our go to tool to diagnose any kind of performance related issues. It also pings your website every minute which is a nice addition to stillalive. After all, if two alerting systems are reporting errors, it's a real sign that there is definitely an issue.</p><br />
<h3>You have invalid ActiveRecord objects lurking in your database </h3><p>Yes I am trying to scare you into reading this, but let's face it, you probably do. There are a bunch of ways it can happen.</p><ul><li>That cowboy sitting next to you saved a bunch of objects with validation turned off</li>
<li>Code changes - new validations that haven't always been there</li>
<li>Faulty logic in your code that let's invalid stuff through</li>
</ul><p>So you be saying, is this a big deal? The answer in many cases is yes. Suppose you add some new code that validates a new presence of a new field on your user object but you forget to update all of your existing user profiles. If any page within your app updates a user object, your customers may be in for a 500 page.<br />
This is where <a href="https://github.com/versapay/active_sanity">active_sanity</a> come in which checks all of the objects in your database for validity. For each object that is invalid, it inserts an entry into an InvalidRecord table. Again, we have a resque job that runs active sanity once a night:</p><script src="https://gist.github.com/2634498.js">
</script> <br />
<p>You're probably noticing a pattern here. If there are any invalid records, an error is raised, which fires off an airbrake alert. This alert has a link to the <a href="https://github.com/sferik/rails_admin">rails_admin</a> page which shows a list of all invalid records:</p><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-2hfD5iLF8po/T6kXzPpYv7I/AAAAAAAAmnA/NMJ20GwEC7M/s1600/Screen+shot+2012-05-08+at+8.54.35+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="http://3.bp.blogspot.com/-2hfD5iLF8po/T6kXzPpYv7I/AAAAAAAAmnA/NMJ20GwEC7M/s320/Screen+shot+2012-05-08+at+8.54.35+AM.png" width="320" /></a></div><br />
<h3>Conclusion </h3><br />
<p>It's important for your sanity and general well being to be the first one to know that your app isn't functioning properly. At View the Space, we are constantly trying to think of new ways to improve upon our monitoring. Let us know what you do? Drop us a comment.</p>Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com0tag:blogger.com,1999:blog-2453744292966511027.post-81296918084541137602011-12-08T18:15:00.000-08:002011-12-12T05:24:14.561-08:00Cut down on those objects to speed up your cucumber testsChances are that a big reason your tests are so slow is all of those extra objects you are creating. Recently I cut down on the objects in my tests and I was able to speed up by cucumber runs by 30-40%. I achieved most of those gains by making some small changes to factory girl. I don't want to bore you with the detail of our object model but it goes something like this. Our main object is a space and our tests frequently create several of them. The problem is that a space has a reference to a user, property, an contacts. Each of those actually have references to other objects. Just by creating a few spaces we end up creating a web of objects but our tests didn't need all of these extra objects. Enter the default objects within our factory_girl definitions:<br /><br /><div class='hscroll'><br /><table class="CodeRay"><tbody><tr><br /> <td ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }" title="double click to toggle" class="line-numbers"><pre><a name="n1" href="#n1">1</a><br /><a name="n2" href="#n2">2</a><br /><a name="n3" href="#n3">3</a><br /><a name="n4" href="#n4">4</a><br /><a name="n5" href="#n5">5</a><br /><a name="n6" href="#n6">6</a><br /><a name="n7" href="#n7">7</a><br /><a name="n8" href="#n8">8</a><br /><a name="n9" href="#n9">9</a><br /><strong><a name="n10" href="#n10">10</a></strong><br /><a name="n11" href="#n11">11</a><br /><a name="n12" href="#n12">12</a><br /><a name="n13" href="#n13">13</a><br /><a name="n14" href="#n14">14</a><br /><a name="n15" href="#n15">15</a><br /><a name="n16" href="#n16">16</a><br /><a name="n17" href="#n17">17</a><br /><a name="n18" href="#n18">18</a><br /><a name="n19" href="#n19">19</a><br /><strong><a name="n20" href="#n20">20</a></strong><br /><a name="n21" href="#n21">21</a><br /><a name="n22" href="#n22">22</a><br /><a name="n23" href="#n23">23</a><br /><a name="n24" href="#n24">24</a><br /><a name="n25" href="#n25">25</a><br /><a name="n26" href="#n26">26</a><br /><a name="n27" href="#n27">27</a><br /><a name="n28" href="#n28">28</a><br /><a name="n29" href="#n29">29</a><br /></pre></td><br /> <td class="code"><pre><span class="keyword">def</span> <span class="function">default_user</span><br /> default_email = <span class="string"><span class="delimiter">'</span><span class="content">default@test.com</span><span class="delimiter">'</span></span><br /> <span class="constant">User</span>.find_by_email(default_email) || Factory(<span class="symbol">:user</span>, <span class="symbol">:email</span>=>default_email)<br /><span class="keyword">end</span><br /><br /><span class="keyword">def</span> <span class="function">default_property</span><br /> street_address = <span class="string"><span class="delimiter">'</span><span class="content">5555 5th Ave</span><span class="delimiter">'</span></span><br /> <span class="constant">Property</span>.find_by_street_address(street_address) || Factory(<span class="symbol">:property</span>, <span class="symbol">:street_address</span>=>street_address)<br /><span class="keyword">end</span><br /><br /><span class="keyword">def</span> <span class="function">default_space</span><br /> floor = <span class="string"><span class="delimiter">"</span><span class="content">99th-98th floor</span><span class="delimiter">"</span></span><br /> <span class="constant">Space</span>.find_by_floor(floor) || Factory(<span class="symbol">:space</span>, <span class="symbol">:floor</span>=>floor)<br /><span class="keyword">end</span><br /><br /><br />factory <span class="symbol">:space</span> <span class="keyword">do</span><br /> property { default_property }<br /> user { default_user }<br /> suite { <span class="string"><span class="delimiter">"</span><span class="content">#</span><span class="inline"><span class="inline-delimiter">#{</span>Forgery(<span class="symbol">:basic</span>).number.to_i<span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span> }<br /> floor { Forgery(<span class="symbol">:basic</span>).number.to_i }<br /> rental_rate { Forgery(<span class="symbol">:basic</span>).number * <span class="integer">100</span> }<br /> description { Forgery(<span class="symbol">:lorem_ipsum</span>).words(rand <span class="integer">100</span>) }<br /> space_available <span class="string"><span class="delimiter">"</span><span class="content">25,000 sq ft</span><span class="delimiter">"</span></span><br /> availability_type <span class="string"><span class="delimiter">"</span><span class="content">arranged</span><span class="delimiter">"</span></span><br /> status <span class="string"><span class="delimiter">'</span><span class="content">active</span><span class="delimiter">'</span></span><br /><span class="keyword">end</span><br /><br /><br /></pre></td><br /></tr></tbody></table><br /></div><br />Instead of creating new properties, users, and contacts each time a space is created, by default all spaces share the same nested objects. Most often this is not at all a problem in any of our tests. If we do require spaces do not share the same property, we specify it the Given section of our test.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com3tag:blogger.com,1999:blog-2453744292966511027.post-44503490745551512212011-12-06T12:41:00.000-08:002011-12-12T06:36:57.367-08:00Mostly using capybara-webkit in cucumberBeen making a big effort of late speeding up our cucumber tests. We saw significant speed gains using capybara-webkit instead of selenium webdriver, but the problem is that capybara-webkit doesn't always work so well on some of our @javascript cucumber scenarios. In order to work around this problem for now, we default to the webkit driver for all of our @javascript scenarios and fallback to selenium in some cases by tagging @selenium. <br /><br /><table class="CodeRay"><tbody><tr><br /> <td ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }" title="double click to toggle" class="line-numbers"><pre><a name="n1" href="#n1">1</a><br /><a name="n2" href="#n2">2</a><br /><a name="n3" href="#n3">3</a><br /><a name="n4" href="#n4">4</a><br /><a name="n5" href="#n5">5</a><br /><a name="n6" href="#n6">6</a><br /><a name="n7" href="#n7">7</a><br /><a name="n8" href="#n8">8</a><br /><a name="n9" href="#n9">9</a><br /><strong><a name="n10" href="#n10">10</a></strong><br /><a name="n11" href="#n11">11</a><br /><a name="n12" href="#n12">12</a><br /><a name="n13" href="#n13">13</a><br /><a name="n14" href="#n14">14</a><br /><a name="n15" href="#n15">15</a><br /><a name="n16" href="#n16">16</a><br /><a name="n17" href="#n17">17</a><br /><a name="n18" href="#n18">18</a><br /><a name="n19" href="#n19">19</a><br /><strong><a name="n20" href="#n20">20</a></strong><br /><a name="n21" href="#n21">21</a><br /><a name="n22" href="#n22">22</a><br /><a name="n23" href="#n23">23</a><br /></pre></td><br /> <td class="code"><pre><span class="string">@javascript @selenium<br /></span> <span class="keyword">Scenario:</span> I should be able to remove spaces from my spaces page<br /> <span class="keyword">Given </span>I am a logged-in broker<br /> <span class="keyword">And </span>I have 2 spaces<br /> <span class="keyword">And </span>I am on my spaces page<br /> <span class="keyword">And </span>I click remove for first space<br /> <span class="keyword">And </span>I confirm popup<br /> <span class="keyword">Then </span>I should see remove successfull message<br /> <span class="keyword">And </span>The space should be removed from my spaces<br /> <span class="keyword">And </span>The space should no longer be visible on my spaces page<br /><br /><span class="string"> @javascript<br /></span> <span class="keyword">Scenario:</span> Share a space<br /> <span class="keyword">Given </span>I am a broker<br /> <span class="keyword">And </span>a space exists<br /> <span class="keyword">And </span>I am logged in<br /> <span class="keyword">And </span>I am on the space page<br /> <span class="keyword">And </span>I follow "Share"<br /> <span class="keyword">And </span>I fill in "Email Addresses" with "myfriend@test.com"<br /> <span class="keyword">And </span>I press "Send"<br /> <span class="keyword">And </span>a shared space should exist<br /><br /><br /></pre></td><br /></tr></tbody></table><br /><br />In order to get this working, we defined @selenium tag hooks within the features/support/custom.rb (could be any file in the support directory).<br /><br /><table class="CodeRay"><tbody><tr><br /> <td ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }" title="double click to toggle" class="line-numbers"><pre><a name="n1" href="#n1">1</a><br /><a name="n2" href="#n2">2</a><br /><a name="n3" href="#n3">3</a><br /><a name="n4" href="#n4">4</a><br /><a name="n5" href="#n5">5</a><br /><a name="n6" href="#n6">6</a><br /><a name="n7" href="#n7">7</a><br /><a name="n8" href="#n8">8</a><br /><a name="n9" href="#n9">9</a><br /><strong><a name="n10" href="#n10">10</a></strong><br /><a name="n11" href="#n11">11</a><br /><a name="n12" href="#n12">12</a><br /><a name="n13" href="#n13">13</a><br /></pre></td><br /> <td class="code"><pre> <span class="keyword">def</span> <span class="function">default_javascript_driver</span><br /> <span class="predefined-constant">ENV</span>[<span class="string"><span class="delimiter">'</span><span class="content">WEB_TEST_JS_DRIVER</span><span class="delimiter">'</span></span>].try(<span class="symbol">:to_sym</span>) || <span class="symbol">:webkit</span><br /> <span class="keyword">end</span><br /><br /> <span class="constant">Capybara</span>.javascript_driver = default_javascript_driver<br /><br /> Before(<span class="string"><span class="delimiter">'</span><span class="content">@selenium</span><span class="delimiter">'</span></span>) <span class="keyword">do</span><br /> <span class="constant">Capybara</span>.javascript_driver = <span class="symbol">:selenium</span><br /> <span class="keyword">end</span><br /><br /> After(<span class="string"><span class="delimiter">'</span><span class="content">@selenium</span><span class="delimiter">'</span></span>) <span class="keyword">do</span><br /> <span class="constant">Capybara</span>.javascript_driver = default_javascript_driver<br /> <span class="keyword">end</span><br /></pre></td><br /></tr></tbody></table><br /><br />Before each @selenium scenario, change to selenium. After the scenario, we change back to the previous driver.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com1tag:blogger.com,1999:blog-2453744292966511027.post-91739157302627830172011-12-05T13:30:00.000-08:002011-12-05T13:55:12.121-08:00ruby-prof within cucumberWe've all been there... slow cucumber tests. It's tough to know where to begin. Recently, I added the ability to enable ruby-prof for a cucumber run. I added the following file to our features/support directory:<br /><br /><script src="https://gist.github.com/1435547.js?file=ruby_prof_cucumber.rb"></script><br /><br />Then to run cucumber with ruby prof enabled:<br /><br /> RUBY_PROF=true cucumber features/tourbooks.feature<br /><br />After cucumber finishes, profile-flat.txt and profile-graph.html should exist in the rails root tmp directory.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com2tag:blogger.com,1999:blog-2453744292966511027.post-12800623726732533892011-12-01T15:36:00.000-08:002011-12-01T19:15:43.439-08:00Running your still alive recipe as part of your buildFor those of you not familiar with <a href="http://stillallive.com">StillAlive</a>, it's an awesome platform that monitors your web site using custom Gherkin recipes. Those comfortable with cucumber will be right at home with StillAlive as the recipes give you almost exactly what comes with the controversial web_steps.rb. The end result is that instead of simply pinging a page and getting back success or failure, StillAlive allows you to actually exercise the full stack of your application up to once a minute. <br /><br />We started using StillAllive last month. One limitation with StillAlive and other intelligent monitoring solutions is that as your application changes with each deployment, your StillAlive monitoring recipe can break becoming incompatible with the latest version of your app. Our solution to this issue is including our StillAlive recipes as part of our cucumber suite. In this case our still_alive.feature file looks like:<br /><br /><script src="https://gist.github.com/1421531.js?file=still_alive.feature"></script><br /><br />Since we are being naughty and still using web_steps.rb, we just needed to add the following to our paths.rb which converts the absolute url http://www.ourapp.com/users/sign_in to the relative /users/sign_in for testing within cucumber.<br /><br /><script src="https://gist.github.com/1421531.js?file=path.rb"></script><br /><br />Once that test worked within our cucumber suite, we copied everything but the Background to our StillAlive recipe. <br /><br />Now if something we change in development is incompatible with our StillAlive recipe, we'll find out about it before we release to production.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com0tag:blogger.com,1999:blog-2453744292966511027.post-39620924271223786412011-11-30T19:24:00.000-08:002011-11-30T20:13:10.017-08:00How not to delete your heroku production environment and all of your database backups in one commandSo you're never going to guess what I did today. Ok, I'll give you a hint. It has something to do with the title of this post.<br /><br />Wait.. before you go judging me, I want to explain how I deleted my entire heroku production environment and what I learned as a result of this slight mishap.<br /><br />My intentions were good. I already had a production environment called myapp and I wanted to create an additional staging environment. I harnessed all of my creativity and came up with myapp-staging. To create the new environment, I executed:<br /><br />heroku create myapp-staging<br /><br />Whoops, that creates a bamboo app and I need cedar to match my production environment. No problem, I'll just delete that and create a new one.<br /><br />heroku apps:destroy myapp-staging<br /><br />! WARNING: Potentially Destructive Action<br />! This command will affect the app: myapp<br />! To proceed, type "myapp" or re-run this command with --confirm myapp<br />> myapp<br />Destroying myapp (including all add-ons)... done<br /><br />Wait.. what did I just do? Ctrl-c... too late. My app == gone. I forgot the "--app" and heroku just ignored myapp-staging. Being the cowboy that I am, I just entered "myapp" without thinking.<br /><br />Ok, on one hand.. that was really dumb. Heroku gave me an out with the confirm, but I still entered "myapp". On the other hand, my heroku app:destroy command was completely invalid and instead of throwing an error, the heroku client just silently went on. Probably less than ideal.<br /><br />Either way, since we weren't yet officially launched and we did not lose any valuable data , making that mistake helped me to realize something extremely important. Heroku database backups go bye-bye when you delete your app. In hindsight, this seems obvious since heroku:pgbackups is just an add on and all add ons are deleted, but it's a little scary how in just one command, your entire app including your data and all backups are gone. Seems like that backup strategy isn't exactly full proof. At that moment, I decided it would be a good idea, to have regular dumps of our database exported out of heroku to Amazon S3.<br /><br />I looked around and there were a few libraries and examples of people uploading their database dumps to S3, but I decided to go with the <a href="https://github.com/kamui/heroku_cloud_backup">heroku_cloud_backups</a> gem. I followed all of the instructions on the github readme and the whole process was extremely seamless. The only slight difference is that heroku now wants people to use the scheduler add-on instead of cron.<br /><br />So lesson learned. If you are only using heroku <a href="http://devcenter.heroku.com/articles/pgbackups">pgbackups</a>, you are one idiot away from losing all of your data.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com1tag:blogger.com,1999:blog-2453744292966511027.post-15603556947834060282011-11-29T07:55:00.000-08:002011-11-29T08:21:02.067-08:00Relative email links with Saucelabs and capybara/cucumberMy cucumber/capybara tests use pickle to click links within emails but I came across a slight issue when integrating with saucelabs. Saucelabs proxies requests through a different host than what our rails app uses. Our rails emails get the URL host from:<br /><br /><script src="https://gist.github.com/1405348.js?file=action_mailer_default_url_options.rb"></script><br /><br />This is most likely set to localhost:5000 or something like that. Saucelabs on the other hand seems to use a random host. The end result is that when we click the first link within our email, the test fails with a DNS error as the saucelabs selenium server tries to hit localhost:5000 which does not exist at saucelabs. Emailed the folks at saucelabs and they recommended converting links within the email from absolute to relative. Got it done by monkey patching pickle:<br /><br /><script src="https://gist.github.com/1405348.js?file=pickle_email_links_relative.rb"></script><br /><br />The absolute_link_to_relative method just slices off the hostname in the url using the <a href="http://ruby-doc.org/core-1.9.2/String.html#method-i-slice">String#slice</a> method. The first argument is a regular expression built with the ActionMailer host configuration and the second is the capture number.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com0tag:blogger.com,1999:blog-2453744292966511027.post-82573181327496559012011-09-13T11:54:00.000-07:002011-09-13T11:56:23.699-07:00ScrollTo the first rails client_side_validation with JQueryI was using the amazing <a href="https://github.com/bcardarella/client_side_validations">client_side_validations</a> gem with an extremely large form. Everything was working perfectly but since this was a large form, the inline validation errors were not on the screen when the user hit submit. As a result, it just appeared as though the form was not working. To fix this, we used the <a href="http://plugins.jquery.com/project/ScrollTo">jquery ScrollTo plugin</a> and <a href="https://github.com/bcardarella/client_side_validations/wiki/Callbacks">client_side_validations callbacks</a> to scroll to the first validation error:<br /><br /><br /><script src="https://gist.github.com/1214686.js?file=scroll_to_first_error.js"></script>Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com2tag:blogger.com,1999:blog-2453744292966511027.post-24102206057582494252011-08-02T06:37:00.000-07:002011-08-02T10:11:04.800-07:00Resque-Cleaner - Too Good to be just Another PluginResque without <a href="https://github.com/ono/resque-cleaner">resque-cleaner</a> is like peas without carrots, peanut butter without jelly, french fries without ketchup, batman without robin.... it just doesn't make sense. Let me explain.<br /><span style="font-size:130%;"><br />Not All Jobs are Created Equal</span><br /><br /><span style="font-size:100%;">In the real world jobs fail all the time and, depending on which job it is, this could be an emergency or something we'll take a look at when there is time (never). The problem with Resque is that it treats all failures the same. All failures end up on one sometimes enormous queue whether the job is of type EndOfTheWorldIfThisDoesntRunJob or ThisProbablyShouldRunButWhoReallyCaresJob. Let's take a look at a typical resque-web console:</span><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-1Fv3uuIdjpE/TjglvayTcoI/AAAAAAAAl8w/MXzA8SPMQWo/s1600/Screen%2Bshot%2B2011-08-02%2Bat%2B12.25.35%2BPM.png"><img style="cursor: pointer; width: 400px; height: 84px;" src="http://3.bp.blogspot.com/-1Fv3uuIdjpE/TjglvayTcoI/AAAAAAAAl8w/MXzA8SPMQWo/s400/Screen%2Bshot%2B2011-08-02%2Bat%2B12.25.35%2BPM.png" alt="" id="BLOGGER_PHOTO_ID_5636296430228566658" border="0" /></a><br /><br />Ok, deep breath... we have some failures here. Let's take a look at what these are. Just click on the failed link:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-Nx0i6gwqd5A/TjgmVZdl5uI/AAAAAAAAl84/2Y1gB-10A0E/s1600/Screen%2Bshot%2B2011-08-02%2Bat%2B12.30.31%2BPM.png"><img style="cursor: pointer; width: 400px; height: 168px;" src="http://1.bp.blogspot.com/-Nx0i6gwqd5A/TjgmVZdl5uI/AAAAAAAAl84/2Y1gB-10A0E/s400/Screen%2Bshot%2B2011-08-02%2Bat%2B12.30.31%2BPM.png" alt="" id="BLOGGER_PHOTO_ID_5636297082708289250" border="0" /></a><br /><br />Looking at the first few few pages of failures, we make an educated guess that it's just the ThisProbablyShouldRunButWhoReallyCaresJob. We could just hope for the best and assume there's nothing important on that failure queue but how can we be sure that an EndOfTheWorldIfThisDoesntRunJob isn't sandwiched in there somewhere? Ok, so what options do we have:<br /><ol><li>Manually page through each failure results page 20 at a time.</li><li>Write a custom script that counts the number of failures by job type.</li><li>Pretend this problem doesn't exist and go back to what we were doing before.</li></ol>Option 2 is probably our best bet but it still seems like our job queue library should not leave us with writing a custom script just to figure out what is on our failure queue. This is where resque-cleaner comes in.<br /><br />Once installed, resque-cleaner adds a cleaner link to the header of resque-web:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-CX-3OqTuOF8/Tjgq4-s8z6I/AAAAAAAAl9A/WChdqB_6sxI/s1600/Screen%2Bshot%2B2011-08-02%2Bat%2B12.49.59%2BPM.png"><img style="cursor: pointer; width: 400px; height: 73px;" src="http://2.bp.blogspot.com/-CX-3OqTuOF8/Tjgq4-s8z6I/AAAAAAAAl9A/WChdqB_6sxI/s400/Screen%2Bshot%2B2011-08-02%2Bat%2B12.49.59%2BPM.png" alt="" id="BLOGGER_PHOTO_ID_5636302092046749602" border="0" /></a><br /><br />When we follow the "Cleaner" link, we can see all of our failed jobs broken down by type. Not a moment too soon either.. it looks like there's an EndOfTheWorldIfThisDoesntRunJob sitting on a failure queue.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-izrl4i3xfmQ/TjgrbTwL2TI/AAAAAAAAl9Q/tlrCqUI6P_Q/s1600/Screen%2Bshot%2B2011-08-02%2Bat%2B12.51.10%2BPM.png"><img style="cursor: pointer; width: 400px; height: 85px;" src="http://3.bp.blogspot.com/-izrl4i3xfmQ/TjgrbTwL2TI/AAAAAAAAl9Q/tlrCqUI6P_Q/s400/Screen%2Bshot%2B2011-08-02%2Bat%2B12.51.10%2BPM.png" alt="" id="BLOGGER_PHOTO_ID_5636302681813014834" border="0" /></a><br /><br />There are plenty of other cool features that resque-cleaner brings to the table and they do a great job of explaining it on their <a href="https://github.com/ono/resque-cleaner">github README</a>.<br /><br /><span style="font-size:130%;">Let's Make it Legal</span><br /><br />Resque is a great tool for job queueing but after using it for a while, it's apparent that something is missing. The problem is that when things go wrong, there is only one failure queue. Failed jobs that are not important can hide those failures that must be dealt with immediately. When we add resque-cleaner into the picture, this problem goes away. I cannot picture anyone not wanting resque-cleaner out of box with resque. Let's just merge this functionality in with resque so everyone knows when the EndOfTheWorldIfThisDoesntRunJob has failed on their project.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com3tag:blogger.com,1999:blog-2453744292966511027.post-5146016889633431212011-06-27T18:27:00.000-07:002011-06-27T19:05:48.645-07:00Elasticsearch and Zen DiscoveryBeen working with a large Elasticsearch cluster within ec2 and I wanted to share my elasticsearch configuration.<br /><br /><script src="https://gist.github.com/1050309.js?file=elasticsearch.yml"></script><br /><br />The ping timeout and recovery settings are important for cluster health. In my case instances started at the same time would need multiple retries before they could connect to one another.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com1tag:blogger.com,1999:blog-2453744292966511027.post-40311572649820886232011-06-23T05:59:00.000-07:002011-06-23T06:05:14.697-07:00Elasticsearch cluster health in rubyRecently (just now) I was creating a large elastic search cluster using <a href="http://www.elasticsearch.org/guide/reference/modules/discovery/zen.html">zen discovery</a> because I could not get <a href="http://www.elasticsearch.org/guide/reference/modules/discovery/ec2.html">ec2 discovery</a> to work. Picture an elasticsearch.yml file like this but with many more hosts:<br /><br /><script src="https://gist.github.com/1042485.js?file=gistfile1.yml"></script><br /><br />I wanted to check the <a href="http://www.elasticsearch.org/guide/reference/api/admin-cluster-health.html">cluster health</a> of each node to make sure that they all were part of my cluster so I whipped together a quick ruby script to get the job done.<br /><br /><script src="https://gist.github.com/1042485.js?file=cluster_health.rb"></script>Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com0tag:blogger.com,1999:blog-2453744292966511027.post-72283075724653655202011-06-15T08:01:00.000-07:002011-08-06T19:42:29.396-07:00Realtime facebook updates with Rails, Koala, and ResqueIt's one thing if you know i hit like on skittles three years ago, it's another if I liked skittles 5 minutes ago. There is a good chance that I am currently still thinking of candy. Enter <a href="http://developers.facebook.com/docs/api/realtime/">facebook realtime updates</a> with Rails, Koala, and Resque.<br /><br />I am going to mostly assume that you are starting off with an empty rails3 project with koala and resque installed. Let's create a controller for receiving updates from facebook:<br /><br /><script src="https://gist.github.com/1027303.js?file=facebook_updates_controller.rb"></script><br /><br />Notice we have two methods. The index method is a get request from facebook and it is simply for verification. The create is where the real action happens. Let's move on to the FacebookLike#real_time_update! implementation.<br /><br /><script src="https://gist.github.com/1027303.js?file=facebook_like.rb"></script><br /><br />The above FacebookLike#real_time_update! method take a payload object which might look like this:<br /><br /><script src="https://gist.github.com/1027303.js?file=facebook_payload.json"></script><br /><br />Some important points about the realtime update payload:<br /><br /><ol><li>The id and uid are the same. They represent the facebook user id of the person doing the liking of something.</li><li>There is no information about what is being actually liked. We're going to have to query the facebook API to figure that out ourselves.</li><li>The same uid's are repeated for each timestamp. If I liked skittles at 12pm and rainbow skittles at 12:01 pm, there may be two entries for me in the payload.<br /></li></ol><br />Our code first concentrates on problem #3 from above. The FacebookLike#real_time_update! method first removes duplicate facebook ids by creating a hash of uids to entries and then taking entry with the smallest timestamp. Why with the smallest timestamp you ask? Because we are going to be querying the facebook api next using the since timestamp parameter? In my example, I liked skittles at 12pm and rainbow skittles at 12:01. If we query for all of my likes since 12pm, we will pick up my 12:01 like of rainbow skittles.<br /><br />So now that we have removed all duplicates, you're probably thinking that we should go ahead and query the facebook api for all likes since the first timestamp. Tempting, but this can be a time consuming query in the context of an http post to our rails app. Let's instead enqueue a resque job to do this enventually for each uid within the FacebookLike#RealtimeUpdate#enqueue_updates! method which just iterates over all facebook ids, finds the corresponding user, and enqueues a job to query and update all facebook likes since a given unix timestamp. Let's take a look at the resque job:<br /><br /><script src="https://gist.github.com/1027303.js?file=update_facebook_likes.rb"></script><br /><br /><br />The job simply delegates back to the FacebookLike model invoking update_likes_for_user! passing in the user object along with an options hash containing the :since timestamp. Now finally the FacebookLike#update_likes_for_user! queries the graph api for facebook likes since the given timestamp. Notice if the timestamp is not passed into the method, we query for all facebook likes. Next we iterate over the restuls and create the FacebookLike if it does not yet exist within our database.<br /><br />One last step before we deploy our code. We need to create the routes for our controller. <br /><br /><script src="https://gist.github.com/1027303.js?file=routes.rb"></script><br /><br />Ok, almost everything is in place. Let's deploy all that to production, open up a rails console, and let facebook know that we are interested in real time updates.<br /><br /><script src="https://gist.github.com/1027303.js?file=subscribe_realtime_fb_updates.rb"></script><br /><br />Obviously in the above sample, replace app_id and secret_key with your actual app_id and secret key. <br /><br />Alright, now you should be getting updates. How do you know? In our app, facebook posts to our controller every few seconds. The result should be facebook update jobs enqueued within resque.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com5tag:blogger.com,1999:blog-2453744292966511027.post-38388754904568923872011-06-05T18:46:00.000-07:002011-06-05T19:22:05.450-07:00Clean Resque Workers from a Terminated HostSituation: A host that used to have resque workers running has been terminated and information about those workers still appears within resque web.<br /><br />Solution: The unregister_workers_for_host method below.<br /><br /><script src="https://gist.github.com/892247.js?file=gistfile1.rb"></script>Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com0tag:blogger.com,1999:blog-2453744292966511027.post-30452618799036123842011-06-04T06:39:00.000-07:002011-06-08T12:02:46.503-07:00Why aren't I blogging?<span style="font-size:100%;">I've always wanted to be a regular technology blogger but it just has not happened. Of late I've been pondering what exactly is holding me back. Am I too busy? Do I have nothing to blog about? Those are both possibilities, but I think my biggest barrier is that I don't have a decent blog set up. Sounds silly, but from my angle, going from no blog to blog is no simple task. So many questions that need answering:<br /></span><ul><li><span style="font-size:100%;">Where should I blog?</span></li><li><span style="font-size:100%;">What is the name of my blog?</span></li><li><span style="font-size:100%;">How do I make it look pretty?</span></li><li><span style="font-size:100%;">How do I handle code syntax highlighting?</span></li></ul><span style="font-size:100%;">I have a hunch that other technologists out there may be in the same boat... so I have decided to blog about my journey from no blog to blog. After all, wouldn't the technology field be a better place if we all had blogs?</span><br /><br /><span style="font-size:130%;">Where to blog?</span><br /><br />I chose blogger.com and I am not exactly sure why other than that it seems like it's a pretty popular choice that's free and customizable. I'll let you know how it works out...<br /><br /><span style="font-size:130%;">The Name</span><br /><br /><span style="font-size:100%;">I'd be lying if i said that coming up with a name was easy. I googled around a bit for some advice:<br /><br /><a href="http://www.chrisg.com/catchy-blog-names/">http://www.chrisg.com/catchy-blog-names/</a><br /><br />Even found some generators out there which helped spur my creativity by generating random words:<br /><a href="http://vicmadz.com/create-blog-names/"><br />http://vicmadz.com/create-blog-names/</a><br /><br />In the end I wanted something unique, catchy, that had at least something to do with me and technology. So, I thought, what is unique about me as a software developer? Best I could come up with is that i grew up on a dairy farm. Don't think there are many farmers out there that become software developers.<br /><br />Ok... something that brings together farmers and technology. Started brainstorming some ideas on a scratch pad with my wife during a long car ride.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-Zid3CD-C6qA/Teqwmlhqk4I/AAAAAAAAjxk/Lwez3_WxuO4/s1600/Screen%2Bshot%2B2011-06-04%2Bat%2B6.15.36%2BPM.png"><img style="cursor: pointer; width: 400px; height: 233px;" src="http://1.bp.blogspot.com/-Zid3CD-C6qA/Teqwmlhqk4I/AAAAAAAAjxk/Lwez3_WxuO4/s400/Screen%2Bshot%2B2011-06-04%2Bat%2B6.15.36%2BPM.png" alt="" id="BLOGGER_PHOTO_ID_5614494062425576322" border="0" /></a><br /><br />Good start, but nothing jumped out at me so I slept on it. Next morning during a jog, it came to me:<br /><br /></span><span style="font-style: italic;font-size:100%;" >Cow Jumped over the Commodore64</span><span style="font-size:100%;"><br /><br />Obviously the cow represented the farmer in me and the commodore64 was our first family computer. Seemed to meet some <a href="http://www.chrisg.com/catchy-blog-names/">basic requirements</a>.<br /></span><ul><li><span style="font-size:100%;">Readable</span></li><li><span style="font-size:100%;">Pronounceable</span></li><li><span style="font-size:100%;">Spellable</span></li><li><span style="font-size:100%;">Memorable</span></li><li><span style="font-size:100%;">Concise</span></li><li><span style="font-size:100%;">Unique</span></li></ul><span style="font-size:100%;">Ok.. maybe it's not concise.<br /><br /></span><span style="font-size:130%;">How do I make it look pretty?</span><br /><br /><span style="font-size:100%;">Ok, this is an important one, but as you ca</span>n see, I'm probably not the best person to be giving advice out on this topic. I actually wanted to use my blog as an opportunity to learn a basic level of design and CSS as this has never been my strong point as a developer. Not something that's going to happen over night but I have started with these two books:<br /><br /><a href="http://www.amazon.com/Non-Designers-Design-Book-Robin-Williams/dp/0321534042/ref=sr_1_1?ie=UTF8&qid=1307557928&sr=8-1">Non Designers Design Book</a><br /><a href="http://www.amazon.com/CSS-Missing-David-Sawyer-McFarland/dp/0596802447/ref=sr_1_1?ie=UTF8&qid=1307557981&sr=8-1">CSS: The Missing Manual</a><br /><br />As for the logo, I asked a designer buddy for that one.<br /><br />Bottom line is, if you are like me, you are probably not going to have a beautiful blog on day 1. It will always be a work in progress, but don't let it keep your from blogging.<br /><br /><span style="font-size:130%;">Code Syntax Highlighting</span><br /><br /><span style="font-size:100%;">What kind of technology blog would it be without code examples?! There are a ton of <a href="http://www.webresourcesdepot.com/11-syntax-highlighters-to-beautify-code-presentation/">options</a> out there, but in the end</span> I decided to just <a href="https://github.com/blog/122-embedded-gists">embed github gists</a> as it was the simplest solution and it comes with all of the gist versioning niceness.Karl Baumhttp://www.blogger.com/profile/07019373625555053831noreply@blogger.com1