<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>RubyRuby | Ruby</title>
	
	<link>http://seriouslyruby.com</link>
	<description>real-time discovery by s.erious.ly</description>
	<lastBuildDate>Mon, 06 Feb 2012 12:00:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
	
<cloud domain="seriouslyruby.com" port="80" path="/?rsscloud=notify" registerProcedure="" protocol="http-post" />
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/seriouslyruby" /><feedburner:info uri="seriouslyruby" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://seriouslyruby.com/?pushpress=hub" /><item>
		<title>More Tiny Classes</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/SwLgDk2MtNg/</link>
		<comments>http://feedproxy.google.com/~r/railstips/~3/ASwJ8eLYCi0/#comments</comments>
		<pubDate>Mon, 06 Feb 2012 12:00:00 +0000</pubDate>
		<dc:creator>John Nunemaker</dc:creator>
				<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Rails Tips]]></category>
		<category><![CDATA[gauges]]></category>
		<category><![CDATA[refactoring]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=6a71f278d86f8acdbdb1a3d8d8ffd579</guid>
		<description><![CDATA[<p>In which I share how we are using more tiny classes to make Gauges more maintainable.</p>]]></description>
			<content:encoded><![CDATA[<p>My last post, <a href="http://railstips.org/blog/archives/2012/02/04/keep-em-separated/">Keep &#8217;Em Separated</a>, made me realize I should start sharing more about what we are doing to make Gauges maintainable. This post is another in the same vein.</p>
<p><a href="http://gaug.es">Gauges</a> allows you to share a gauge with someone else by email. That email does not have to exist prior to your adding it, because nothing is more annoying that wanting to share something with a friend or co-worker, but first having to get them to sign up for the service.</p>
<p>If the email address is found, we add the user to the gauge and notify them that they have been added.</p>
<p>If the email address is not found, we create an invite and then send an email to notify them they should sign up, so they can see the data.</p>
<h2>The Problem: McUggo Route</h2>
<p>The aforementioned sharing logic isn&#8217;t difficult, but it was just enough that our share route was getting uggo. It started off looking something like this:</p>
<pre><code class="ruby">post('/gauges/:id/shares') do
  gauge = Gauge.get(params['id'])

  if user = User.first_by_email(params[:email])
    Stats.increment('shares.existing')
    gauge.add_user(user)
    ShareWithExistingUserMailer.new(gauge, user).deliver
    {:share =&gt; SharePresenter.new(gauge, user)}.to_json
  else
    invite = gauge.invite(params['email'])
    Stats.increment('shares.new')
    ShareWithNewUserMailer.new(gauge, invite).deliver
    {:share =&gt; SharePresenter.new(gauge, invite)}.to_json
  end
end</code></pre>
<p>Let&#8217;s be honest. We&#8217;ve all seen Rails controller actions and Sinatra routes that are fantastically worse, but this was really burning my eyes, so I charged our <a href="http://theprogrammingbutler.com">programming butler</a> to refactor it.</p>
<h2>The Solution: Move Logic to Separate Class</h2>
<p>We talked some ideas through, and once he had finished, the route looked more like this:</p>
<pre><code class="ruby">post('/gauges/:id/shares') do
  gauge    = Gauge.get(params['id'])
  sharer   = GaugeSharer.new(gauge, params['email'])
  receiver = sharer.perform
  {:share =&gt; SharePresenter.new(gauge, receiver)}.to_json
end</code></pre>
<p>Perfect? Who cares. Waaaaaaaaay better? <strong>Yes</strong>. The concern of a user existing or not is <strong>moved away to a place where the route could care less</strong>.</p>
<p>Also, the bonus is that <strong>sharing a gauge can now be used without invoking a route</strong>.</p>
<p>So what does GaugeSharer look like?</p>
<pre><code class="ruby">class GaugeSharer
  def initialize(gauge, email)
    @gauge = gauge
    @email = email
  end

  def user
    @user ||= … # user from database
  end

  def existing?
    user.present?
  end

  def perform
    if existing?
      share_with_existing_user
    else
      share_with_invitee
    end
  end

  def share_with_existing_user
    # add user to gauge
    ShareWithExistingUserMailer.new(@gauge, user).deliver
    user
  end

  def share_with_invitee
    invite = ... # invite to db
    ShareWithNewUserMailer.new(@gauge, invite).deliver
    invite
  end
end</code></pre>
<p>Now, instead of having several higher-level tests to check each piece of logic, we can just ensure that GaugeSharer is invoked correctly in the route test and then test the crap out of GaugeSharer with unit tests. We can also use GaugeSharer anywhere else in the application that we want to.</p>
<p><strong>This isn&#8217;t a dramatic change in code, but it has a dramatic effect on the coder</strong>. Moving all these bits into separate classes and tiny methods improves <strong>ease of testing</strong> and, probably more importantly, <strong>ease of grokking</strong> for another developer, including yourself at a later point in time.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/railstips?a=ASwJ8eLYCi0:l_5hQkG0nAA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/railstips?a=ASwJ8eLYCi0:l_5hQkG0nAA:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/railstips?a=ASwJ8eLYCi0:l_5hQkG0nAA:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"></img></a>
</div>
<p><img src="http://feeds.feedburner.com/~r/railstips/~4/ASwJ8eLYCi0" height="1" width="1"/></p>

<p><a href="http://feedads.g.doubleclick.net/~a/YMgU-s8SAqLMTtKdHqmexQP_mC0/0/da"><img src="http://feedads.g.doubleclick.net/~a/YMgU-s8SAqLMTtKdHqmexQP_mC0/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/YMgU-s8SAqLMTtKdHqmexQP_mC0/1/da"><img src="http://feedads.g.doubleclick.net/~a/YMgU-s8SAqLMTtKdHqmexQP_mC0/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/SwLgDk2MtNg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://seriouslyruby.com/2012/02/06/05/00/00/more-tiny-classes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://feedproxy.google.com/~r/railstips/~3/ASwJ8eLYCi0/</feedburner:origLink></item>
		<item>
		<title>What’s New in Edge Rails #7</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/R6EyFYti6pE/whats-new-in-edge-rails-7.html</link>
		<comments>http://afreshcup.com/home/2012/2/6/whats-new-in-edge-rails-7.html#comments</comments>
		<pubDate>Mon, 06 Feb 2012 11:26:28 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Edge Rails]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=ced2ca815f672227c6afa42b4db7763c</guid>
		<description><![CDATA[Week of January 29-February 4, 2012

Things are still moving along on the Rails 4 front, with a mix of tidying up and new features. Here are some of the commits that caught my eye last week.


	A whole mess of commits worked over some of the collection...]]></description>
			<content:encoded><![CDATA[<h1>Week of January 29-February 4, 2012</h1>
<p>Things are still moving along on the Rails 4 front, with a mix of tidying up and new features. Here are some of the commits that caught my eye last week.</p>
<ul>
<li>A whole mess of commits worked over some of the collection form helpers. Now <code>collection_select</code> and <code>options_from_collection_for_select</code> can take procs to evaluate their text and value in the current context, and there are new <code>collection_check_boxes</code> and <code>collection_radio_buttons</code> helpers. The easiest starting point is with <a href="https://github.com/rails/rails/commit/f506c8063b3084f54aa8bd157d94f10b7aed2bf0">f506c806</a> which documents the API changes.</li>
<li><a href="https://github.com/rails/rails/commit/8270e4a8ce8337fca98030953922e5992b06a3dd">8270e4a8</a> implements the null object pattern for ActiveRecord relations. Post.none will return a relation containing no posts &#8211; which may seem daffy, but makes for much nicer chainable methods on your ActiveRecord objects.</li>
<li><a href="https://github.com/rails/rails/commit/b31eac56097a0bfc5f5af70de91ad261067a395f">b31eac56</a> replaces all <code>for</code> loops in the Rails source code with <code>Enumerable#each</code>. This should lead to considerably less confusion for new Rails developers.</li>
<li><a href="https://github.com/rails/rails/commit/336ff8a97e9391d4111e923a9b841376110d04c9">336ff8a9</a><a href="https://github.com/rails/rails/commit/"></a> deprecates the <code>:dependent => :restrict</code> construct for ActiveRecord associations.</li>
<li><a href="https://github.com/rails/rails/commit/66c04431d3a886c99e63e87167aba5808a16ca10">66c04431</a> updates Rails&#8217; Unicode support from 6.0 to 6.1, making it<a href="http://www.fileformat.info/info/unicode/char/1f4a9/index.htm"> pile of poo</a> compatible.</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/0/da"><img src="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/1/da"><img src="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/R6EyFYti6pE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14896498.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/6/whats-new-in-edge-rails-7.html</feedburner:origLink></item>
		<item>
		<title>What’s New in Edge Rails #7</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/R6EyFYti6pE/whats-new-in-edge-rails-7.html</link>
		<comments>http://afreshcup.com/home/2012/2/6/whats-new-in-edge-rails-7.html#comments</comments>
		<pubDate>Mon, 06 Feb 2012 11:26:28 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Edge Rails]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=ced2ca815f672227c6afa42b4db7763c</guid>
		<description><![CDATA[Week of January 29-February 4, 2012

Things are still moving along on the Rails 4 front, with a mix of tidying up and new features. Here are some of the commits that caught my eye last week.


	A whole mess of commits worked over some of the collection...]]></description>
			<content:encoded><![CDATA[<h1>Week of January 29-February 4, 2012</h1>
<p>Things are still moving along on the Rails 4 front, with a mix of tidying up and new features. Here are some of the commits that caught my eye last week.</p>
<ul>
<li>A whole mess of commits worked over some of the collection form helpers. Now <code>collection_select</code> and <code>options_from_collection_for_select</code> can take procs to evaluate their text and value in the current context, and there are new <code>collection_check_boxes</code> and <code>collection_radio_buttons</code> helpers. The easiest starting point is with <a href="https://github.com/rails/rails/commit/f506c8063b3084f54aa8bd157d94f10b7aed2bf0">f506c806</a> which documents the API changes.</li>
<li><a href="https://github.com/rails/rails/commit/8270e4a8ce8337fca98030953922e5992b06a3dd">8270e4a8</a> implements the null object pattern for ActiveRecord relations. Post.none will return a relation containing no posts &#8211; which may seem daffy, but makes for much nicer chainable methods on your ActiveRecord objects.</li>
<li><a href="https://github.com/rails/rails/commit/b31eac56097a0bfc5f5af70de91ad261067a395f">b31eac56</a> replaces all <code>for</code> loops in the Rails source code with <code>Enumerable#each</code>. This should lead to considerably less confusion for new Rails developers.</li>
<li><a href="https://github.com/rails/rails/commit/336ff8a97e9391d4111e923a9b841376110d04c9">336ff8a9</a><a href="https://github.com/rails/rails/commit/"></a> deprecates the <code>:dependent => :restrict</code> construct for ActiveRecord associations.</li>
<li><a href="https://github.com/rails/rails/commit/66c04431d3a886c99e63e87167aba5808a16ca10">66c04431</a> updates Rails&#8217; Unicode support from 6.0 to 6.1, making it<a href="http://www.fileformat.info/info/unicode/char/1f4a9/index.htm"> pile of poo</a> compatible.</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/0/da"><img src="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/1/da"><img src="http://feedads.g.doubleclick.net/~a/usoOLXa_QYIw_CYwSrVkJnc0yFg/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/R6EyFYti6pE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14896498.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/6/whats-new-in-edge-rails-7.html</feedburner:origLink></item>
		<item>
		<title>Double Shot #811</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/30aIZy_pYGo/double-shot-811.html</link>
		<comments>http://afreshcup.com/home/2012/2/6/double-shot-811.html#comments</comments>
		<pubDate>Mon, 06 Feb 2012 11:24:58 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Double Shot]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=cfe26cf40a658730fefd1836a4abdd3e</guid>
		<description><![CDATA[Not so much to report this morning, since I spent a good chunk of the weekend out with the Boy Scouts.


	Twitter's Bootstrap in the Asset Pipeline - Hooking it up using the less-rails-bootstrap gem.
	Async JavaScript - Book project on Kickstarter.
	Tr...]]></description>
			<content:encoded><![CDATA[<p>Not so much to report this morning, since I spent a good chunk of the weekend out with the Boy Scouts.</p>
<ul>
<li><strong><a href="http://blog.crowdint.com/2012/02/03/bootstrap-in-the-asset-pipeline.html">Twitter&#8217;s Bootstrap in the Asset Pipeline</a></strong> &#8211; Hooking it up using the less-rails-bootstrap gem.</li>
<li><strong><a href="http://www.kickstarter.com/projects/869786663/async-javascript-book">Async JavaScript</a></strong> &#8211; Book project on Kickstarter.</li>
<li><strong><a href="http://try.redis-db.com/">Try Redis</a></strong> &#8211; An in-browser demonstration and tutorial.</li>
<li><strong><a href="http://peepcode.com/products/html5-browser-caching">HTML5: Browser Caching</a></strong> &#8211; the latest from PeepCode.</li>
<li><strong><a href="https://github.com/basho/riak-ruby-client">riak-ruby-client</a></strong> &#8211; This gem has hit version 1.0, for those of you interested in riak.</li>
<li><strong><a href="http://ngenuity.ngmoco.com/2012/01/introducing-falcore-and-timber.html">Introducing Falcore and Timber</a></strong> &#8211; A pipelined web server (written in Go) and a logger library to go with it.</li>
<li><strong><a href="http://www.simonecarletti.com/blog/2012/02/heroku-and-rails-3-2-assetprecompile-error/">Heroku and Rails 3.2 asset:precompile error</a></strong> &#8211; And how to fix it.</li>
<li><strong><a href="http://www.turnjs.com/#home">turn.js</a></strong> &#8211; Page-turning animation for your HTML5 sites.</li>
<li><strong><a href="http://quojs.tapquo.com/">QuoJS</a></strong> &#8211; &#8220;Micro JavaScript Library for Mobile Devices.&#8221;</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/0/da"><img src="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/1/da"><img src="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/30aIZy_pYGo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14896486.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/6/double-shot-811.html</feedburner:origLink></item>
		<item>
		<title>Double Shot #811</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/30aIZy_pYGo/double-shot-811.html</link>
		<comments>http://afreshcup.com/home/2012/2/6/double-shot-811.html#comments</comments>
		<pubDate>Mon, 06 Feb 2012 11:24:58 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Double Shot]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=cfe26cf40a658730fefd1836a4abdd3e</guid>
		<description><![CDATA[Not so much to report this morning, since I spent a good chunk of the weekend out with the Boy Scouts.


	Twitter's Bootstrap in the Asset Pipeline - Hooking it up using the less-rails-bootstrap gem.
	Async JavaScript - Book project on Kickstarter.
	Tr...]]></description>
			<content:encoded><![CDATA[<p>Not so much to report this morning, since I spent a good chunk of the weekend out with the Boy Scouts.</p>
<ul>
<li><strong><a href="http://blog.crowdint.com/2012/02/03/bootstrap-in-the-asset-pipeline.html">Twitter&#8217;s Bootstrap in the Asset Pipeline</a></strong> &#8211; Hooking it up using the less-rails-bootstrap gem.</li>
<li><strong><a href="http://www.kickstarter.com/projects/869786663/async-javascript-book">Async JavaScript</a></strong> &#8211; Book project on Kickstarter.</li>
<li><strong><a href="http://try.redis-db.com/">Try Redis</a></strong> &#8211; An in-browser demonstration and tutorial.</li>
<li><strong><a href="http://peepcode.com/products/html5-browser-caching">HTML5: Browser Caching</a></strong> &#8211; the latest from PeepCode.</li>
<li><strong><a href="https://github.com/basho/riak-ruby-client">riak-ruby-client</a></strong> &#8211; This gem has hit version 1.0, for those of you interested in riak.</li>
<li><strong><a href="http://ngenuity.ngmoco.com/2012/01/introducing-falcore-and-timber.html">Introducing Falcore and Timber</a></strong> &#8211; A pipelined web server (written in Go) and a logger library to go with it.</li>
<li><strong><a href="http://www.simonecarletti.com/blog/2012/02/heroku-and-rails-3-2-assetprecompile-error/">Heroku and Rails 3.2 asset:precompile error</a></strong> &#8211; And how to fix it.</li>
<li><strong><a href="http://www.turnjs.com/#home">turn.js</a></strong> &#8211; Page-turning animation for your HTML5 sites.</li>
<li><strong><a href="http://quojs.tapquo.com/">QuoJS</a></strong> &#8211; &#8220;Micro JavaScript Library for Mobile Devices.&#8221;</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/0/da"><img src="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/1/da"><img src="http://feedads.g.doubleclick.net/~a/pwI2zxgnTZ4FIwdnK52dXfl6_Dk/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/30aIZy_pYGo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14896486.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/6/double-shot-811.html</feedburner:origLink></item>
		<item>
		<title>Keep ‘Em Separated</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/lVLumrmt1dA/</link>
		<comments>http://feedproxy.google.com/~r/railstips/~3/aRF1TynWxyA/#comments</comments>
		<pubDate>Sat, 04 Feb 2012 18:00:00 +0000</pubDate>
		<dc:creator>John Nunemaker</dc:creator>
				<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Rails Tips]]></category>
		<category><![CDATA[gauges]]></category>
		<category><![CDATA[pusher]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=0bf6aa25e75092691d4ee58f82fc6767</guid>
		<description><![CDATA[<p>In which I share a quick tale of refactoring.</p>]]></description>
			<content:encoded><![CDATA[<p><strong>Note</strong>: If you end up enjoying this post, you should do two things: <a href="http://pusher.com/">sign up for Pusher</a> and then <a href="https://www.destroyallsoftware.com/">subscribe to destroy all software screencasts</a>. I&#8217;m not telling you do this because I get referrals, I just really like both services.</p>
<p>For those that do not know, <a href="http://get.gaug.es">Gauges</a> currently uses <a href="http://pusher.com">Pusher.com</a> for flinging around all the traffic live.</p>
<p>Every track request to Gauges sends a request to Pusher. We do this using EventMachine in a thread, as I have <a href="http://railstips.org/blog/archives/2011/05/04/eventmachine-and-passenger/">previously written about</a>.</p>
<h2>The Problem</h2>
<p>The downside of this, is when you get to the point we were (thousands of a requests a minute), there are so many pusher notifications to send (thousands of a minute) that <strong>the EM thread starts stealing a lot of time</strong> from the main request thread. You end up with random slow requests that have one to five seconds of &#8220;uninstrumented&#8221; time. <strong>Definitely not a happy scaler does this make</strong>.</p>
<p>In the past, we had talked about keeping track of which gauges were actually being watched and only sending a notification for those, but never actually did anything about it.</p>
<h2>The Solution</h2>
<p>Recently, Pusher added <a href="http://pusher.com/docs/webhooks">web hooks</a> on channel occupy and channel vacate. This, combined with a growing number of slow requests, was just the motivation I needed to come up with a solution.</p>
<p>We (<a href="http://opensoul.org/">@bkeepers</a> and I) started by mapping a simple route to a class.</p>
<pre><code class="ruby">class PusherApp &lt; BaseApp
  post '/pusher/ping' do
    webhook = Pusher::WebHook.new(request)
    if webhook.valid?
      PusherPing.receive(webhook)
      'ok'
    else
      status 401
      'invalid'
    end
  end
end</code></pre>
<p>Using a simple class method like this moves all logic out of the route and into a place that is easier to test. The receive method iterates the events and runs each ping individually.</p>
<pre><code class="ruby">class PusherPing
  def self.receive(webhook)
    webhook.events.each do |event|
      new(event, webhook.time).run
    end
  end
end</code></pre>
<p>At first, we had something like this for each PusherPing instance.</p>
<pre><code class="ruby">class PusherPing
  def initialize(event, time)
    @event         = event || {}
    @time          = time
    @event_name    = @event['name']
    @event_channel = @event['channel']
  end

  def run
    case @event_name
    when 'channel_occupied'
      occupied
    when 'channel_vacated'
      vacated
    end
  end

  def occupied
    update(@time)
  end

  def vacated
    update(nil)
  end

  def update(value)
    # update the gauge in the
    # db with the value
  end
end</code></pre>
<p>We pushed out the change so we could start marking gauges as occupied. We then forced a browser refresh, which effectively vacated and re-occupied all gauges people were watching.</p>
<p>Once we new the occupied state of each gauge was correct, we added the code to only send the request to pusher on track if a gauge was occupied.</p>
<p>Deploy. Celebrate. Booyeah.</p>
<h2>The New Problem</h2>
<p>Then, less than a day later, we realized that pusher doesn&#8217;t guarantee the order of events. Imagine someone vacating and then occupying a gauge, but receiving the occupy first and then the vacate.</p>
<p><strong>This situation would mean that live tracking would never turn on</strong> for the gauge. Indeed, it started happening to a few people, who quickly let us know.</p>
<h2>The New Solution</h2>
<p>We figured it was better to send a few extra notifications than never send any, so we decided to &#8220;occupy&#8221; gauges on our own when people loaded up the Gauges dashboard.</p>
<p>We started in and quickly realized the error of our ways in the pusher ping. Having the database calls directly tied to the PusherPing class meant that we had two options:</p>
<ol>
<li>Use the PusherPing class to occupy a gauge when the dashboard loads, which just felt wrong.</li>
<li>Re-write it to separate the occupying and vacating of a gauge from the PusherPing class.</li>
</ol>
<p>Since we are good little developers, we went with 2. We created a GaugeOccupier class that looks like this:</p>
<pre><code class="ruby">class GaugeOccupier
  attr_reader :ids

  def initialize(*ids)
    @ids = ids.flatten.compact.uniq
  end

  def occupy(time=Time.now.utc)
    update(time)
  end

  def vacate
    update(nil)
  end

private

  def update(value)
    return if @ids.blank?
    # do the db updates
  end
end</code></pre>
<p>We tested that class on its own quite quickly and refactored the PusherPing to use it.</p>
<pre><code class="ruby">class PusherPing
  def run
    case @event_name
    when 'channel_occupied'
      GaugeOccupier.new(gauge_id).occupy(@time)
    when 'channel_vacated'
      GaugeOccupier.new(gauge_id).vacate
    end
  end
end</code></pre>
<p>Boom. PusherPing now worked the same and we had a way to &#8220;occupy&#8221; gauges separate from the PusherPing. We added the occupy logic to the correct point in our app like so:</p>
<pre><code class="ruby">ids = gauges.map { |gauge| gauge.id }
GaugeOccupier.new(ids).occupy</code></pre>
<p>At this point, we were now &#8220;occupied&#8221; more than &#8220;vacated&#8221;, which is good. However, you may have noticed, that we still had the issue where someone loads the dashboard, we occupy the gauge, but then receive a delayed, or what I will now refer to as &#8220;stale&#8221;, hook.</p>
<p>To fix the stale hook issue, we simply added a bit of logic to the PusherPing class to detect staleness and simple ignore the ping if it is stale.</p>
<pre><code class="ruby">class PusherPing
  def run
    return if stale?
    # do occupy/vacate
  end

  def stale?
    return false if gauge.occupied_at.blank?
    gauge.occupied_at &gt; @time
  end
end</code></pre>
<h2>Closing Thoughts</h2>
<p>This is by no means a perfect solution. There are still other holes. For example, a gauge could be occupied by us after we receive a vacate hook from pusher and stay in an &#8220;occupied&#8221; state, sending notifications that no one is looking for.</p>
<p>To fix that issue, we can add a cleanup cron or something that occasionally gets all occupied channels from pusher and vacates gauges that are not in the list.</p>
<p>We decided it wasn&#8217;t worth the time. We pushed out the occupy fix and are now reaping the benefits of sending about 1/6th of the pusher requests we were before. This means our EventMachine thread is doing less work, which gives our main thread more time to process requests.</p>
<p>You might think us crazy for sending hundreds of http requests in a thread that shares time with the main request thread, but it is actually working quite well.</p>
<p>We know that some day we will have to move this to a queue and an external process that processes the queue, but <strong>that day is not today</strong>. Instead, we can <strong>focus on the next round of features that will blow people&#8217;s socks off</strong>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/railstips?a=aRF1TynWxyA:tkI5wtJnjaM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/railstips?a=aRF1TynWxyA:tkI5wtJnjaM:dnMXMwOfBR0"><img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/railstips?a=aRF1TynWxyA:tkI5wtJnjaM:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"></img></a>
</div>
<p><img src="http://feeds.feedburner.com/~r/railstips/~4/aRF1TynWxyA" height="1" width="1"/></p>

<p><a href="http://feedads.g.doubleclick.net/~a/9vNeoPpl_g0kq4d8u6UCNeJubYw/0/da"><img src="http://feedads.g.doubleclick.net/~a/9vNeoPpl_g0kq4d8u6UCNeJubYw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/9vNeoPpl_g0kq4d8u6UCNeJubYw/1/da"><img src="http://feedads.g.doubleclick.net/~a/9vNeoPpl_g0kq4d8u6UCNeJubYw/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/lVLumrmt1dA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://seriouslyruby.com/2012/02/04/11/00/00/keep-em-separated/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://feedproxy.google.com/~r/railstips/~3/aRF1TynWxyA/</feedburner:origLink></item>
		<item>
		<title>JRuby 1.6.6</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/byMEBEajGnU/197</link>
		<comments>http://feedproxy.google.com/~r/railsenvy-podcast/~3/9q8zzVG2tyI/197#comments</comments>
		<pubDate>Sat, 04 Feb 2012 01:13:18 +0000</pubDate>
		<dc:creator>Jason Seifer and Dan Benjamin</dc:creator>
				<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Rails Envy]]></category>

		<guid isPermaLink="false">http://rubyshow.com/episodes/197</guid>
		<description><![CDATA[Jason and Peter talk about JRuby, the id of nil, JBuilder, Devise 2.0, and more.]]></description>
			<content:encoded><![CDATA[<p>Jason and Peter talk about JRuby, the id of nil, JBuilder, Devise 2.0, and more.<img src="http://feeds.feedburner.com/~r/railsenvy-podcast/~4/9q8zzVG2tyI" height="1" width="1"/></p>

<p><a href="http://feedads.g.doubleclick.net/~a/7kbgXn1Jf6arct3m9BhgRQbc7ps/0/da"><img src="http://feedads.g.doubleclick.net/~a/7kbgXn1Jf6arct3m9BhgRQbc7ps/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/7kbgXn1Jf6arct3m9BhgRQbc7ps/1/da"><img src="http://feedads.g.doubleclick.net/~a/7kbgXn1Jf6arct3m9BhgRQbc7ps/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/byMEBEajGnU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://seriouslyruby.com/2012/02/03/18/13/18/jruby-1-6-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://feedproxy.google.com/~r/railsenvy-podcast/~3/9q8zzVG2tyI/197</feedburner:origLink></item>
		<item>
		<title>Open Source Report #4</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/ywjESu8YnRM/open-source-report-4.html</link>
		<comments>http://afreshcup.com/home/2012/2/3/open-source-report-4.html#comments</comments>
		<pubDate>Fri, 03 Feb 2012 11:26:40 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Miscellany]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=a822bf2978d1fb6654c52db53a787c9f</guid>
		<description><![CDATA[In the spirit of keeping myself honest, here's what I've touched on open source work over the past week. Most of it isn't spectacular, but I did manage to do something every day in January and I'm happy about that.


	RubyGems Guides - This was where I...]]></description>
			<content:encoded><![CDATA[<p>In the spirit of keeping myself honest, here&#8217;s what I&#8217;ve touched on open source work over the past week. Most of it isn&#8217;t spectacular, but I did manage to do something every day in January and I&#8217;m happy about that.</p>
<ul>
<li><strong><a href="http://guides.rubygems.org/">RubyGems Guides</a></strong> &#8211; This was where I did most of my work the past week, bringing in a new Contributing page and taking editing passes over a few other pages. Details are in the <a href="http://afreshcup.com/home/2012/2/3/open-source-report-4.html">repo</a>.</li>
<li><strong><a href="https://github.com/rubygems">RubyGems</a></strong> &#8211; Deprecated the separate <a href="https://github.com/rubygems/contribute">contribute</a> repo and retargeted the Contribute link on the <a href="https://rubygems.org/">RubyGems.org</a> home page to point to the Guides <a href="http://guides.rubygems.org/contributing/">Contributing</a> page instead.</li>
<p>	<strong><a href="https://github.com/nruth/show_me_the_cookies">Show me the cookies</a></strong> &#8211; Updated the <a href="https://github.com/nruth/show_me_the_cookies/pull/6">documentation</a> for this Capybara addon.	</p>
<li><strong><strong><a href="http://afreshcup.com/home/2012/1/30/whats-new-in-edge-rails-6.html">What&#8217;s New in Edge Rails</a></strong></strong> -Finished up #6 in this series.</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/cs7D3wxu775nxssthMu3odpP83s/0/da"><img src="http://feedads.g.doubleclick.net/~a/cs7D3wxu775nxssthMu3odpP83s/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/cs7D3wxu775nxssthMu3odpP83s/1/da"><img src="http://feedads.g.doubleclick.net/~a/cs7D3wxu775nxssthMu3odpP83s/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/ywjESu8YnRM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14855617.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/3/open-source-report-4.html</feedburner:origLink></item>
		<item>
		<title>Double Shot #810</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/vfwJJ8WAMFI/double-shot-810.html</link>
		<comments>http://afreshcup.com/home/2012/2/3/double-shot-810.html#comments</comments>
		<pubDate>Fri, 03 Feb 2012 11:09:19 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Double Shot]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=f20f6a5a20da62f02964fbafd956f6a2</guid>
		<description><![CDATA[A few orts to close out the work week.


	Jason - Free JSON viewer and editor for OS X.
	The History of Rubinius - Evan Phoenix offers some insights into the origin and growth of Rubinius.
	Ruby Freelancers - New site featuring a podcast for those inte...]]></description>
			<content:encoded><![CDATA[<p>A few orts to close out the work week.</p>
<ul>
<li><strong><a href="http://olivierlabs.com/jason/">Jason</a></strong> &#8211; Free JSON viewer and editor for OS X.</li>
<li><strong><a href="http://razzledazzle.it/1%3Aorigin-story/3%3Arubinius">The History of Rubinius</a></strong> &#8211; Evan Phoenix offers some insights into the origin and growth of Rubinius.</li>
<li><strong><a href="http://rubyfreelancers.com/">Ruby Freelancers</a></strong> &#8211; New site featuring a podcast for those interested in getting started.</li>
<li><strong><a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">Yes, You Should Write Controller Tests!</a></strong> &#8211; Argues that as classes, they need test coverage in every method. I find that recently I&#8217;ve been writing a lot more integrations than functionals to achieve that, though.</li>
<li><strong><a href="http://stevendanna.github.com/blog/2012/01/28/shef-debugging-tips-1/">Shef Tips and Tricks: Steppiong Through Chef-client Runs with Shef</a></strong> &#8211; An introduction to the interactive console for the Chef automated provisioning system.</li>
<li><strong><a href="http://alittlecode.com/backbone-js-a-roundup-for-beginners/">Backbone.js: A Roundup for Beginners</a></strong> &#8211; Start here with a bunch of links to other resources.</li>
<li><strong><a href="http://icelab.com.au/articles/customising-activerecords-attribute-formatting-on-inspect/">Customising ActiveRecord&#8217;s attribute formatting on inspect </a></strong>- Yes, it can be done. And it&#8217;s useful.</li>
<li><strong><a href="http://www.fileformat.info/info/unicode/char/1f4a9/index.htm">Unicode Character &#8216;PILE OF POO&#8217; (U+1F4A9)</a></strong> &#8211; Yes, it&#8217;s really part of the standard.</li>
<li><strong><a href="http://batsov.com/articles/2011/11/30/the-ultimate-collection-of-emacs-resources/">The Ultimate Collection of Emacs Resources</a></strong> &#8211; Yep, you have fun with that.</li>
<li><strong><a href="http://onilabs.com/stratifiedjs">StratifiedJS</a></strong> &#8211; Another approach to concurrency programming in JavaScript.</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/0/da"><img src="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/1/da"><img src="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/vfwJJ8WAMFI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14855482.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/3/double-shot-810.html</feedburner:origLink></item>
		<item>
		<title>Double Shot #810</title>
		<link>http://feedproxy.google.com/~r/seriouslyruby/~3/vfwJJ8WAMFI/double-shot-810.html</link>
		<comments>http://afreshcup.com/home/2012/2/3/double-shot-810.html#comments</comments>
		<pubDate>Fri, 03 Feb 2012 11:09:19 +0000</pubDate>
		<dc:creator>Mike Gunderloy</dc:creator>
				<category><![CDATA[A Fresh Cup]]></category>
		<category><![CDATA[Publishers]]></category>
		<category><![CDATA[Double Shot]]></category>

		<guid isPermaLink="false">http://seriouslyruby.com/?guid=f20f6a5a20da62f02964fbafd956f6a2</guid>
		<description><![CDATA[A few orts to close out the work week.


	Jason - Free JSON viewer and editor for OS X.
	The History of Rubinius - Evan Phoenix offers some insights into the origin and growth of Rubinius.
	Ruby Freelancers - New site featuring a podcast for those inte...]]></description>
			<content:encoded><![CDATA[<p>A few orts to close out the work week.</p>
<ul>
<li><strong><a href="http://olivierlabs.com/jason/">Jason</a></strong> &#8211; Free JSON viewer and editor for OS X.</li>
<li><strong><a href="http://razzledazzle.it/1%3Aorigin-story/3%3Arubinius">The History of Rubinius</a></strong> &#8211; Evan Phoenix offers some insights into the origin and growth of Rubinius.</li>
<li><strong><a href="http://rubyfreelancers.com/">Ruby Freelancers</a></strong> &#8211; New site featuring a podcast for those interested in getting started.</li>
<li><strong><a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">Yes, You Should Write Controller Tests!</a></strong> &#8211; Argues that as classes, they need test coverage in every method. I find that recently I&#8217;ve been writing a lot more integrations than functionals to achieve that, though.</li>
<li><strong><a href="http://stevendanna.github.com/blog/2012/01/28/shef-debugging-tips-1/">Shef Tips and Tricks: Steppiong Through Chef-client Runs with Shef</a></strong> &#8211; An introduction to the interactive console for the Chef automated provisioning system.</li>
<li><strong><a href="http://alittlecode.com/backbone-js-a-roundup-for-beginners/">Backbone.js: A Roundup for Beginners</a></strong> &#8211; Start here with a bunch of links to other resources.</li>
<li><strong><a href="http://icelab.com.au/articles/customising-activerecords-attribute-formatting-on-inspect/">Customising ActiveRecord&#8217;s attribute formatting on inspect </a></strong>- Yes, it can be done. And it&#8217;s useful.</li>
<li><strong><a href="http://www.fileformat.info/info/unicode/char/1f4a9/index.htm">Unicode Character &#8216;PILE OF POO&#8217; (U+1F4A9)</a></strong> &#8211; Yes, it&#8217;s really part of the standard.</li>
<li><strong><a href="http://batsov.com/articles/2011/11/30/the-ultimate-collection-of-emacs-resources/">The Ultimate Collection of Emacs Resources</a></strong> &#8211; Yep, you have fun with that.</li>
<li><strong><a href="http://onilabs.com/stratifiedjs">StratifiedJS</a></strong> &#8211; Another approach to concurrency programming in JavaScript.</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/0/da"><img src="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/1/da"><img src="http://feedads.g.doubleclick.net/~a/lxsU5sQtGG7FZcNzHPokeKOx-as/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/seriouslyruby/~4/vfwJJ8WAMFI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://afreshcup.com/home/rss-comments-entry-14855482.xml</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://afreshcup.com/home/2012/2/3/double-shot-810.html</feedburner:origLink></item>
	</channel>
</rss>

