<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

 <title>Alex Rothenberg</title>
 
 <link href="http://www.alexrothenberg.com/" rel="alternate" type="text/html" />
 <updated>2013-04-26T05:53:22-07:00</updated>
 <id>http://www.alexrothenberg.com/</id>
 <author>
   <name>Alex Rothenberg</name>
   <email>alex@alexrothenberg.com</email>
 </author>

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/alexrothenberg" /><feedburner:info uri="alexrothenberg" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
   <title>The Magic Behind Angularjs Dependency Injection</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/x0lg7LwQnmg/the-magic-behind-angularjs-dependency-injection.html" />
   <published>2013-02-11T00:00:00-08:00</published>
   <updated>2013-02-11T00:00:00-08:00</updated>
   <id>http://www.alexrothenberg.com/2013/02/11/the-magic-behind-angularjs-dependency-injection</id>
   <content type="html">&lt;!DOCTYPE html&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us"&gt;
&lt;head&gt;
   &lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&gt;
   &lt;title&gt;Alex Rothenberg - The "Magic" behind AngularJS Dependency Injection&lt;/title&gt;
   &lt;meta name="author" content="Alex Rothenberg" /&gt;
   &lt;link rel="alternate" type="application/atom+xml" title="Alex Rothenberg - feed" href="http://feeds2.feedburner.com/alexrothenberg"&gt;
   &lt;link rel="stylesheet" href="/stylesheets/master.css" type="text/css" media="screen" charset="utf-8"/&gt;
   &lt;link rel="stylesheet" href="/stylesheets/vibrant_ink.css" type="text/css" media="screen" charset="utf-8"/&gt;
   &lt;script src="/javascripts/jquery.js" type="text/javascript" charset="utf-8"&gt;&lt;/script&gt;
   &lt;script src="/javascripts/jquery.github.js" type="text/javascript" charset="utf-8"&gt;&lt;/script&gt;
   &lt;link href="http://www.alexrothenberg.com/atom.xml" rel="alternate" title="Alex Rothenberg" type="application/atom+xml" /&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div id="site"&gt;
  &lt;div id="header"&gt;
    &lt;h1&gt;AlexRothenberg&lt;/h1&gt;
    &lt;div id="menu"&gt;
      &lt;a href="/index.html" id="blog_link"&gt;Blog Articles&lt;/a&gt;
      &lt;a href="/open_source.html" id="github_link"&gt;Open Source&lt;/a&gt;
      &lt;a href="/about_me.html" id="about_me"&gt;About Me&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="clearfix"/&gt;

  &lt;div id="content"&gt;
    &lt;div class="post"&gt;
  &lt;h1&gt;The "Magic" behind AngularJS Dependency Injection&lt;/h1&gt;
  &lt;div class="date"&gt;February 11, 2013&lt;/div&gt;
  &lt;div&gt;&lt;a href="https://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-via="alexrothenberg" data-text="The 'Magic' behind AngularJS Dependency Injection" data-url="http://www.alexrothenberg.com/2013/02/11/the-magic-behind-angularjs-dependency-injection.html"&gt;Tweet&lt;/a&gt;
       &lt;script type="text/javascript" src="//platform.twitter.com/widgets.js"&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class="body"&gt;
  &lt;p&gt;If you&amp;#8217;ve built anything with AngularJS you know there&amp;#8217;s a lot of &amp;#8220;magic&amp;#8221; that you can usually ignore because it just works. One of the most magical parts for me is &lt;strong&gt;dependency injection&lt;/strong&gt;. Just by adding a parameter to your controller function you suddenly get access to a powerful Angular service. It&amp;#8217;s really pretty amazing but you sorta just have to trust it &amp;#8230; until something goes wrong.&lt;/p&gt;

&lt;p&gt;It turns out one easy way to break an AngularJS app is to minify your javascript. This happened to me when I deployed my app to production. The Angular app was being served from a Rails application and Rails automatically minifies javascript in prodution. It turns out there&amp;#8217;s a simple and a &lt;a href='http://docs.angularjs.org/tutorial/step_05'&gt;well documented fix&lt;/a&gt; in their tutorial (search for &amp;#8220;A Note on Minification&amp;#8221;) that boils down to &amp;#8220;use an array of parameter names&amp;#8221; but it wasn&amp;#8217;t clear to me &lt;em&gt;why&lt;/em&gt; it worked.&lt;/p&gt;

&lt;p&gt;In the rest of this article we&amp;#8217;re going to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a simple AngularJS application&lt;/li&gt;

&lt;li&gt;See how magical dependency injection is&lt;/li&gt;

&lt;li&gt;Investigate how dependency injection is implemented in AngularJS&lt;/li&gt;

&lt;li&gt;Break our app by minifying the javascript&lt;/li&gt;

&lt;li&gt;Understand how the fix works&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id='an_angularjs_application_using_the_github_api'&gt;An AngularJS application using the GitHub API&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;re going to build a simple AngularJS app that uses the GitHub API to find the most recent commits on the angular.js project.&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:300px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/index.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/index.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/index.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s the source for that page:&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='html'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nt'&gt;&amp;lt;html&lt;/span&gt; &lt;span class='na'&gt;ng-app&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class='nt'&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;MyController&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;$http&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;$http&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;https://api.github.com/repos/angular/angular.js/commits&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;success&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;commits&lt;/span&gt;
      &lt;span class='p'&gt;})&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class='nt'&gt;&amp;lt;body&lt;/span&gt; &lt;span class='na'&gt;ng-controller=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;MyController&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Recent commits to AngularJS&lt;span class='nt'&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;li&lt;/span&gt; &lt;span class='na'&gt;ng-repeat=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;commit in commits&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      {{ commit.commit.committer.date | date }}
      &lt;span class='nt'&gt;&amp;lt;a&lt;/span&gt; &lt;span class='na'&gt;ng-href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;https://github.com/angular/angular.js/commit/{{commit.sha}}&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;{{ commit.sha }}&lt;span class='nt'&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      {{ commit.commit.message }}
    &lt;span class='nt'&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Let&amp;#8217;s walk through what&amp;#8217;s going on here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Line 1 - The &lt;code&gt;ng-app&lt;/code&gt; attribute tells angular to treat this page as an angular app.&lt;/li&gt;

&lt;li&gt;Line 6-11 - Some JavaScript that defines the controller and tell angular to &amp;#8220;inject&amp;#8221; the &lt;code&gt;$scope&lt;/code&gt; and &lt;code&gt;$http&lt;/code&gt; services.&lt;/li&gt;

&lt;li&gt;Line 15 - The &lt;code&gt;ng-controller=&amp;quot;MyController&amp;quot;&lt;/code&gt; attribute tells angular the &lt;code&gt;body&lt;/code&gt; tag will be scoped by the &lt;code&gt;MyController&lt;/code&gt; controller.&lt;/li&gt;

&lt;li&gt;Line 18-22 - This section will be expanded to many &lt;code&gt;li&lt;/code&gt; elements in the DOM, each containing information about one commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is pretty cool. Of course not everyone is a fan, in &lt;a href='http://david.heinemeierhansson.com/2012/dependency-injection-is-not-a-virtue.html'&gt;Dependency injection is not a virtue&lt;/a&gt; DHH argues that it is a legacy of the Java language that is unnecessary in Ruby (and I suspect he would argue JavaScript). AngularJS is figuring out what the hash part of the url is and automatically inserting it in the page.&lt;/p&gt;

&lt;h2 id='dependency_injection_is_magical'&gt;Dependency Injection is magical&lt;/h2&gt;

&lt;p&gt;So where does dependency injection come in? Here&amp;#8217;s where it gets weird&amp;#8230; let&amp;#8217;s try reordering the arguments in the controller function.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;MyController&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$http&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$http&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;https://api.github.com/repos/angular/angular.js/commits&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;success&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;commits&lt;/span&gt;
    &lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We changed &lt;code&gt;function($scope, $http)&lt;/code&gt; to &lt;code&gt;function($http, $scope)&lt;/code&gt; and surprisingly it still works!&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:300px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/args_swapped.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/args_swapped.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/args_swapped.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;What seems to be going on as it runs is AngularJS&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Knows what services our controller function needs in each parameter slot (originally &lt;code&gt;$scope&lt;/code&gt; first &amp;amp; &lt;code&gt;$http&lt;/code&gt; second now &lt;code&gt;$http&lt;/code&gt; first &amp;amp; &lt;code&gt;$scope&lt;/code&gt; second).&lt;/li&gt;

&lt;li&gt;Decides what object should &amp;#8220;provide&amp;#8221; each of the named services (eg. &lt;a href='http://docs.angularjs.org/api/ng.$httpProvider'&gt;$httpProvider&lt;/a&gt; provides &lt;code&gt;$http&lt;/code&gt;).&lt;/li&gt;

&lt;li&gt;Calls our controller with the appropriate providers in each slot (either &lt;code&gt;MyController(scope, $httpProvider)&lt;/code&gt; or &lt;code&gt;MyController(scope, $httpProvider)&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How does Angular do step #1?&lt;/p&gt;

&lt;p&gt;In JavaScript the order of the parameters is important and the names do not matter to the caller (see this &lt;a href='http://egghead.io/video/faq-scope-vs-scope/'&gt;egghead.io video: $scope vs. scope&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s take a look at some straight JavaScript and convince ourselves this is true. If we define a function divide that takes two arguments&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;divide&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;numerator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;denominator&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;numerator&lt;/span&gt; &lt;span class='o'&gt;/&lt;/span&gt; &lt;span class='nx'&gt;denominator&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As expected &lt;code&gt;divide(1, 2) == 0.5&lt;/code&gt;.&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:120px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/divide.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/divide.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/divide.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;When we change the order of the parameters, we also change the definition of the function.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;divide&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;denominator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;numerator&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;numerator&lt;/span&gt; &lt;span class='o'&gt;/&lt;/span&gt; &lt;span class='nx'&gt;denominator&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the definition has changed and &lt;code&gt;divide(1, 2) == 2&lt;/code&gt;&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:120px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/divide_args_swapped.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/divide_args_swapped.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/divide_args_swapped.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;It seems Angular is going above and beyond what JavaScript the language supports.&lt;/p&gt;

&lt;h2 id='how_dependency_injection_implements_named_parameters_in_javascript'&gt;How Dependency Injection implements Named Parameters in JavaScript&lt;/h2&gt;

&lt;p&gt;We saw that Angular&amp;#8217;s dependency injection relies on the name of the parameters not their order which is a language feature called &lt;a href='http://en.wikipedia.org/wiki/Named_parameter'&gt;named parameters&lt;/a&gt; that does not exist in JavaScript. How do they do it?&lt;/p&gt;

&lt;p&gt;AngularJS makes clever use of the fact that every object in JavaScript has a &lt;code&gt;toString&lt;/code&gt; function to parse and extract the names of the parameters before deciding what arguments to call your controller function with.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s play around with &lt;code&gt;toString&lt;/code&gt; and get a feel of how it works. On function objects it returns the source code definition of the object, including the function signature with the names of the parameters.&lt;/p&gt;

&lt;p&gt;When we call it on our &lt;code&gt;divide&lt;/code&gt; function we can see this in action, &lt;code&gt;divide.toString() == &amp;quot;function (numerator, denominator) {
   return numerator / denominator;
}&amp;quot;
&lt;/code&gt;&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:120px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/divide_toString.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/divide_toString.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/divide_toString.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Angular takes this to the next level in a function called &lt;code&gt;annotate&lt;/code&gt; in &lt;a href='https://github.com/angular/angular.js/blob/master/src/auto/injector.js#L53-61'&gt;injector.js&lt;/a&gt; that takes a function and returns the names of its parameters.&lt;/p&gt;
&lt;table class='highlighttable'&gt;&lt;tr&gt;&lt;td class='linenos'&gt;&lt;div class='linenodiv'&gt;&lt;pre&gt;&lt;code class='javascript'&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nx'&gt;$inject&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[];&lt;/span&gt;
&lt;span class='nx'&gt;fnText&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;toString&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;replace&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;STRIP_COMMENTS&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='nx'&gt;argDecl&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;fnText&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;match&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;FN_ARGS&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='nx'&gt;forEach&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;argDecl&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;FN_ARG_SPLIT&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;arg&lt;/span&gt;&lt;span class='p'&gt;){&lt;/span&gt;
  &lt;span class='nx'&gt;arg&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;replace&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;FN_ARG&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;all&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;underscore&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;){&lt;/span&gt;
    &lt;span class='nx'&gt;$inject&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;push&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;$inject&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$inject&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='c1'&gt;// ...&lt;/span&gt;
&lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;$inject&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;line 2 - use the &lt;code&gt;toString()&lt;/code&gt; trick to get the function definition.&lt;/li&gt;

&lt;li&gt;line 3 - do regular expression pattern matching &lt;code&gt;FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m&lt;/code&gt; to find the function signature.&lt;/li&gt;

&lt;li&gt;line 4-8 - loop through all the parameters and save their names in an array.&lt;/li&gt;

&lt;li&gt;line 11 - return the array or parameter names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can see this in action with our trusty &lt;code&gt;divide&lt;/code&gt; function, &lt;code&gt;angular.injector().annotate(divide) == [&amp;quot;numerator&amp;quot;, &amp;quot;denominator&amp;quot;]&lt;/code&gt;&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:120px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/divide_annotate.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/divide_annotate.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/divide_annotate.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id='minification_breaks_our_app'&gt;Minification breaks our App&lt;/h2&gt;

&lt;p&gt;The technique of using &lt;code&gt;toString&lt;/code&gt; has a big problem. We often minify our javascript before sending it to the browser, in my case the Rails asset pipeline was configured to automatically do this in the production environment.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='c1'&gt;// whitespace re-added for readability&lt;/span&gt;
&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;MyController&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;e&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='nx'&gt;t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;t&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;https://api.github.com/repos/angular/angular.js/commits&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;success&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;e&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='nx'&gt;t&lt;/span&gt;
    &lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When minified the parameter names are mangled to &lt;code&gt;e&lt;/code&gt; or &lt;code&gt;t&lt;/code&gt; so cannot be mapped to service names by angular. How can it know that &lt;code&gt;t&lt;/code&gt; should be implemented by the &lt;code&gt;$httpProvider&lt;/code&gt; or that &lt;code&gt;e&lt;/code&gt; is the &lt;code&gt;$scope&lt;/code&gt;?&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:120px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/minified_broken.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/minified_broken.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/minified_broken.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;In fact if you open the &lt;a href='/examples/angularjs_dependency_injection/minified_broken.html'&gt;minified_broken.html&lt;/a&gt; example and look at the console you will see the error &lt;code&gt;Error: Unknown provider: eProvider &amp;lt;- e&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Angular&amp;#8217;s &lt;code&gt;annotate&lt;/code&gt; function is sophisticated enough to handle minification, in fact, I only showed part of it before. When we look at the whole thing, we see it can annotate a function &lt;em&gt;or&lt;/em&gt; an array of parameter name strings followed by a function. In the array form, the first string names the first argument, the second string names the second, etc. and the actual names of the parameters are unimportant.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;function&lt;/span&gt; &lt;span class='nx'&gt;annotate&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;$inject&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;fnText&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;argDecl&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;last&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;typeof&lt;/span&gt; &lt;span class='nx'&gt;fn&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;function&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$inject&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;$inject&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='c1'&gt;// omitting the code we saw before&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;isArray&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;last&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;length&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='nx'&gt;assertArgFn&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;last&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;fn&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;$inject&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;slice&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;last&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;assertArgFn&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;fn&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;fn&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;$inject&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When we can now redefine our &lt;code&gt;MyController&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;MyController&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;$scope&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;$http&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;$http&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$http&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;https://api.github.com/repos/angular/angular.js/commits&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;success&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;commits&lt;/span&gt;
    &lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='p'&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When it is minified the strings are not touched so &lt;code&gt;annotate&lt;/code&gt; will still work.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;MyController&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;$scope&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;$http&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;e&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='nx'&gt;t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;t&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;https://api.github.com/repos/angular/angular.js/commits&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;success&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;e&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;commits&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='nx'&gt;t&lt;/span&gt;
    &lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='p'&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can see the &lt;code&gt;annotate&lt;/code&gt; function parsing this array &lt;code&gt;angular.injector().annotate(MyController) == [&amp;quot;$scope&amp;quot;, &amp;quot;$http&amp;quot;]&lt;/code&gt;&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:140px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/minified_annotate.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/minified_annotate.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/minified_annotate.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Of course what really matters is that it works on the page - which it does.&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:300px; width:80%;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/angularjs_dependency_injection/minified_working.html' target='_blank'&gt;http://alexrothenberg.github.com/examples/angularjs_dependency_injection/minified_working.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/angularjs_dependency_injection/minified_working.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;ve made it to the end of this long post, I hope you&amp;#8217;ve enjoyed the journey into how AngularJS knows what services to inject in your controllers. Again all you really need to know is to use an array naming the parameters followed by your function but I hope you enjoyed learning how it actually works. I know I did!&lt;/p&gt;
  &lt;/div&gt;

  &lt;div id="disqus_thread"&gt;&lt;/div&gt;
  &lt;script type="text/javascript" src="http://alexrothenberg.disqus.com/embed.js"&gt; &lt;/script&gt;
  &lt;noscript&gt;Please enable JavaScript to &lt;a href="http://alexrothenberg.disqus.com/?url=ref"&gt;view the discussion thread.&lt;/a&gt;&lt;/noscript&gt;
&lt;/div&gt;
  &lt;/div&gt;

  &lt;div id='footer'&gt;
    Copyright &amp;copy; 2008-2013 Alex Rothenberg. Some rights reserved.
  &lt;/div&gt;

&lt;/div&gt;

&lt;!-- Google Analytics --&gt;
&lt;script type="text/javascript"&gt;
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-5273122-1']);
  _gaq.push(['_setSiteSpeedSampleRate', 100]);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
&lt;/script&gt;
&lt;!-- Google Analytics end --&gt;


&lt;/body&gt;
&lt;/html&gt;
&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/x0lg7LwQnmg" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2013/02/11/the-magic-behind-angularjs-dependency-injection.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Combining D3 and Ember to Build Interactive Maps</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/usYtz46sv00/interactive-maps-with-d3-and-ember.html" />
   <published>2012-05-30T00:00:00-07:00</published>
   <updated>2012-05-30T00:00:00-07:00</updated>
   <id>http://www.alexrothenberg.com/2012/05/30/interactive-maps-with-d3-and-ember</id>
   <content type="html">&lt;img class='heading_image' src='http://upload.wikimedia.org/wikipedia/commons/thumb/a/ad/Simple2008PresElections-USA-states.png/120px-Simple2008PresElections-USA-states.png' /&gt;
&lt;p&gt;The Javascript world is exploding with new libraries that let us build really interactive applications. The image on the left is a static image showing how each US State voted in the 2008 Presidential Election - I suspect we&amp;#8217;ll be seeing a lot more of this map in the coming months!&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s look at how we can draw that map ourselves and make it interactive using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='https://developer.mozilla.org/en/SVG'&gt;SVG&lt;/a&gt; - a vector graphics format. That will let us draw an good looking map using publicly available data about the geography of each state.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://d3js.org'&gt;D3.js&lt;/a&gt; - a JavaScript library for manipulating documents based on data. Its super fast and will help us interact with the SVG map.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://emberjs.com'&gt;Ember.js&lt;/a&gt; - a JavaScript framework for creating ambitious web applications that eliminates boilerplate and provides a standard application architecture. Its going to keep us disciplined and our code organized.&lt;/li&gt;
&lt;/ul&gt;
&lt;script type='text/javascript'&gt;
  $(function() {
    $('.demo iframe').contents().find('.link_back').hide()
  })
&lt;/script&gt;
&lt;h1 id='an_outline_map_of_the_states_in_the_usa'&gt;An outline map of the States in the USA&lt;/h1&gt;

&lt;p&gt;We&amp;#8217;ll get stated building an Ember app to draw a map of the USA with each state outlined.&lt;/p&gt;

&lt;p&gt;First, we&amp;#8217;ll need to know what each State looks like. This information is available to download from &lt;a href='http://data.jquerygeo.com/usastates.json'&gt;http://data.jquerygeo.com/usastates.json&lt;/a&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;usaStatesGeoData&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;FeatureCollection&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;features&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;
   &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Feature&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;fips&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Colorado&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;abbr&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;CO&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='cm'&gt;/*...one polygon defining a rectangular shape...*/&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
   &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Feature&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;fips&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;22&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Louisiana&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;abbr&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;LA&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;&lt;span class='cm'&gt;/*...many polygons defining a very complicated coastline */&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;

   &lt;span class='c1'&gt;// all the other States are here too ...&lt;/span&gt;
  &lt;span class='p'&gt;]&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a &lt;a href='http://www.geojson.org/'&gt;GeoJSON&lt;/a&gt; format which Thomas Newton points out in his post &lt;a href='http://blog.newtonlabs.io/post/21964404793/positioning-and-scaling-maps-in-d3'&gt;Positioning and Scaling maps in D3&lt;/a&gt; is a format that works really well with D3 since it is data that can be rendered into SVG by our code. Each State&amp;#8217;s boundaries are defined in complete detail with polygons that a cartographer has drawn out for us. This works for rectangular States like Colorodo or Wyoming and amazingly also works for those with lots of coastline like Louisiana or Alaska (although the polygons are much more complicated).&lt;/p&gt;

&lt;p&gt;Now, to create an Ember app we need to create our application and we&amp;#8217;ll assign the GeoJSON data into a bindable property.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nb'&gt;window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;MapApp&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;Ember&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Application&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;ready&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;set&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;usaStatesGeoData&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;usaStatesGeoData&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we&amp;#8217;ll add some &lt;a href='http://emberjs.com/#toc_describing-your-ui-with-handlebars'&gt;handlebars&lt;/a&gt; expressions to our html page telling it to display the map as an Ember View (we&amp;#8217;ll create that in a minute).&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;text/x-handlebars&amp;#39;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
  &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nx'&gt;view&lt;/span&gt; &lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Map&lt;/span&gt; &lt;span class='nx'&gt;geoDataBinding&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;MapApp.usaStatesGeoData&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally we write the view&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Map&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;Ember&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;View&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;extend&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;didInsertElement&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;elementId&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;elementId&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;regions&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;d3&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;select&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;#&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;elementId&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;svg&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;g&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;regions&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;

    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;features&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;geoData&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;features&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='nx'&gt;regions&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;selectAll&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;#regions path&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;features&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;enter&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;insert&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;      &lt;span class='nx'&gt;path&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;stroke&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;#ccc&amp;#39;&lt;/span&gt;  &lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;fill&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;   &lt;span class='s1'&gt;&amp;#39;white&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='nx'&gt;path&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;mapW&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;height&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;mapH&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;width&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;mapXY&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;d3&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;geo&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;albersUsa&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;scale&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1000&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;translate&lt;/span&gt;&lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='nx'&gt;mapH&lt;/span&gt;&lt;span class='o'&gt;/&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='nx'&gt;mapW&lt;/span&gt;&lt;span class='o'&gt;/&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;]);&lt;/span&gt;

    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;d3&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;geo&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;path&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;projection&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;mapXY&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}.&lt;/span&gt;&lt;span class='nx'&gt;property&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Whew that is a bit of code so let&amp;#8217;s dig in. This is where we use &lt;code&gt;d3&lt;/code&gt; to create the svg that looks like a map of the USA. &lt;code&gt;d3&lt;/code&gt; (Data-Driven Documents) works by binding data to your DOM then applying tranformations to that data. In this case our data is the GeoJSON file and our tranformations turn it into an SVG map. &lt;code&gt;didInsertElement&lt;/code&gt; is called by Ember when the view is inserted into the page&amp;#8217;s DOM.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first two lines use &lt;code&gt;d3.select&lt;/code&gt; to add the &lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;g id=&amp;quot;regions&amp;quot;&amp;gt;&lt;/code&gt; elements to the page on this view&amp;#8217;s portion of the page.&lt;/li&gt;

&lt;li&gt;The remaining use &lt;code&gt;d3.selectAll.data.enter&lt;/code&gt; to loop through the features (each State) and add a &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; element.&lt;/li&gt;

&lt;li&gt;Looping through each feature (ie. State), we make use of the rest of the code and set the &lt;code&gt;d&lt;/code&gt; attribute to the value of the &lt;code&gt;path&lt;/code&gt; property. The &lt;code&gt;path&lt;/code&gt; logic scales the map and uses the &lt;code&gt;albersUsa&lt;/code&gt; projection which moves Alaska and Hawaii to the bottom left where we usually see them on maps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at code is fun but what does it look like? Below is the page we just built and the you can see that it does, in fact, look like a map of the USA with each State outlined.&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:450px; width:80%; margin: 0px auto;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/ember-d3maps/map_outline.html' target='_blank'&gt;http://www.alexrothenberg.com/examples/ember-d3maps/map_outline.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/ember-d3maps/map_outline.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h1 id='coloring_the_states_red_or_blue'&gt;Coloring the States red or blue&lt;/h1&gt;

&lt;p&gt;A red/blue map isn&amp;#8217;t much use when all the States are white so let&amp;#8217;s color them in.&lt;/p&gt;

&lt;p&gt;First we&amp;#8217;ll need some data with the election results so we know what color each State should be. There&amp;#8217;s a source from Google at &lt;a href='http://code.google.com/p/general-election-2008-data/source/browse/trunk/json/votes/2008'&gt;http://code.google.com/p/general-election-2008-data/source/browse/trunk/json/votes/2008&lt;/a&gt;. I found the format to be a little hard to work with so I processed it into a simpler json file with just the information we&amp;#8217;ll need. It looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='p'&gt;[&lt;/span&gt;
  &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Alabama&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;    &lt;span class='s2'&gt;&amp;quot;electoral&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;winner&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;McCain&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Alaska&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;     &lt;span class='s2'&gt;&amp;quot;electoral&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;winner&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;McCain&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Arizona&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;    &lt;span class='s2'&gt;&amp;quot;electoral&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;winner&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;McCain&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Arkansas&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;   &lt;span class='s2'&gt;&amp;quot;electoral&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;6&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;winner&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;McCain&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;California&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;electoral&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;55&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;winner&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Obama&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;

  &lt;span class='c1'&gt;// results for the rest of the States too...&lt;/span&gt;
&lt;span class='p'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To use it we&amp;#8217;ll save it in our Ember Application.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nb'&gt;window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;MapApp&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;Ember&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Application&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;ready&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;set&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;usaStatesGeoData&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;usaStatesGeoData&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;

    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;stateResults&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;usaPres2008Data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;stateResult&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;StateResult&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;stateResult&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;});&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;set&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;usaPres2008Results&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;  &lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;StateResults&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;content&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;stateResults&lt;/span&gt;&lt;span class='p'&gt;}));&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We use the javascript &lt;code&gt;map&lt;/code&gt; function to turn our json data into ember objects that we can put into an Ember &lt;code&gt;ArrayProxy&lt;/code&gt; object. For this example we don&amp;#8217;t really need these ember objects but in a real application we&amp;#8217;d probably get some requirements that would make them useful. For instance if we wanted the colors to update on election night as new results come in.&lt;/p&gt;

&lt;p&gt;Since we are using the &lt;code&gt;StateResult&lt;/code&gt; and &lt;code&gt;StateResults&lt;/code&gt; objects, we need to define them next.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;StateResult&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;Ember&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nb'&gt;Object&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;extend&lt;/span&gt;&lt;span class='p'&gt;({});&lt;/span&gt;

&lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;StateResults&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;Ember&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;ArrayProxy&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;extend&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;names&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;mapProperty&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;property&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;

  &lt;span class='nx'&gt;findByName&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;content&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;index&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;names&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;indexOf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;content&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;index&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Both objects are pretty simple. In fact they would both be empty except for the &lt;code&gt;findByName&lt;/code&gt; accessor we define on &lt;code&gt;StateResults&lt;/code&gt;. We&amp;#8217;ll see the need for that in a minute when we look at how the view decides whether to color a State red or blue.&lt;/p&gt;

&lt;p&gt;This takes us to the view which enhances what we had before:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Map&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;Ember&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;View&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;extend&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='c1'&gt;// the path property is unchanged&lt;/span&gt;

  &lt;span class='nx'&gt;didInsertElement&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// same code as before ...&lt;/span&gt;

    &lt;span class='nx'&gt;regions&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;selectAll&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;#regions path&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;features&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;enter&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;insert&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;      &lt;span class='nx'&gt;path&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;stroke&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;#ccc&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;attr&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;fill&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;   &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;updateFillFor&lt;/span&gt; &lt;span class='p'&gt;);&lt;/span&gt; &lt;span class='c1'&gt;// This line changed&lt;/span&gt;
  &lt;span class='p'&gt;},&lt;/span&gt;

  &lt;span class='c1'&gt;// A new function&lt;/span&gt;
  &lt;span class='nx'&gt;updateFillFor&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;d&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;stateResult&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;MapApp&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;usaPres2008Results&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;findByName&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;d&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;properties&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='k'&gt;switch&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;stateResult&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;winner&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='k'&gt;case&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Obama&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
      &lt;span class='k'&gt;case&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;McCain&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most of the view is the same, all that&amp;#8217;s different is the way we set the fill on each State (&amp;#8220;feature&amp;#8221; in d3 terms).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.attr(&amp;#39;fill&amp;#39;, this.updateFillFor);&lt;/code&gt; - Instead of setting the &amp;#8220;fill&amp;#8221; attribute to &amp;#8216;white&amp;#8217; for all States we now provide a function to make the decision differently for each State. D3 will call the function with the current State as an argument (it actually passes the D3 &amp;#8220;datum&amp;#8221; object).&lt;/li&gt;

&lt;li&gt;&lt;code&gt;updateFillFor&lt;/code&gt; - This function uses the &lt;code&gt;findByName&lt;/code&gt; function we defined in &lt;code&gt;StateResults&lt;/code&gt; to match the &amp;#8220;geographical&amp;#8221; State with the &amp;#8220;election result&amp;#8221; State. They are separate because each came from a different external json source. Once we have the stateResult object we look at the winner and know to return either &amp;#8220;red&amp;#8221; or &amp;#8220;blue&amp;#8221;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can see it all working together right here and yes it does look like all the other red/blue election maps you&amp;#8217;ve seen.&lt;/p&gt;
&lt;div class='demo' style='text-align:center; height:450px; width:80%; margin: 0px auto;'&gt;
  &lt;div class='github_link'&gt;&lt;a href='/examples/ember-d3maps/map_red_blue.html' target='_blank'&gt;http://www.alexrothenberg.com/examples/ember-d3maps/map_red_blue.html&lt;/a&gt;&lt;/div&gt;
  &lt;iframe src='/examples/ember-d3maps/map_red_blue.html' style='height:100%;width:100%'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;This is just the beginning of what you can do by combining D3.js with Ember.js. Next I plan to write about how you could add behavior like clicking to select a State and let Ember show details about that State.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/usYtz46sv00" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2012/05/30/interactive-maps-with-d3-and-ember.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Closures can provide encapsulation when Creating Objects in Javascript</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/MAk9jmFJsT4/closures-can-provide-encapsulation-when-creating-objects-in-javascript.html" />
   <published>2012-03-14T00:00:00-07:00</published>
   <updated>2012-03-14T00:00:00-07:00</updated>
   <id>http://www.alexrothenberg.com/2012/03/14/closures-can-provide-encapsulation-when-creating-objects-in-javascript</id>
   <content type="html">&lt;p&gt;Objects are funny things in Javascript. If you&amp;#8217;re coming from a language like Ruby with classical inheritance you&amp;#8217;ll probably be surprised that in Javascript&amp;#8217;s &lt;a href='http://en.wikipedia.org/wiki/Prototype-based_programming'&gt;prototype system&lt;/a&gt; there are no such things as classes. The patterns are different but we can still achieve what&amp;#8217;s important about object oriented software namely &lt;code&gt;objects that encapsulate data and behavior&lt;/code&gt;. Today I&amp;#8217;m going to show you two different ways we can create a simple database object that supports the basic CRUD operations in Javascript.&lt;/p&gt;

&lt;h1 id='object_literal'&gt;Object Literal&lt;/h1&gt;

&lt;p&gt;The simplest way to create our database is to just declare it as a singleton object with some instance data and functions. We&amp;#8217;ll use the &amp;#8220;starts with underscore&amp;#8221; naming convention to denote &amp;#8220;private&amp;#8221; data but that is just a convention and not enforced.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='p'&gt;{},&lt;/span&gt;

  &lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;object&lt;/span&gt;
  &lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='nx'&gt;update&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;object&lt;/span&gt;
  &lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='k'&gt;delete&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kc'&gt;null&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is pretty simple and if you try it out you&amp;#8217;ll see it actually works.&lt;/p&gt;
&lt;div class='github_link'&gt;Try it at &lt;a href='http://jsfiddle.net/alexrothenberg/Tn8uq/' target='_blank'&gt;http://jsfiddle.net/alexrothenberg/Tn8uq/&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='c1'&gt;// We can CREATE&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Pat&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;});&lt;/span&gt;

&lt;span class='c1'&gt;// We can READ&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// 7: Alex&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;9: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// 9: Pat&lt;/span&gt;

&lt;span class='c1'&gt;// We can UPDATE&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;update&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Alexander&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// 7: Alexander&lt;/span&gt;

&lt;span class='c1'&gt;// We can DELETE&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='k'&gt;delete&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
  &lt;span class='c1'&gt;// 7: null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The biggest downside of this approach is that there is no encapsulation. We can get at the &lt;code&gt;_data&lt;/code&gt; instance variable directly to read or change it.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;I changed your name&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id='encapsulation_via_a_closure'&gt;Encapsulation via a Closure&lt;/h1&gt;

&lt;p&gt;By wrapping our &lt;code&gt;Database&lt;/code&gt; in a closure we can encapsulate our private functions and data. The way to read this is that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database is a function that defines a closure.&lt;/li&gt;

&lt;li&gt;The variables within that closure have access to each other (i.e. &lt;code&gt;_create&lt;/code&gt; has access to &lt;code&gt;_data&lt;/code&gt;).&lt;/li&gt;

&lt;li&gt;When the &lt;code&gt;Database&lt;/code&gt; is called it returns an object that explicitly exposes 4 functions and nothing else.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;_data&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{}&lt;/span&gt;

  &lt;span class='nx'&gt;_create&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;object&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='nx'&gt;_read&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;  &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='nx'&gt;_update&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;object&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;object&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='nx'&gt;_delete&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kc'&gt;null&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;_create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;   &lt;span class='nx'&gt;_read&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;update&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;_update&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='k'&gt;delete&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;_delete&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;call&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When we test it we see that it still works.&lt;/p&gt;
&lt;div class='github_link'&gt;Try it at &lt;a href='http://jsfiddle.net/alexrothenberg/kcGLK/' target='_blank'&gt;http://jsfiddle.net/alexrothenberg/kcGLK/&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='c1'&gt;// We can CREATE&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;create&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Pat&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;});&lt;/span&gt;

&lt;span class='c1'&gt;// We can READ&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// 7: Alex&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;9: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// 9: Pat&lt;/span&gt;

&lt;span class='c1'&gt;// We can UPDATE&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;update&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nx'&gt;id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Alexander&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// 7: Alexander&lt;/span&gt;

&lt;span class='c1'&gt;// We can DELETE&lt;/span&gt;
&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='k'&gt;delete&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;7: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;read&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
  &lt;span class='c1'&gt;// 7: null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;_data&lt;/code&gt; is encapsulated and not accessible from the outside&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;Database&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;_data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='c1'&gt;// undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are other patterns for working with objects in Javascript that take advantage of Javascript&amp;#8217;s prototype system but I don&amp;#8217;t have time to go into that today &amp;#8230; perhaps in a future post.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/MAk9jmFJsT4" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2012/03/14/closures-can-provide-encapsulation-when-creating-objects-in-javascript.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Building a Browser IDE</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/mGG1b_w2Y-s/building-a-browser-ide.html" />
   <published>2012-02-29T00:00:00-08:00</published>
   <updated>2012-02-29T00:00:00-08:00</updated>
   <id>http://www.alexrothenberg.com/2012/02/29/building-a-browser-ide</id>
   <content type="html">&lt;p&gt;Its become so easy to share code examples as &lt;a href='https://gist.github.com/gists'&gt;gists&lt;/a&gt; but once you start sharing html, css or javascript you can do so much more than share static code. Browsers can run html, css and javascript so we can actually run the code we&amp;#8217;re sharing. Let&amp;#8217;s look at how we could build a simple IDE in your browser like &lt;a href='http://jsfiddle.net/'&gt;jsfiddle&lt;/a&gt; where where you can experiment with your HTML, CSS and Javascript.&lt;/p&gt;

&lt;h1 id='html_ide'&gt;HTML IDE&lt;/h1&gt;

&lt;p&gt;First we&amp;#8217;re going to build an &lt;code&gt;HTML&lt;/code&gt; editor. Try it out&amp;#8230;I&amp;#8217;ll wait.&lt;/p&gt;
&lt;style&gt;
  .demo { width:80%; margin:auto; margin-bottom:1em; }
  .demo iframe  { width: 100%; border: 5px inset; }
&lt;/style&gt;&lt;script type='text/javascript'&gt;
  $(function() {
    $('#html_ide iframe').contents().find('.link_back').hide()
    $('#html_css_ide iframe').contents().find('.link_back').hide()
    $('#html_css_js_ide iframe').contents().find('.link_back').hide()
    $('#complete_ide iframe').contents().find('.link_back').hide()
  })
&lt;/script&gt;&lt;div class='demo' id='html_ide'&gt;
  &lt;iframe src='/examples/browser_ide/html_ide.html' style='height:325px;'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;How does it work? We need three things.&lt;/p&gt;

&lt;p&gt;1 - A &lt;code&gt;textarea&lt;/code&gt; where you can type in the html&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;  &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;html&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    Hi there this is some &lt;span class='nt'&gt;&amp;lt;b&amp;gt;&lt;/span&gt;bold&lt;span class='nt'&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; content
    and this is &lt;span class='nt'&gt;&amp;lt;i&amp;gt;&lt;/span&gt;italic&lt;span class='nt'&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;.
    &lt;span class='nt'&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Pretty cool huh!&lt;span class='nt'&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;p&amp;gt;&lt;/span&gt;What else can you type?&lt;span class='nt'&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;2 - An &lt;code&gt;iframe&lt;/code&gt; to preview the page&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;preview&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;3 - Some javascript to copy the html from the text area into the div&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keypress&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#preview&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;contents&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;
&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After we add some formatting and put it all together we get a page like this.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='http://www.alexrothenberg.com/examples/browser_ide/html_ide.html'&gt;http://www.alexrothenberg.com/examples/browser_ide/html_ide.html&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;meta&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;utf-8&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Browser IDE&lt;span class='nt'&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;link&lt;/span&gt; &lt;span class='na'&gt;rel=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./styles.css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#preview&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;contents&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
      &lt;span class='p'&gt;})&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Edit the HTML and see it in the Preview&lt;span class='nt'&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;HTML&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;html&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
Hi there this is some &lt;span class='nt'&gt;&amp;lt;b&amp;gt;&lt;/span&gt;bold&lt;span class='nt'&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; content
and this is &lt;span class='nt'&gt;&amp;lt;i&amp;gt;&lt;/span&gt;italic&lt;span class='nt'&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;.
&lt;span class='nt'&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Pretty cool huh!&lt;span class='nt'&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;p&amp;gt;&lt;/span&gt;What else can you type?&lt;span class='nt'&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Preview&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;preview&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id='html_and_css_ide'&gt;HTML and CSS IDE&lt;/h1&gt;

&lt;p&gt;HTML is good but any self respecting webpage will also have a &lt;code&gt;CSS&lt;/code&gt; file. We can add that too.&lt;/p&gt;
&lt;div class='demo' id='html_css_ide'&gt;
  &lt;iframe src='/examples/browser_ide/html_css_ide.html' style='height:550px;'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;To get this working we added&lt;/p&gt;

&lt;p&gt;1 - A new &lt;code&gt;textarea&lt;/code&gt; where you can type in the css&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;  &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    .warning { color: red; }
  &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;2 - Some javascript to copy the css from the text area into the div&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;  &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keypress&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head style&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
    &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&amp;lt;style&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;/style&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='p'&gt;})&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All put together it looks like this.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='http://www.alexrothenberg.com/examples/browser_ide/html_css_ide.html'&gt;http://www.alexrothenberg.com/examples/browser_ide/html_css_ide.html&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;meta&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;utf-8&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Browser IDE&lt;span class='nt'&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;link&lt;/span&gt; &lt;span class='na'&gt;rel=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./styles.css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;preview_contents&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#preview&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;contents&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head style&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&amp;lt;style&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;/style&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
      &lt;span class='p'&gt;})&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Edit HTML and CSS&lt;span class='nt'&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;HTML&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;html&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;warning&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;This is a warning!&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;This is a message.&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;CSS&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
.warning { color: red; }
/* Try adding a .message style.  Make it bold. */
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Preview&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;preview&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id='html_css_and_javascript_ide'&gt;HTML, CSS and Javascript IDE&lt;/h1&gt;

&lt;p&gt;So the last part of the page we want to edit is &lt;code&gt;javascript&lt;/code&gt;.&lt;/p&gt;
&lt;div class='demo' id='html_css_js_ide'&gt;
  &lt;iframe src='/examples/browser_ide/html_css_js_ide.html' style='height:550px;'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;We implement this following the same pattern we&amp;#8217;ve been using.&lt;/p&gt;

&lt;p&gt;1 - A new &lt;code&gt;textarea&lt;/code&gt; where you can type in the javascript&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;  &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    // Change the next line and watch the preview change
    var new_text = &amp;#39;This text was added by js&amp;#39;

    var js_element = document.getElementById(&amp;#39;js_content&amp;#39;)
    js_element.innerHTML = new_text
  &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;2 - Some javascript to copy the css from the text area into the div&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;  &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keypress&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head script&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
    &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;created_script&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;createElement&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='nx'&gt;created_script&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;text&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
    &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;appendChild&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;created_script&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='p'&gt;})&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s the complete page.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='http://www.alexrothenberg.com/examples/browser_ide/html_css_js_ide.html'&gt;http://www.alexrothenberg.com/examples/browser_ide/html_css_js_ide.html&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;meta&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;utf-8&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Browser IDE&lt;span class='nt'&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;link&lt;/span&gt; &lt;span class='na'&gt;rel=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./styles.css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;preview_contents&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#preview&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;contents&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;
          &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;   &lt;span class='c1'&gt;// let the javascript change the page &lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head style&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&amp;lt;style&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;/style&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head script&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
          &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;created_script&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;createElement&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
          &lt;span class='nx'&gt;created_script&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;text&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;appendChild&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;created_script&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
        &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;keyup&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
      &lt;span class='p'&gt;})&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Edit HTML, CSS and Javascript&lt;span class='nt'&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;HTML&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;html&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;warning&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;This is a warning!&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;This is a message&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;js_content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;CSS&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
.warning { color: red; }
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Javascript&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
// Change the next line and watch the preview change
var new_text = &amp;#39;This text was added by js&amp;#39;

var js_element = document.getElementById(&amp;#39;js_content&amp;#39;)
js_element.innerHTML = new_text
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Preview&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;preview&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id='syntax_highlighting'&gt;Syntax highlighting&lt;/h1&gt;

&lt;p&gt;We now have a very simple IDE but its missing something all code editors have. No I don&amp;#8217;t mean &amp;#8220;save&amp;#8221;, although that is important I&amp;#8217;m going to ignore it. When I&amp;#8217;m typing text it all starts to run together and become really hard to read. It would be so much easier if we had some &lt;code&gt;syntax highlighting&lt;/code&gt;. We&amp;#8217;re going to use a javascript library called &lt;a href='http://codemirror.net'&gt;CodeMirror&lt;/a&gt; which knows how to syntax highlight &lt;code&gt;html&lt;/code&gt;, &lt;code&gt;css&lt;/code&gt;, &lt;code&gt;javascript&lt;/code&gt; and a ton of other languages.&lt;/p&gt;

&lt;p&gt;When we&amp;#8217;re done it will be much easier to read our code&lt;/p&gt;
&lt;div class='demo' id='complete_ide'&gt;
  &lt;iframe src='/examples/browser_ide/index.html' style='height:550px;'&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;#8217;ll add CodeMirror in these 3 steps.&lt;/p&gt;

&lt;p&gt;1 - First we &lt;a href='http://codemirror.net/codemirror.zip'&gt;download CodeMirror&lt;/a&gt; and save it along with our files.&lt;/p&gt;

&lt;p&gt;2 - Now we need to add it to our page&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;  &lt;span class='nt'&gt;&amp;lt;link&lt;/span&gt; &lt;span class='na'&gt;rel=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/codemirror.css&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/javascript/javascript.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/css/css.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/xml/xml.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/htmlmixed/htmlmixed.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;3 - Finally some javascript that enables CodeMirror on our three textareas. Here&amp;#8217;s the code for the &lt;code&gt;html&lt;/code&gt; pane.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;  &lt;span class='nx'&gt;CodeMirror&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;fromTextArea&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;mode&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;htmlmixed&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
    &lt;span class='nx'&gt;matchBrackets&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;onChange&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='nx'&gt;copyHTML&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getValue&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='p'&gt;})&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;copyHTML&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;html_text&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;html_text&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;copyJavascript&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;  &lt;span class='c1'&gt;// let the javascript change the page&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='nx'&gt;copyHTML&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;             &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CodeMirror actually offers a ton more functionality than just syntax highlighting like emacs or vim keybinding, matching parentheses, undo, and more that you can investigate if you&amp;#8217;re interested. When we&amp;#8217;re done with all that we have this single page app.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='http://www.alexrothenberg.com/examples/browser_ide/index.html'&gt;http://www.alexrothenberg.com/examples/browser_ide/index.html&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;meta&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;utf-8&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Browser IDE&lt;span class='nt'&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;link&lt;/span&gt; &lt;span class='na'&gt;rel=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./styles.css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;charset=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/codemirror.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;link&lt;/span&gt; &lt;span class='na'&gt;rel=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/codemirror.css&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/javascript/javascript.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/css/css.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/xml/xml.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;src=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;./codemirror/mode/htmlmixed/htmlmixed.js&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;script &lt;/span&gt;&lt;span class='na'&gt;type=&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;preview_contents&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#preview&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;contents&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
        &lt;span class='nx'&gt;CodeMirror&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;fromTextArea&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;mode&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;htmlmixed&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
          &lt;span class='nx'&gt;matchBrackets&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
          &lt;span class='nx'&gt;onChange&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='nx'&gt;copyHTML&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getValue&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;CodeMirror&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;fromTextArea&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;mode&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;css&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
          &lt;span class='nx'&gt;matchBrackets&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
          &lt;span class='nx'&gt;onChange&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='nx'&gt;copyCSS&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getValue&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='nx'&gt;CodeMirror&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;fromTextArea&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;mode&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;  &lt;span class='s2'&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
          &lt;span class='nx'&gt;matchBrackets&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kc'&gt;true&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
          &lt;span class='nx'&gt;onChange&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='nx'&gt;copyJavascript&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;editor&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getValue&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='p'&gt;})&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;copyHTML&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;html_text&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;html_text&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='nx'&gt;copyJavascript&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;  &lt;span class='c1'&gt;// let the javascript change the page&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;copyCSS&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;css_text&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head style&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&amp;lt;style&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;css_text&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;/style&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;copyJavascript&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;javascript_text&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head script&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
          &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;created_script&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;createElement&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
          &lt;span class='nx'&gt;created_script&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;text&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;javascript_text&lt;/span&gt;
          &lt;span class='nx'&gt;preview_contents&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;appendChild&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;created_script&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='nx'&gt;copyHTML&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#html&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;             &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
        &lt;span class='nx'&gt;copyCSS&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#css&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt;               &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
        &lt;span class='nx'&gt;copyJavascript&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#javascript&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;val&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt; &lt;span class='c1'&gt;// Initialize&lt;/span&gt;
      &lt;span class='p'&gt;})&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Edit HTML, CSS and Javascript with Syntax Highlighting&lt;span class='nt'&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;HTML&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;html&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;warning&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;This is a warning!&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;This is a message&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;js_content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;CSS&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;css&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
.warning { color: red; }
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Javascript&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
// Change the next line and watch the preview change
var new_text = &amp;#39;This text was added by js&amp;#39;

var js_element = document.getElementById(&amp;#39;js_content&amp;#39;)
js_element.innerHTML = new_text
      &lt;span class='nt'&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;box&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;Preview&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class='nt'&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;preview&amp;quot;&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This entire sample we&amp;#8217;ve built is on github at &lt;a href='https://github.com/alexrothenberg/alexrothenberg.github.com/tree/master/examples/browser_ide'&gt;https://github.com/alexrothenberg/alexrothenberg.github.com/tree/master/examples/browser_ide&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/mGG1b_w2Y-s" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2012/02/29/building-a-browser-ide.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Testing IP Whitelisting in your Specs and Features</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/bkEviFwwiBs/testing-ip-whitelisting-in-your-specs-and-features.html" />
   <published>2011-11-21T00:00:00-08:00</published>
   <updated>2011-11-21T00:00:00-08:00</updated>
   <id>http://www.alexrothenberg.com/2011/11/21/testing-ip-whitelisting-in-your-specs-and-features</id>
   <content type="html">&lt;p&gt;Rails has so much support for testing built into itself that its rare I come up with something that&amp;#8217;s &lt;em&gt;hard to test&lt;/em&gt; but HTTP headers is not easy. Normally you don&amp;#8217;t have to worry about HTTP headers as they&amp;#8217;re set by the browser and you don&amp;#8217;t do much with them. Recently I was working on an application where each user has an IP whitelist and they are only allowed to come from their whitelisted IP addresses. This isn&amp;#8217;t as crazy as it sounds since the app is in a corporate environment and the users will all be coming from their corporate networks.&lt;/p&gt;

&lt;p&gt;Basically this means our authentication method needs 3 pieces of information&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;username&lt;/li&gt;

&lt;li&gt;password&lt;/li&gt;

&lt;li&gt;remote ip address&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What makes this interesting is that the first two are input by the user but the ip address comes from the browser and network. Writing an RSpec unit test or Cucumber scenario to test user parameters (username and password) is something we&amp;#8217;ve all done before but today I&amp;#8217;m going to talk about how you can also test the IP address in a header.&lt;/p&gt;

&lt;h2 id='implementation'&gt;Implementation&lt;/h2&gt;

&lt;p&gt;Before we look at how to test this let&amp;#8217;s take a look at the implementation of our &lt;code&gt;SessionController&lt;/code&gt;.&lt;/p&gt;
&lt;div class='github_link'&gt;app/controllers/sessions_controller.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;SessionsController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;new&lt;/span&gt;
    &lt;span class='vi'&gt;@session&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Session&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;create&lt;/span&gt;
    &lt;span class='n'&gt;remote_ip_address&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;X-Forwarded-For&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;REMOTE_ADDR&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='vi'&gt;@session&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Session&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:password&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;remote_ip_address&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='vi'&gt;@session&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;valid?&lt;/span&gt;
      &lt;span class='n'&gt;session&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:current_user&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@session&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;user&lt;/span&gt;
      &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;root_url&lt;/span&gt;
    &lt;span class='k'&gt;else&lt;/span&gt;
      &lt;span class='n'&gt;flash&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;now&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:error&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Unable to authenticate. Please try again&amp;#39;&lt;/span&gt;
      &lt;span class='n'&gt;render&lt;/span&gt; &lt;span class='ss'&gt;:new&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;destroy&lt;/span&gt;
    &lt;span class='n'&gt;session&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:current_user&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
    &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;session&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These three actions provide login and logout.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new&lt;/code&gt; displays the login form with username &amp;amp; password fields&lt;/li&gt;

&lt;li&gt;&lt;code&gt;create&lt;/code&gt; uses the username and password from the form as well as the ip address to create a session (i.e. authenticate). In case the request hops through some proxy servers we use the &lt;a href='http://en.wikipedia.org/wiki/X-Forwarded-For'&gt;X-Forwarded-For&lt;/a&gt; header to get the source IP and not the proxy&amp;#8217;s IP.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;destroy&lt;/code&gt; users need to log out (but we wont talk about that anymore here)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works, but you shouldn&amp;#8217;t trust me. We need tests around the &lt;code&gt;create&lt;/code&gt; action!&lt;/p&gt;

&lt;h2 id='unit_testing_the_ip_whitelist_with_rspec'&gt;Unit Testing the IP Whitelist with RSpec&lt;/h2&gt;

&lt;p&gt;Our Controller Spec needs to pass all 3 pieces of information (username, password &amp;amp; ip address) to the controller. Passing the username and password is pretty standard and something I&amp;#8217;m sure you&amp;#8217;ve done before. They come from a form so we pass them as a hash in the second argument to post.&lt;/p&gt;
&lt;div class='github_link'&gt;spec/controllers/sessions_controller_spec.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately we can&amp;#8217;t pass the IP the same way because the post method in ActionController::TestCase doesn&amp;#8217;t support passing headers in (but it does take the &lt;em&gt;session&lt;/em&gt; or &lt;em&gt;flash&lt;/em&gt; - that&amp;#8217;s interesting to remember for some other time).&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='https://github.com/rails/rails/blob/v3.1.2/actionpack/lib/action_controller/test_case.rb#L368-371'&gt;actionpack/lib/action_controller/test_case.rb&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;post&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;action&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;parameters&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;session&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;flash&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;process&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;action&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;parameters&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;session&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;flash&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we keep looking around it turns out the ActionDispatch::TestRequest object has a nice convenience method that lets us specify the remote_addr directly.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='https://github.com/rails/rails/blob/v3.1.2/actionpack/lib/action_dispatch/testing/test_request.rb#L61-63'&gt;actionpack/lib/action_dispatch/testing/test_request.rb&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;remote_addr&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;addr&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='vi'&gt;@env&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;REMOTE_ADDR&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;addr&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we add a line to our spec we can handle the case where the IP comes in the &lt;em&gt;REMOTE_ADDR&lt;/em&gt; HTTP header.&lt;/p&gt;
&lt;div class='github_link'&gt;spec/controllers/sessions_controller_spec.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;remote_addr&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We still need to deal with the X-Forwarded-For case. While Rails doesn&amp;#8217;t give us a convenience method, by looking at the implementation of the remote_addr= method we can see how to set this header ourselves.&lt;/p&gt;
&lt;div class='github_link'&gt;spec/controllers/sessions_controller_spec.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;X-Forwarded-For&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;
&lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Putting it all together we end up with a controller spec that looks like this.&lt;/p&gt;
&lt;div class='github_link'&gt;spec/controllers/sessions_controller_spec.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec_helper&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;SessionsController&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;#create&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;successfully&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:alex&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:valid_session&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:valid?&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:user&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;alex&lt;/span&gt; &lt;span class='p'&gt;)}&lt;/span&gt;
      &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='no'&gt;Session&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;valid_session&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;using REMOTE_ADDR&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;remote_addr&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;
          &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;set_session&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:current_user&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;alex&lt;/span&gt;&lt;span class='p'&gt;)}&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;using X-Forwarded-For&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;remote_addr&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;172.16.254.1&amp;#39;&lt;/span&gt;
          &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;X-Forwarded-For&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;
          &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;set_session&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:current_user&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;alex&lt;/span&gt;&lt;span class='p'&gt;)}&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;unsuccessfully&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:invalid_session&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:valid?&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='no'&gt;Session&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;invalid_session&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;using REMOTE_ADDR&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;remote_addr&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;
          &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;render_template&lt;/span&gt; &lt;span class='ss'&gt;:new&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;using X-Forwarded-For&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
        &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;remote_addr&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;172.16.254.1&amp;#39;&lt;/span&gt;
          &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;X-Forwarded-For&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;192.168.1.100&amp;#39;&lt;/span&gt;
          &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:username&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;render_template&lt;/span&gt; &lt;span class='ss'&gt;:new&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To sum up we can&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;pass parameters as a hash in the post method&lt;/p&gt;

&lt;p&gt;&lt;code&gt;post :create, {:username =&amp;gt; &amp;#39;alex&amp;#39;, :password =&amp;gt; &amp;#39;secret&amp;#39;}&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;set the remote_addr on the request with a convenience method&lt;/p&gt;

&lt;p&gt;&lt;code&gt;request.remote_addr = &amp;#39;192.168.1.100&amp;#39;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;et the X-Forwarded-For directly on the requests&amp;#8217;s environment hash&lt;/p&gt;

&lt;p&gt;&lt;code&gt;request.env[&amp;#39;X-Forwarded-For&amp;#39;] = &amp;#39;192.168.1.100&amp;#39;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='integration_testing_the_ip_whitelist_in_a_cucumber_feature'&gt;Integration Testing the IP Whitelist in a Cucumber Feature&lt;/h2&gt;

&lt;p&gt;We face a similar issue when writing our cucumber scenarios - its easy to pass the username and password but harder to pass the IP address. The solution turns out to be similar but not quite exactly the because our Cucumber steps will use Capybara instead of ActionController::TestCase directly. Before we look into how to implement the steps, let&amp;#8217;s write the feature we want which will help us define the steps we need.&lt;/p&gt;
&lt;div class='github_link'&gt;features/authentication.feature&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='gherkin'&gt;&lt;span class='k'&gt;Feature:&lt;/span&gt;&lt;span class='nf'&gt; Authentication of a user&lt;/span&gt;
&lt;span class='nf'&gt;  In order to ensure a really secure application&lt;/span&gt;
&lt;span class='nf'&gt;  As a user&lt;/span&gt;
&lt;span class='nf'&gt;  I want my IP address to be validated during login&lt;/span&gt;

&lt;span class='nf'&gt;  &lt;/span&gt;&lt;span class='k'&gt;Background:&lt;/span&gt;&lt;span class='nf' /&gt;
&lt;span class='k'&gt;    Given &lt;/span&gt;&lt;span class='nf'&gt;the following user exists:&lt;/span&gt;
&lt;span class='k'&gt;      |&lt;/span&gt;&lt;span class='s'&gt; username&lt;/span&gt;&lt;span class='k'&gt; |&lt;/span&gt;&lt;span class='s'&gt; password&lt;/span&gt;&lt;span class='k'&gt; |&lt;/span&gt;&lt;span class='s'&gt; company&lt;/span&gt;&lt;span class='k'&gt;                   |&lt;/span&gt;&lt;span class='nf' /&gt;
&lt;span class='k'&gt;      |&lt;/span&gt;&lt;span class='s'&gt; alex&lt;/span&gt;&lt;span class='k'&gt;     |&lt;/span&gt;&lt;span class='s'&gt; secret&lt;/span&gt;&lt;span class='k'&gt;   |&lt;/span&gt;&lt;span class='s'&gt; ip_address: 192.168.1.100&lt;/span&gt;&lt;span class='k'&gt; |&lt;/span&gt;

&lt;span class='nf'&gt;  &lt;/span&gt;&lt;span class='k'&gt;Scenario:&lt;/span&gt;&lt;span class='nf'&gt; Successful log in&lt;/span&gt;
&lt;span class='k'&gt;    Given &lt;/span&gt;&lt;span class='nf'&gt;I am connecting from ip &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;192.168.1.100&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;When &lt;/span&gt;&lt;span class='nf'&gt;I log in as &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;alex&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; with password &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;secret&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;Then &lt;/span&gt;&lt;span class='nf'&gt;I should be on the home page&lt;/span&gt;

&lt;span class='nf'&gt;  &lt;/span&gt;&lt;span class='k'&gt;Scenario:&lt;/span&gt;&lt;span class='nf'&gt; Successful log in with X-Forwarded-For header&lt;/span&gt;
&lt;span class='k'&gt;    Given &lt;/span&gt;&lt;span class='nf'&gt;I am connecting from ip &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;192.168.1.100&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; behind a proxy&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;When &lt;/span&gt;&lt;span class='nf'&gt;I log in as &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;alex&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; with password &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;secret&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;Then &lt;/span&gt;&lt;span class='nf'&gt;I should be on the home page&lt;/span&gt;

&lt;span class='nf'&gt;  &lt;/span&gt;&lt;span class='k'&gt;Scenario:&lt;/span&gt;&lt;span class='nf'&gt; Failed log in from wrong IP&lt;/span&gt;
&lt;span class='k'&gt;    Given &lt;/span&gt;&lt;span class='nf'&gt;I am connecting from ip &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;172.16.254.1&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;When &lt;/span&gt;&lt;span class='nf'&gt;I log in as &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;alex&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; with password &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;secret&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;Then &lt;/span&gt;&lt;span class='nf'&gt;authentication should have failed&lt;/span&gt;

&lt;span class='nf'&gt;  &lt;/span&gt;&lt;span class='k'&gt;Scenario:&lt;/span&gt;&lt;span class='nf'&gt; Failed log in from wrong IP behind a proxy&lt;/span&gt;
&lt;span class='k'&gt;    Given &lt;/span&gt;&lt;span class='nf'&gt;I am connecting from ip &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;172.16.254.1&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; behind a proxy&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;When &lt;/span&gt;&lt;span class='nf'&gt;I log in as &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;alex&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; with password &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;secret&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;Then &lt;/span&gt;&lt;span class='nf'&gt;authentication should have failed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We immediately realize we don&amp;#8217;t know how to write the first step&lt;/p&gt;
&lt;div class='github_link'&gt;features/step_definitions/authentication_steps.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sr'&gt;/^I am connecting from ip &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;pending&lt;/span&gt; &lt;span class='c1'&gt;# How do we set the IP Address???&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To figure this out we need to dig into how capybara works.&lt;/p&gt;

&lt;p&gt;We don&amp;#8217;t call &lt;code&gt;post&lt;/code&gt; in ActionController::TestCase directly instead letting capybara do it for us. To see what capybara is doing we can skip that step and implement the login step&lt;/p&gt;
&lt;div class='github_link'&gt;features/step_definitions/authentication_steps.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sr'&gt;/^I am connecting from ip &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='c1'&gt;# do nothing for now&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;When&lt;/span&gt; &lt;span class='sr'&gt;/^I log in as &amp;quot;([^&amp;quot;]*)&amp;quot; with password &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;password&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;visit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;new_session_path&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;fill_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;User name&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;fill_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Password&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;password&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;click_button&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Log In&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and edit the &lt;em&gt;SessionsController&lt;/em&gt; to show us the stack trace.&lt;/p&gt;
&lt;div class='github_link'&gt;app/controllers/sessions_controller.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;SessionsController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;create&lt;/span&gt;
    &lt;span class='k'&gt;raise&lt;/span&gt; &lt;span class='nb'&gt;caller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;inspect&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The stack trace is very big but if we look closely, somewhere in the middle of it we see lines below that show how capybara uses the &lt;a href='https://github.com/brynary/rack-test'&gt;rack-test gem&lt;/a&gt; to submit our form.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;~/.rvm/gems/ruby-1.8.7-p334/gems/rack-test-0.6.1/lib/rack/test.rb:66:in `post&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;~/.rvm/gems/ruby-1.8.7-p334/gems/capybara-1.1.2/lib/capybara/rack_test/browser.rb:62:in `send&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;~/.rvm/gems/ruby-1.8.7-p334/gems/capybara-1.1.2/lib/capybara/rack_test/browser.rb:62:in `process&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;~/.rvm/gems/ruby-1.8.7-p334/gems/capybara-1.1.2/lib/capybara/rack_test/browser.rb:27:in `submit&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;~/.rvm/gems/ruby-1.8.7-p334/gems/capybara-1.1.2/lib/capybara/rack_test/form.rb:64:in `submit&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;... more lines omitted...&lt;/span&gt;
&lt;span class='go'&gt;~/.rvm/gems/ruby-1.8.7-p334/gems/capybara-1.1.2/lib/capybara/node/actions.rb:38:in `click_button&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Looking at the Rack::Test#post method we see something similar to what we saw before in &lt;a href='https://github.com/rails/rails/blob/v3.1.2/actionpack/lib/action_controller/test_case.rb#L368-371'&gt;ActionController::TestCase&lt;/a&gt; but its not quite identical. It takes the &lt;em&gt;env&lt;/em&gt; as a parameter so we need to figure out how to inject our header in there.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='https://github.com/brynary/rack-test/blob/v0.6.1/lib/rack/test.rb#L60-67'&gt;rack-test - lib/rack/test.rb&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;post&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;params&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{},&lt;/span&gt; &lt;span class='n'&gt;env&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{},&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;env&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;env_for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;merge&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:method&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:params&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
  &lt;span class='n'&gt;process_request&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Following the stack trace up we see the &lt;code&gt;env&lt;/code&gt; passed into &lt;code&gt;Rack::Test::Session.post&lt;/code&gt; comes from &lt;code&gt;Capybara::RackTest::Browser&lt;/code&gt; and it turns out that env is computed in the Capybara::RackTest::Browser#env method.&lt;/p&gt;
&lt;div class='github_link'&gt;&lt;a href='https://github.com/jnicklas/capybara/blob/1.1.2/lib/capybara/rack_test/browser.rb#L110-119'&gt;capybara - lib/capybara/rack_test/browser.rb&lt;/a&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;options&lt;/span&gt;
  &lt;span class='n'&gt;driver&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;options&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;env&lt;/span&gt;
  &lt;span class='n'&gt;env&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{}&lt;/span&gt;
  &lt;span class='k'&gt;begin&lt;/span&gt;
    &lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;HTTP_REFERER&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;last_request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;url&lt;/span&gt;
  &lt;span class='k'&gt;rescue&lt;/span&gt; &lt;span class='ss'&gt;Rack&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Test&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Error&lt;/span&gt;
    &lt;span class='c1'&gt;# no request yet&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;merge!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:headers&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:headers&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='n'&gt;env&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key is in the line &lt;code&gt;env.merge!(options[:headers]) if options[:headers]&lt;/code&gt; and those options are delegated to the driver. Now we know how to inject our IP address onto the driver&amp;#8217;s options.&lt;/p&gt;
&lt;div class='github_link'&gt;features/step_definitions/authentication_steps.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sr'&gt;/^I am connecting from ip &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;page&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;driver&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:headers&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;REMOTE_ADDR&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Putting it all together we can write all our steps&lt;/p&gt;
&lt;div class='github_link'&gt;features/step_definitions/authentication_steps.rb&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sr'&gt;/^I am connecting from ip &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;page&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;driver&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:headers&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;REMOTE_ADDR&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sr'&gt;/^I am connecting from ip &amp;quot;([^&amp;quot;]*)&amp;quot; behind a proxy$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;page&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;driver&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:headers&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;X-Forwarded-For&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;ip_address&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;When&lt;/span&gt; &lt;span class='sr'&gt;/^I log in as &amp;quot;([^&amp;quot;]*)&amp;quot; with password &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;password&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;visit&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;new_session_path&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;fill_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;User name&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;fill_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Password&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;password&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;click_button&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Log In&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;Then&lt;/span&gt; &lt;span class='sr'&gt;/^I should be on the home page$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='no'&gt;URI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;current_url&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;Then&lt;/span&gt; &lt;span class='sr'&gt;/^authentication should have failed$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;page&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Unable to authenticate. Please try again&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the scenarios we wrote before all pass.&lt;/p&gt;

&lt;p&gt;To sum up&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;capybara handles form submission superbly with&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fill_in(&amp;#39;User name&amp;#39;, :with =&amp;gt; name)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;click_button(&amp;#39;Log In&amp;#39;)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;we can set any HTTP header in capybara with&lt;/p&gt;

&lt;p&gt;&lt;code&gt;page.driver.options[:headers] = {&amp;#39;REMOTE_ADDR&amp;#39; =&amp;gt; ip_address}&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='testing_is_good'&gt;Testing is good&lt;/h2&gt;

&lt;p&gt;Since we&amp;#8217;re testing the IP logic at both the unit level with RSpec and integration level with Cucumber and Capybara we can be pretty sure it&amp;#8217;s all going to work correctly.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/bkEviFwwiBs" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2011/11/21/testing-ip-whitelisting-in-your-specs-and-features.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Programming With Kids</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/52QixCSdKco/programming-with-kids.html" />
   <published>2011-11-14T00:00:00-08:00</published>
   <updated>2011-11-14T00:00:00-08:00</updated>
   <id>http://www.alexrothenberg.com/2011/11/14/programming-with-kids</id>
   <content type="html">&lt;p&gt;I&amp;#8217;ve started to teach my kids to program. I figured I build websites professionally and it&amp;#8217;d be a fun way for me to share what I do and help supplement their learning. And it was something they expressed interest in not something I was pushing. I suggested we build our own small version of facebook or twitter. Very quickly I learned two truths&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;websites are boring&lt;/li&gt;

&lt;li&gt;games are fun&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Okay. I&amp;#8217;ve never built a game before after a little digging there are plenty of tools in the open source world and many built on Ruby. We&amp;#8217;re currently experimenting with three different tools/technologies.&lt;/p&gt;

&lt;h2 id='shoes'&gt;Shoes&lt;/h2&gt;

&lt;p&gt;I first came across &lt;a href='http://shoesrb.com/'&gt;Shoes&lt;/a&gt; several years ago and was blown away. It was originally written by &lt;em&gt;why&lt;/em&gt; and is now maintained by Team Shoes &lt;a href='https://github.com/shoes/shoes'&gt;on github&lt;/a&gt;. &amp;#8220;Shoes is the best little DSL for cross-platform GUI programming there is. It feels like real Ruby, rather than just another C++ library wrapper&amp;#8221;&lt;/p&gt;

&lt;p&gt;Writing a Shoes app feels just like writing a Ruby app (it is Ruby!). The best analogy I can use is that what Rails does for websites, Shoes does for GUI apps.&lt;/p&gt;

&lt;p&gt;If you want to create a blue rectangle on a page, here&amp;#8217;s your app&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Shoes&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;app&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;fill&lt;/span&gt; &lt;span class='n'&gt;blue&lt;/span&gt;
  &lt;span class='n'&gt;rect&lt;/span&gt; &lt;span class='ss'&gt;:top&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;25&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:left&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;50&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:height&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;75&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:width&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;150&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/shoes_a_blue_rectangle.png' alt='A blue rectangle' /&gt;&lt;/p&gt;

&lt;p&gt;We can make it a bit more interactive and allow the user to move our rectangle around with the arrow keys and display the current coordinates&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Shoes&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;app&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;fill&lt;/span&gt; &lt;span class='n'&gt;blue&lt;/span&gt;
  &lt;span class='vi'&gt;@player&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;rect&lt;/span&gt; &lt;span class='ss'&gt;:top&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;25&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:left&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;50&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:height&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;75&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:width&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;150&lt;/span&gt;
  &lt;span class='vi'&gt;@current_coordinates&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;para&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;(&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;, &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;)&amp;quot;&lt;/span&gt;

  &lt;span class='n'&gt;keypress&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:right&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt; &lt;span class='o'&gt;-=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:left&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;  &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:down&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;  &lt;span class='o'&gt;-=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:up&lt;/span&gt;
    &lt;span class='vi'&gt;@current_coordinates&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;replace&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;(&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;, &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;)&amp;quot;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/shoes_the_blue_rectangle_moves.png' alt='The Blue Rectangle Moves' /&gt;&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re not limited to blue rectangles. We can replace it with an image&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Shoes&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;app&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='vi'&gt;@player&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;image&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;images/Starfighter.png&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:top&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;25&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:left&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;50&lt;/span&gt;
  &lt;span class='vi'&gt;@current_coordinates&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;para&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;(&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;, &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;)&amp;quot;&lt;/span&gt;

  &lt;span class='n'&gt;keypress&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:right&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt; &lt;span class='o'&gt;-=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:left&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;  &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:down&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;  &lt;span class='o'&gt;-=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:up&lt;/span&gt;
    &lt;span class='vi'&gt;@current_coordinates&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;replace&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;(&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;, &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;top&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;)&amp;quot;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/shoes_starfighter_player.png' alt='The Player is a Starship' /&gt;&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re writing Ruby in a fairly natural way. Shoes just gives us GUI methods like &lt;code&gt;rect&lt;/code&gt; to create a rectangle or &lt;code&gt;para&lt;/code&gt; to create a paragraph. Because this is Ruby, as your Shoes app gets more complex you can create classes and methods to organize and keep it manageable just as you would in any other app.&lt;/p&gt;

&lt;p&gt;There are all sorts of great resources out there including&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://shoesrb.com/manual'&gt;The Shoes Manual&lt;/a&gt; which includes a reference for all &lt;a href='http://shoesrb.com/manual/Elements.html'&gt;the elements&lt;/a&gt; you may use (like rect or para)&lt;/li&gt;

&lt;li&gt;&lt;a href='https://github.com/ashbb/shoes_tutorial_walkthrough/blob/master/README.md'&gt;why&amp;#8217;s tutorial&lt;/a&gt; - the original introduction to Shoes&lt;/li&gt;

&lt;li&gt;&lt;a href='https://github.com/shoes/shoes/tree/develop/samples'&gt;sample apps&lt;/a&gt; - there are some really good ones here!&lt;/li&gt;

&lt;li&gt;&lt;a href='http://teachingkids.railsbridge.org/2009/08/15/teaching-ruby-to-high-school-girls.html'&gt;Teaching Ruby to High School Girls&lt;/a&gt; (using Shoes) article by Sarah Mei&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='gosu'&gt;Gosu&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://libgosu.org/'&gt;Gosu&lt;/a&gt; is a gaming library so while Shoes lets us build any sort of GUI apps this is seemed like it might be a better fit since we&amp;#8217;re interested in gaming. Luckily there&amp;#8217;s a gem that wraps up the Ruby interface (Gosu can be used from C++ or Ruby).&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;gem install gosu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we want to build a similar game to what we did in Shoes with a play we move around via the arrow keys we need to subclass the &lt;code&gt;Gosu::Window&lt;/code&gt; class.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;gosu&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Player&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;window&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@image&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Image&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;window&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;media/Starfighter.png&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@x&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@y&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;125&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;50&lt;/span&gt;
    &lt;span class='vi'&gt;@angle&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;draw&lt;/span&gt;
    &lt;span class='vi'&gt;@image&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;draw_rot&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@x&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@y&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@angle&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;GameWindow&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Window&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;
    &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;640&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;480&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;caption&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Gosu Tutorial Game&amp;quot;&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;draw&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;draw&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;window&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;GameWindow&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='n'&gt;window&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we see a window with a player that looks like a starship&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/gosu_starfighter.png' alt='Starship player' /&gt;&lt;/p&gt;

&lt;p&gt;Its not too hard to make it move by overriding the &lt;code&gt;update&lt;/code&gt; method in our window class&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;gosu&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Player&lt;/span&gt;
  &lt;span class='kp'&gt;attr_accessor&lt;/span&gt; &lt;span class='ss'&gt;:x&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:y&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;window&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@image&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Image&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;window&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;media/Starfighter.bmp&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@x&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@y&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;75&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;50&lt;/span&gt;
    &lt;span class='vi'&gt;@angle&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;draw&lt;/span&gt;
    &lt;span class='vi'&gt;@image&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;draw_rot&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@x&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@y&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@angle&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;GameWindow&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Window&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;
    &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;400&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;300&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;caption&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Our Game&amp;quot;&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@current_coordinates&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Font&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:default_font_name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;20&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;update&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt; &lt;span class='o'&gt;-=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;button_down?&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:KbLeft&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;button_down?&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:KbRight&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;y&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;button_down?&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:KbDown&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;y&lt;/span&gt; &lt;span class='o'&gt;-=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;button_down?&lt;/span&gt; &lt;span class='ss'&gt;Gosu&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:KbUp&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;draw&lt;/span&gt;
    &lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;draw&lt;/span&gt;
    &lt;span class='vi'&gt;@current_coordinates&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;draw&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;(&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;, &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@player&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;y&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;)&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mh'&gt;0xffffff00&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;window&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;GameWindow&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='n'&gt;window&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/gosu_starfighter_moves.png' alt='Starship player moves' /&gt;&lt;/p&gt;

&lt;p&gt;This example can be extended into a full Asteroids like like game where your ship has inertia. You should look at the &lt;a href='https://github.com/jlnr/gosu/blob/master/examples/Tutorial.rb'&gt;source&lt;/a&gt; or an &lt;a href='https://github.com/jlnr/gosu/wiki/Ruby-Tutorial'&gt;explanation&lt;/a&gt; on the gosu site.&lt;/p&gt;

&lt;p&gt;There are all sorts of great resources out there including&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Samples from the &lt;a href='http://www.libgosu.org/cgi-bin/mwf/board_show.pl?bid=2'&gt;Gosu Showcase&lt;/a&gt; or &lt;table&gt;
&lt;tr&gt;
  &lt;td style='text-align: center;'&gt;&lt;a href='https://github.com/PhilCK/Falling-Blocks'&gt;Falling Blocks&lt;/a&gt;             &lt;/td&gt;
  &lt;td style='text-align: center;'&gt; or            &lt;/td&gt;
  &lt;td style='text-align: center;'&gt;&lt;a href='https://github.com/jlnr/gosu/blob/master/examples/CptnRuby.rb'&gt;CptnRuby&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/falling_blocks.png' alt='Falling Blocks (tetris)' /&gt;&lt;/td&gt;
  &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  &lt;td&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/cptn_ruby.png' alt='Captain Ruby' /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='https://github.com/ippa/chingu'&gt;Chingu&lt;/a&gt; an extension that seems to let us avoid re-writing common tasks &lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/robot.png' alt='Robot' /&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='https://github.com/beoran/chipmunk'&gt;Chipmunk&lt;/a&gt; a physics library that makes it easy to do things like collision detection, gravity, etc &lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/gosu_chipmunk.png' alt='Gravity and Collisions demo' /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though we&amp;#8217;re still writing Ruby, Gosu feels more like C++ Windows development I used to do long long time ago. I&amp;#8217;m not sure if that&amp;#8217;s inevitable and need to keep using Gosu to find out.&lt;/p&gt;

&lt;h2 id='gamesalad'&gt;gamesalad&lt;/h2&gt;

&lt;p&gt;The last framework we&amp;#8217;ve been working with is pretty different. &lt;a href='http://gamesalad.com/'&gt;GameSalad&lt;/a&gt; advertises it lets you &amp;#8220;Create games for iPhone, iPad, Android, Mac, and HTML5. No coding required.&amp;#8221;&lt;/p&gt;

&lt;p&gt;It follows a model similar to what Adobe Flash uses where you have &lt;code&gt;Scenes&lt;/code&gt; containing &lt;code&gt;Actors&lt;/code&gt;. You write your programs in a visual editor by dragging and dropping Actors onto Scenes, Rules onto Actors and Behavior onto Rules. For instance if we have a starship actor and we drop these rules onto it&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/gamesalad_starfighter_movement_rules.png' alt='Starfighter rules' /&gt;&lt;/p&gt;

&lt;p&gt;We will get our familiar spaceship that can move left and right&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-14-programming-with-kids/gamesalad_starfighter_movement.png' alt='Starfighter Moving' /&gt;&lt;/p&gt;

&lt;p&gt;GameSalad is the least familiar to me but seems to be easiest for my kids to start working on. Not having to write any &amp;#8220;code&amp;#8221; or &amp;#8220;do programming&amp;#8221; makes it much easier to get started. It also can create iPhone or iPad games and I would never dream of exposing my kids to Objective-C.&lt;/p&gt;

&lt;h2 id='whats_next'&gt;What&amp;#8217;s next?&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve started experimenting with all three of these tools and so far are having fun with all three. Hopefully we&amp;#8217;ll figure out what works for us and perhaps try to write about it again in a few months.&lt;/p&gt;

&lt;p&gt;After writing this I came across a recent NY Times article &lt;a href='http://www.nytimes.com/2011/11/10/technology/personaltech/computer-programming-for-children-minus-cryptic-syntax.html'&gt;Programming for Children, Minus Cryptic Syntax&lt;/a&gt; and &lt;a href='http://scratch.mit.edu/'&gt;Scratch&lt;/a&gt; also sounds interesting so I may have to look into that sometime too.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/52QixCSdKco" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2011/11/14/programming-with-kids.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Twitter is the new RSS Reader</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/wpGy74xi-uY/twitter-is-the-new-rss-reader.html" />
   <published>2011-11-07T00:00:00-08:00</published>
   <updated>2011-11-07T00:00:00-08:00</updated>
   <id>http://www.alexrothenberg.com/2011/11/07/twitter-is-the-new-rss-reader</id>
   <content type="html">&lt;p&gt;In 2008 I thought RSS was an awesome way to stay abreast of what&amp;#8217;s going on, but now its 2011 and I find myself using Twitter more often than Google Reader to find new and interesting articles people have written. Readers tweet and retweet articles they find interesting which seems a lower barrier than leaving an &amp;#8220;I like this&amp;#8221; comment. As an author Twitter also gives you some idea of who is reading your posts and a way to connect with them.&lt;/p&gt;

&lt;p&gt;Back in 2008 I created a blog aggregator site &lt;a href='http://waywework.it'&gt;http://waywework.it&lt;/a&gt; to group the all the people I work with and promote others to share their thoughts. I was so excited I even wrote &lt;a href='http://www.alexrothenberg.com/2008/11/09/blog-aggregator-wayweworkit.html'&gt;an article&lt;/a&gt; about it.&lt;/p&gt;

&lt;p&gt;Now that its 2011, I&amp;#8217;ve been asking myself how could I update &lt;a href='http://waywework.it'&gt;http://waywework.it&lt;/a&gt; for the twitter world of today?&lt;/p&gt;

&lt;p&gt;I decide that if we&amp;#8217;re going to follow people on twitter that&amp;#8217;s what my site should facilitate. When new posts come in it should tweet them letting you see them if you follow &lt;a href='https://twitter.com/#!/WayWeWorkIT'&gt;@WayWeWorkIT&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='enabling_api_access_to_twitter'&gt;Enabling API access to twitter&lt;/h2&gt;

&lt;p&gt;Once I had this I added the &lt;a href='https://github.com/jnunemaker/twitter'&gt;twitter gem&lt;/a&gt; to my application. I have to give a shout out John Nunemaker for writing this fantastic gem which made my task so simple.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Gemfile&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;twitter&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I created a new twitter account &lt;a href='http://twitter.com/WayWeWorkIT'&gt;@WayWeWorkIT&lt;/a&gt; and registered an application at &lt;a href='https://dev.twitter.com/apps'&gt;https://dev.twitter.com/apps&lt;/a&gt; so I had my OAuth and access tokens.&lt;/p&gt;

&lt;p&gt;The only trick was I had to go &lt;code&gt;Application Settings&lt;/code&gt; tab and configure it for &lt;code&gt;Read and Write&lt;/code&gt; access then regenerate the tokens.&lt;/p&gt;

&lt;p&gt;Now that I had the keys and tokens from twitter I had to tell my application to use them without hardcoding them in my code. This took two steps. First, configuring the app to read the tokens from the environment in &lt;code&gt;config/initializers/twitter.rb&lt;/code&gt;. Yes I am somewhat paranoid about accidentally tweeting from development but that &lt;code&gt;if Rails.env.production?&lt;/code&gt; should save me.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;production?&lt;/span&gt;
  &lt;span class='no'&gt;Twitter&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;consumer_key&lt;/span&gt;       &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ENV&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;TWITTER_CONSUMER_KEY&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;consumer_secret&lt;/span&gt;    &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ENV&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;TWITTER_CONSUMER_SECRET&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;oauth_token&lt;/span&gt;        &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ENV&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;TWITTER_OAUTH_TOKEN&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;oauth_token_secret&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ENV&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;TWITTER_OAUTH_TOKEN_SECRET&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Secondly, setting the tokens on the heroku environment (I typed the real tokens instead of the XXXXXXXX&amp;#8217;s).&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; heroku config:add &lt;span class='nv'&gt;TWITTER_CONSUMER_KEY&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;XXXXXXXX
&lt;span class='gp'&gt;$&lt;/span&gt; heroku config:add &lt;span class='nv'&gt;TWITTER_CONSUMER_SECRET&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;XXXXXXXX
&lt;span class='gp'&gt;$&lt;/span&gt; heroku config:add &lt;span class='nv'&gt;TWITTER_OAUTH_TOKEN&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;XXXXXXXX
&lt;span class='gp'&gt;$&lt;/span&gt; heroku config:add &lt;span class='nv'&gt;TWITTER_OAUTH_TOKEN_SECRET&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;XXXXXXXX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can test it out (after deploying with &lt;code&gt;git push heroku&lt;/code&gt;)&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='err'&gt;$&lt;/span&gt; &lt;span class='n'&gt;heroku&lt;/span&gt; &lt;span class='n'&gt;console&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Twitter&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;http://waywework.it aggregates blog articles&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='c1'&gt;# some big object returned&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Twitter&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;user_timeline&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;wayweworkit&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text&lt;/span&gt;
&lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://t.co/FCmUQdc3 aggregates blog articles&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great we just tweeted our first tweet for the world to see.&lt;/p&gt;

&lt;h2 id='updating_wayweworkit_to_tweet_new_posts'&gt;Updating WayWeWork.IT to tweet new posts&lt;/h2&gt;

&lt;p&gt;The app periodically scans the rss feeds it tracks and when it sees a new post it creates it in the app&amp;#8217;s database.&lt;/p&gt;

&lt;p&gt;First we add a twitter_username to each feed we&amp;#8217;re tracking&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;AddTwitterUsernameToFeeds&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Migration&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;change&lt;/span&gt;
    &lt;span class='n'&gt;add_column&lt;/span&gt; &lt;span class='ss'&gt;:feeds&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:twitter_username&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:string&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, add an &lt;code&gt;after_create&lt;/code&gt; callback to tweet each time we create a new post.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Post&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;after_create&lt;/span&gt; &lt;span class='ss'&gt;:tweet&lt;/span&gt;

  &lt;span class='n'&gt;delegate&lt;/span&gt; &lt;span class='ss'&gt;:twitter_username&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:to&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:feed&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;twitter_username_with_at_sign&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;@&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;feed_twitter_username&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;WayWeWorkIT&amp;#39;&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2' /&gt;
&lt;span class='s2'&gt;  end&lt;/span&gt;

&lt;span class='s2'&gt;  # See https://dev.twitter.com/docs/tco-link-wrapper/faq#Will_t.co-wrapped_links_always_be_the_same_length&lt;/span&gt;
&lt;span class='s2'&gt;  # We should query instead of hardcoding 20&lt;/span&gt;
&lt;span class='s2'&gt;  def short_url_length&lt;/span&gt;
&lt;span class='s2'&gt;    20&lt;/span&gt;
&lt;span class='s2'&gt;  end&lt;/span&gt;

&lt;span class='s2'&gt;  def tweet&lt;/span&gt;
&lt;span class='s2'&gt;    if Rails.env.production?&lt;/span&gt;
&lt;span class='s2'&gt;      non_title_part_of_tweet = &amp;quot;&lt;/span&gt; &lt;span class='c1'&gt;#{&amp;#39;x&amp;#39;*short_url_length} via #{twitter_username_with_at_sign}&amp;quot;&lt;/span&gt;
      &lt;span class='n'&gt;max_title_length&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;140&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;non_title_part_of_tweet&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;length&lt;/span&gt;

      &lt;span class='n'&gt;tweet&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;title&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;truncate&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;max_title_length&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;url&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; via &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;twitter_username_with_at_sign&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
      &lt;span class='no'&gt;Twitter&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;update&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tweet&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Again with the &amp;#8220;if Rails.env.production?&amp;#8221; paranoia? You do know that you can never be too paranoid :)&lt;/p&gt;

&lt;p&gt;With the twitter gem its one line to tweet &lt;code&gt;Twitter.update(tweet)&lt;/code&gt;. The rest of it is to shorten the title so twitter&amp;#8217;s 140 character limit wont cut off the url or the author&amp;#8217;s name.&lt;/p&gt;

&lt;p&gt;Once this is in we&amp;#8217;ll start seeing tweets like&lt;/p&gt;

&lt;p&gt;&lt;a href='https://twitter.com/#!/WayWeWorkIT/status/131380463301427200'&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-07-twitter-is-the-new-rss-reader/using_bdd_and_email_spec_gem.png' alt='Using BDD and the email_spec gem to implement Email www.alexrothenberg.com/2011/10/31/usi… via @alexrothenberg' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href='https://twitter.com/#!/WayWeWorkIT/status/133556175563259904'&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-11-07-twitter-is-the-new-rss-reader/twitter_is_the_new_rss_reader.png' alt='Twitter is the new RSS Reader http://www.alexrothenberg.com/2011/11/07/twitter-is-the-new-rss-reader.html via @alexrothenberg' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and follow &lt;a href='https://twitter.com/#!/WayWeWorkIT'&gt;@WayWeWorkIT&lt;/a&gt; on twitter and you&amp;#8217;ll start seeing these blog posts.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/wpGy74xi-uY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2011/11/07/twitter-is-the-new-rss-reader.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Using BDD and the email_spec gem to implement Email</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/hD85_YRsdO0/using-bdd-and-the-email_spec-gem-to-implement-email.html" />
   <published>2011-10-31T00:00:00-07:00</published>
   <updated>2011-10-31T00:00:00-07:00</updated>
   <id>http://www.alexrothenberg.com/2011/10/31/using-bdd-and-the-email_spec-gem-to-implement-email</id>
   <content type="html">&lt;p&gt;When implementing email functionality, the &lt;a href='https://github.com/bmabey/email-spec'&gt;email_spec gem&lt;/a&gt; is something I&amp;#8217;ve decided I can&amp;#8217;t live without. It makes it so easy to write RSpec specs and Cucumber features around your email that you have no excuse not to. Today I&amp;#8217;m going to go through an example how I recently used BDD to send an email in an app I was working on.&lt;/p&gt;

&lt;p&gt;When I think about email and my Rails environments this is how I typically want them to behave.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;test&lt;/strong&gt; should not send emails and allow us to write specs or features against them&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;development&lt;/strong&gt; should not send emails but provide a UI to view what would be sent on localhost&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;staging&lt;/strong&gt; should not send emails but provide a UI to view what would be sent on a server&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;production&lt;/strong&gt; should send real emails to real people&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Last week I wrote about &lt;a href='http://www.alexrothenberg.com/2011/10/24/using-letter-opener-to-view-sent-email-on-a-server.html'&gt;using letter opener to View Sent Email on a Server (without actually sending anything)&lt;/a&gt; describing how the &lt;code&gt;letter_opener gem&lt;/code&gt; helps us in the &lt;em&gt;development&lt;/em&gt; and &lt;em&gt;staging&lt;/em&gt; environments. This article focuses on using &lt;code&gt;email_spec gem&lt;/code&gt; in the &lt;em&gt;test&lt;/em&gt; environment.&lt;/p&gt;

&lt;h2 id='our_sample_project'&gt;Our Sample Project&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s imagine we are working on a new startup in stealth mode. We want to generate buzz and prepare for a beta launch. We&amp;#8217;re hiding the fact its all vaporware with a splashy homepage where people can request an invitation to the beta and records their email in our database. This is the same example as my article last week and there&amp;#8217;s a live demo at &lt;a href='http://awesome-site-staging.heroku.com/'&gt;http://awesome-site-staging.heroku.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What we&amp;#8217;ll do today is not just record the email address but also send a &amp;#8220;thanks for your interest&amp;#8221; email to the user. We&amp;#8217;re going to do this in BDD fashion bouncing back and forth between &lt;code&gt;Cucumber Scenarios&lt;/code&gt; and &lt;code&gt;RSpec Unit Tests&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Writing a cucumber scenario&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;While the scenario fails&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a failing rspec spec&lt;/li&gt;

&lt;li&gt;Write code to make it pass&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Cucumber scenario tells us what should be accomplished and when it fails it we use that to tell us what unit test we should write.&lt;/p&gt;

&lt;h2 id='adding_the_email_spec_gem'&gt;Adding the &lt;em&gt;email_spec&lt;/em&gt; gem&lt;/h2&gt;

&lt;p&gt;We add it to our &lt;code&gt;Gemfile&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;group&lt;/span&gt; &lt;span class='ss'&gt;:test&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;email_spec&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We bundle and use the email_spec generator to let it initialize itself.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; bundle
&lt;span class='gp'&gt;$&lt;/span&gt; rails g email_spec:steps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&amp;#8217;s also a manual step to get cucumber to load the email_spec gem. We need to create a file &lt;code&gt;features/support/email_spec.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;email_spec/cucumber&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id='our_first_cucumber_scenario'&gt;Our first Cucumber Scenario&lt;/h2&gt;

&lt;p&gt;We know that when a user requests an invitation they should get an email so we write that requirement as a Cucumber Scenario. &lt;code&gt;features/request_an_invitation.feature&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='gherkin'&gt;&lt;span class='k'&gt;Feature:&lt;/span&gt;&lt;span class='nf'&gt; Build excitement for this vaporware&lt;/span&gt;
&lt;span class='nf'&gt;  In order to drum up interest&lt;/span&gt;
&lt;span class='nf'&gt;  As a user&lt;/span&gt;
&lt;span class='nf'&gt;  I will receive an exciting email when I request an invitation&lt;/span&gt;

&lt;span class='nf'&gt;  &lt;/span&gt;&lt;span class='k'&gt;Scenario:&lt;/span&gt;&lt;span class='nf'&gt; Someone requests an invitation and receives an email&lt;/span&gt;
&lt;span class='k'&gt;    Given &lt;/span&gt;&lt;span class='nf'&gt;I am on the home page&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;When &lt;/span&gt;&lt;span class='nf'&gt;I request an invitation for &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;gullible@lemmings.com&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;     &lt;/span&gt;&lt;span class='k'&gt;Then &lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;&lt;span class='s'&gt;gullible@lemmings.com&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; should receive &lt;/span&gt;&lt;span class='s'&gt;1&lt;/span&gt;&lt;span class='nf'&gt; email&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;they open the email&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;they should see the email delivered from &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;alex@awesome-startup.com&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot;&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;they should see &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;Invitation request for Awesome New Startup received&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; in the email subject&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;they should see &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;Dear gullible@lemmings.com,&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; in the email text part body&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;they should see &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;We have received your request &lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; in the email text part body&lt;/span&gt;
&lt;span class='nf'&gt;      &lt;/span&gt;&lt;span class='k'&gt;And &lt;/span&gt;&lt;span class='nf'&gt;they should see &amp;quot;&lt;/span&gt;&lt;span class='s'&gt;Please check back at http://awesome-site-staging.heroku.com&lt;/span&gt;&lt;span class='nf'&gt;&amp;quot; in the email text part body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We run it and it tells us we have a couple of missing steps.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;1 scenario (1 undefined)&lt;/span&gt;
&lt;span class='go'&gt;9 steps (7 skipped, 2 undefined)&lt;/span&gt;
&lt;span class='go'&gt;0m0.006s&lt;/span&gt;

&lt;span class='go'&gt;You can implement step definitions for undefined steps with these snippets:&lt;/span&gt;

&lt;span class='go'&gt;Given /^I am on the home page$/ do&lt;/span&gt;
&lt;span class='go'&gt;  pending # express the regexp above with the code you wish you had&lt;/span&gt;
&lt;span class='go'&gt;end&lt;/span&gt;

&lt;span class='go'&gt;When /^I request an invitation for &amp;quot;([^&amp;quot;]*)&amp;quot;$/ do |arg1|&lt;/span&gt;
&lt;span class='go'&gt;  pending # express the regexp above with the code you wish you had&lt;/span&gt;
&lt;span class='go'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Oh right, &lt;a href='http://aslakhellesoy.com/post/11055981222/the-training-wheels-came-off'&gt;the training wheels came off&lt;/a&gt; in &lt;code&gt;cucumber-rails v1.1.1&lt;/code&gt; and we don&amp;#8217;t have &lt;code&gt;web_steps.rb&lt;/code&gt; anymore. Let&amp;#8217;s write these steps using &lt;em&gt;capybara&lt;/em&gt; in &lt;code&gt;features/step_definitions/invite_steps.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Given&lt;/span&gt; &lt;span class='sr'&gt;/^I am on the home page$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;visit&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;When&lt;/span&gt; &lt;span class='sr'&gt;/^I request an invitation for &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;email&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;visit&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt;
  &lt;span class='n'&gt;fill_in&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;email&lt;/span&gt;
  &lt;span class='n'&gt;click_button&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Request Invitation&amp;#39;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We run one more time and get a failure we expect. Its telling us we haven&amp;#8217;t written any code to implement the scenario yet!&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;  Then &amp;quot;gullible@lemmings.com&amp;quot; should receive 1 email&lt;/span&gt;
&lt;span class='go'&gt;  expected: 1&lt;/span&gt;
&lt;span class='go'&gt;       got: 0 (using ==) (RSpec::Expectations::ExpectationNotMetError)&lt;/span&gt;
&lt;span class='go'&gt;  ./features/step_definitions/email_steps.rb:52:in `/^(?:I|they|&amp;quot;([^&amp;quot;]*?)&amp;quot;) should receive (an|no|\d+) emails?$/&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;  features/request_an_invitation.feature:9:in `Then &amp;quot;gullible@lemmings.com&amp;quot; should receive 1 email&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;h2 id='dropping_into_rspec_unit_tests'&gt;Dropping into RSpec unit tests&lt;/h2&gt;

&lt;p&gt;Our failing feature tells us what we need to implement so we drop down to the unit test level and start implementing it with TDD. The feature tells us an email should be be generated so let&amp;#8217;s go. We&amp;#8217;ll add to our &lt;code&gt;invites_controller_spec&lt;/code&gt; specifying that it should create and deliver an &lt;code&gt;InviteMailer&lt;/code&gt;. &lt;code&gt;spec/controllers/invites_controller_spec.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec_helper&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;InvitesController&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;PUT #update&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:email&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;someone@someco.com&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:save&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:invite_mailer&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='no'&gt;InviteMailer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:invite_requested&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;invite_mailer&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='n'&gt;invite_mailer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:deliver&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='no'&gt;Invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:new&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;email&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;someone@someco.com&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;and_return&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:invite&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:email&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;someone@someco.com&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;root_url&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;set_the_flash&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Thanks for your interest someone@someco.com.  You will hear from us soon.&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course we get an error &lt;code&gt;uninitialized constant InviteMailer&lt;/code&gt;. We fix that by creating the mailer (it doesn&amp;#8217;t do anything yet) &lt;code&gt;app/mailers/invite_mailer.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;InviteMailer&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActionMailer&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The error changes&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;Failure/Error: InviteMailer.should_receive(:invite_requested).with(invite).and_return(invite_mailer)&lt;/span&gt;
&lt;span class='go'&gt;  (&amp;lt;InviteMailer (class)&amp;gt;).invite_requested(#&amp;lt;RSpec::Mocks::Mock:0x82c96210 @name=nil&amp;gt;)&lt;/span&gt;
&lt;span class='go'&gt;      expected: 1 time&lt;/span&gt;
&lt;span class='go'&gt;      received: 0 times&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We add the code to our controller to create and deliver the email in &lt;code&gt;app/controllers/invites_controller.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;InvitesController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;create&lt;/span&gt;
    &lt;span class='vi'&gt;@invite&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:invite&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='vi'&gt;@invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;save&lt;/span&gt;
      &lt;span class='no'&gt;InviteMailer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;invite_requested&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;deliver&lt;/span&gt;
      &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:notice&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Thanks for your interest &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;email&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;.  You will hear from us soon.&amp;quot;&lt;/span&gt;
    &lt;span class='k'&gt;else&lt;/span&gt;
      &lt;span class='n'&gt;render&lt;/span&gt; &lt;span class='ss'&gt;:action&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;new&amp;quot;&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Are we done?&lt;/p&gt;

&lt;h2 id='checking_the_cucumber_scenario'&gt;Checking the Cucumber Scenario&amp;#8230;&lt;/h2&gt;

&lt;p&gt;The RSpec unit tests pass now but the Cucumber features are still failing.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;When I request an invitation for &amp;quot;gullible@lemmings.com&amp;quot;            # features/step_definitions/invite_steps.rb:5&lt;/span&gt;
&lt;span class='go'&gt;  undefined method `invite_requested&amp;#39; for InviteMailer:Class (NoMethodError)&lt;/span&gt;
&lt;span class='go'&gt;  ./app/controllers/invites_controller.rb:10:in `create&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;  (eval):2:in `send&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;  (eval):2:in `click_button&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;  ./features/step_definitions/invite_steps.rb:8:in `/^I request an invitation for &amp;quot;([^&amp;quot;]*)&amp;quot;$/&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;  features/request_an_invitation.feature:8:in `When I request an invitation for &amp;quot;gullible@lemmings.com&amp;quot;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Duh our &lt;code&gt;InviteMailer&lt;/code&gt; doesn&amp;#8217;t do anything.&lt;/p&gt;

&lt;h2 id='back_down_to_the_unit_tests'&gt;Back down to the unit tests&lt;/h2&gt;

&lt;p&gt;We write our &lt;code&gt;spec/mailers/invite_mailer_spec.rb&lt;/code&gt;. We need to include some &lt;em&gt;EmailSpec&lt;/em&gt; modules so we have access to its matchers.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec_helper&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;InviteMailer&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='ss'&gt;EmailSpec&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Helpers&lt;/span&gt;
  &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='ss'&gt;EmailSpec&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Matchers&lt;/span&gt;

  &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;.invite_requested&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;Factory&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;build&lt;/span&gt; &lt;span class='ss'&gt;:invite&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:email&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;someone@someco.com&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;one email to one user&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='n'&gt;subject&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;InviteMailer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;invite_requested&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;deliver_to&lt;/span&gt;     &lt;span class='n'&gt;invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;email&lt;/span&gt;                                                  &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;deliver_from&lt;/span&gt;   &lt;span class='s1'&gt;&amp;#39;alex@awesome-startup.com&amp;#39;&lt;/span&gt;                                    &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;have_subject&lt;/span&gt;   &lt;span class='s2'&gt;&amp;quot;Invitation request for Awesome New Startup received&amp;quot;&lt;/span&gt;         &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;have_body_text&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Dear someone@someco.com,&amp;quot;&lt;/span&gt;                                    &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;have_body_text&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;We have received your request&amp;quot;&lt;/span&gt;                               &lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;have_body_text&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Please check back at http://awesome-site-staging.heroku.com&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course it fails because we still haven&amp;#8217;t implemented anything. Let&amp;#8217;s add some code to &lt;code&gt;app/mailers/invite_mailer.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;InviteMailer&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActionMailer&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;invite_requested&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@invite&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;invite&lt;/span&gt;
    &lt;span class='n'&gt;mail&lt;/span&gt; &lt;span class='ss'&gt;:to&lt;/span&gt;      &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;email&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
         &lt;span class='ss'&gt;:from&lt;/span&gt;    &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;alex@awesome-startup.com&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
         &lt;span class='ss'&gt;:subject&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Invitation request for Awesome New Startup received&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and we can use haml to format the body in &lt;code&gt;app/views/invite_mailer/invite_requested.text.haml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;== Dear #{@invite.email},&lt;/span&gt;
&lt;span class='x'&gt;We have received your request to be invited into our awesome site. We&amp;#39;ll let you know as soon as its available.&lt;/span&gt;
&lt;span class='x'&gt;Please check back at http://awesome-site-staging.heroku.com&lt;/span&gt;
&lt;span class='x'&gt;You must be very excited!&lt;/span&gt;
&lt;span class='x'&gt;Thanks&lt;/span&gt;
&lt;span class='x'&gt;An Awesome New Startup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;We run the &lt;code&gt;rake&lt;/code&gt; one more time and &amp;#8230;&lt;/p&gt;

&lt;h2 id='were_done'&gt;We&amp;#8217;re Done&lt;/h2&gt;

&lt;p&gt;Everything passes - the specs &lt;em&gt;and&lt;/em&gt; the features!&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; rake
&lt;span class='go'&gt;ruby -S rspec &amp;lt;a long list of _spec.rb files&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;............&lt;/span&gt;

&lt;span class='go'&gt;Finished in 2.64 seconds&lt;/span&gt;
&lt;span class='go'&gt;12 examples, 0 failures&lt;/span&gt;

&lt;span class='go'&gt;ruby -S bundle exec cucumber  --profile default&lt;/span&gt;
&lt;span class='go'&gt;Using the default profile...&lt;/span&gt;
&lt;span class='go'&gt;Feature: Build excitement for this vaporware&lt;/span&gt;
&lt;span class='go'&gt;  In order to drum up interest&lt;/span&gt;
&lt;span class='go'&gt;  As a user&lt;/span&gt;
&lt;span class='go'&gt;  I will receive an exciting email&lt;/span&gt;

&lt;span class='go'&gt;  Scenario: Someone requests an invitation and receives an email&lt;/span&gt;
&lt;span class='go'&gt;    Given I am on the home page&lt;/span&gt;
&lt;span class='go'&gt;    When I request an invitation for &amp;quot;gullible@lemmings.com&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    Then &amp;quot;gullible@lemmings.com&amp;quot; should receive 1 email&lt;/span&gt;
&lt;span class='go'&gt;    And they open the email&lt;/span&gt;
&lt;span class='go'&gt;    And they should see the email delivered from &amp;quot;alex@awesome-startup.com&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    And they should see &amp;quot;Invitation request for Awesome New Startup received&amp;quot; in the email subject&lt;/span&gt;
&lt;span class='go'&gt;    And they should see &amp;quot;Dear gullible@lemmings.com,&amp;quot; in the email body&lt;/span&gt;
&lt;span class='go'&gt;    And they should see &amp;quot;We have received your request&amp;quot; in the email body&lt;/span&gt;
&lt;span class='go'&gt;    And they should see &amp;quot;Please check back at http://awesome-site-staging.heroku.com&amp;quot; in the email body&lt;/span&gt;

&lt;span class='go'&gt;1 scenario (1 passed)&lt;/span&gt;
&lt;span class='go'&gt;9 steps (9 passed)&lt;/span&gt;
&lt;span class='go'&gt;0m0.293s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I hope you&amp;#8217;ll consider using the &lt;code&gt;email_spec gem&lt;/code&gt; and &lt;code&gt;BDD&lt;/code&gt; the next time you have to add email to your app.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/hD85_YRsdO0" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2011/10/31/using-bdd-and-the-email_spec-gem-to-implement-email.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Using Letter Opener to View Sent Email on a Server (without actually sending anything)</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/yZX5n6ZtDFA/using-letter-opener-to-view-sent-email-on-a-server.html" />
   <published>2011-10-24T00:00:00-07:00</published>
   <updated>2011-10-24T00:00:00-07:00</updated>
   <id>http://www.alexrothenberg.com/2011/10/24/using-letter-opener-to-view-sent-email-on-a-server</id>
   <content type="html">&lt;p&gt;When developing email functionality you don&amp;#8217;t want to send real emails to real people before in production. At the same time you need to send them to ensure they are formatted correctly and contain the proper information. You can (and should) write integration tests to verify this but that helps developers gain confidence, what can we do to show non-technical stakeholders that it all works?&lt;/p&gt;

&lt;p&gt;Today I&amp;#8217;m going to show you how to use Ryan Bates&amp;#8217; &lt;a href='https://github.com/ryanb/letter_opener'&gt;letter_opener&lt;/a&gt; gem to let you preview your emails without actually sending them. Ryan of course does the always awesome &lt;a href='http://railscasts.com'&gt;RailsCasts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s think about the different Rails environments and how we want them to behave with email.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;test&lt;/strong&gt; should not send emails and allow us to write specs or features against them&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;development&lt;/strong&gt; should not send emails but provide a UI to view what would be sent&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;staging&lt;/strong&gt; should not send emails but provide a UI to view what would be sent&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;production&lt;/strong&gt; should send real emails to real people&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at this, Rails works really well for &lt;em&gt;test&lt;/em&gt; with ActionMailer&amp;#8217;s :test delivery method that stores the emails in the ActionMailer::Base.deliveries array so you can then use &lt;a href='https://github.com/bmabey/email-spec'&gt;email_spec&lt;/a&gt; to write your specs. &lt;em&gt;Production&lt;/em&gt; is also covered as long as you give it your mail server configuration. That leaves &lt;em&gt;development&lt;/em&gt; and &lt;em&gt;staging&lt;/em&gt; which look identical but we&amp;#8217;ll see that they are slightly different. I&amp;#8217;ll spend the rest of this article talking about how &lt;em&gt;letter_opener&lt;/em&gt; lets us do what we want in these environments.&lt;/p&gt;

&lt;h2 id='an_example'&gt;An example&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s imagine we are working on a new startup in stealth mode. We want to generate buzz and prepare for a beta launch. We&amp;#8217;re hiding the fact its all vaporware with a splashy homepage where people can request an invitation to the beta and it sends a &amp;#8220;thanks for your interest&amp;#8221; email. Lastly, we want to test it in &lt;em&gt;development&lt;/em&gt; and &lt;em&gt;staging&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s a &lt;a href='http://awesome-site-staging.heroku.com/'&gt;live demo at http://awesome-site-staging.heroku.com/&lt;/a&gt; if you want to dive in and start clicking.&lt;/p&gt;

&lt;p&gt;After you request an invitation it shows a page like this&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-10-24-previewing-email-instead-of-sending-prior-to-production/home_page.png' alt='Vaporware Homepage' /&gt;&lt;/p&gt;

&lt;h2 id='logfile_testing_we_can_do_better'&gt;Logfile testing (we can do better)&lt;/h2&gt;

&lt;p&gt;When you fill in your email and click the &lt;code&gt;Request Invitation&lt;/code&gt; button, our controller uses a mailer to create and deliver the email.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;InvitesController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;create&lt;/span&gt;
    &lt;span class='vi'&gt;@invite&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:invite&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='vi'&gt;@invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;save&lt;/span&gt;
    &lt;span class='no'&gt;InviteMailer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;invite_requested&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@invite&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;deliver&lt;/span&gt;
    &lt;span class='n'&gt;redirect_to&lt;/span&gt; &lt;span class='n'&gt;root_path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:notice&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Thanks for your interest &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@invite&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;email&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;.  You will hear from us soon.&amp;quot;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The only way to tell whether the email worked is to scroll through the development.log until you see something like this&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;Sent mail to alex@alexrothenberg.com (20ms)&lt;/span&gt;
&lt;span class='go'&gt;Date: Fri, 21 Oct 2011 15:09:16 -0400&lt;/span&gt;
&lt;span class='go'&gt;From: admin@newstartup.com&lt;/span&gt;
&lt;span class='go'&gt;To: alex@alexrothenberg.com&lt;/span&gt;
&lt;span class='go'&gt;Message-ID: &amp;lt;4ea1c35cc3d5b_6693830916bc43754@Alex-Rothenbergs-MacBook-Pro.local.mail&amp;gt;&lt;/span&gt;
&lt;span class='go'&gt;Subject: Invite requested&lt;/span&gt;
&lt;span class='go'&gt;...&lt;/span&gt;
&lt;span class='go'&gt;We have received your request to be invited into our awesome site. We&amp;#39;ll let you know as soon as its available.&lt;/span&gt;
&lt;span class='go'&gt;Please check back at http://awesome-site.heroku.com&lt;/span&gt;
&lt;span class='go'&gt;You must be very excited!&lt;/span&gt;
&lt;span class='go'&gt;Thanks&lt;/span&gt;
&lt;span class='go'&gt;An Awesome New Startup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;Ok if you&amp;#8217;re a developer and you enjoy reading log files but &lt;em&gt;letter_opener&lt;/em&gt; lets us do better.&lt;/p&gt;

&lt;h2 id='using_letter_opener_in_development'&gt;Using &lt;em&gt;letter_opener&lt;/em&gt; in development&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Letter_opener&lt;/em&gt; provides us with a UI so we can view the emails right in our browser. It&amp;#8217;s super easy to add this gem and I&amp;#8217;ll just copy the instructions from its &lt;a href='https://github.com/ryanb/letter_opener/blob/master/README.rdoc'&gt;README&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Preview email in the browser instead of sending it. This means you do not need to set up email delivery in your development environment, and you no longer need to worry about accidentally sending a test email to someone else’s address&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rails Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First add the gem to your development environment and run the bundle command to install it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem &amp;quot;letter_opener&amp;quot;, :group =&amp;gt; :development&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then set the delivery method in config/environments/development.rb&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config.action_mailer.delivery_method = :letter_opener&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now any email will pop up in your browser instead of being sent. The messages are stored in tmp/letter_opener.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once we&amp;#8217;ve done that what happens when we use the site to request an invitation? A new tab opens up with the email right there. Now as a user we can tell it&amp;#8217;s correct and any non-technical people on the team can feel their confidence rise.&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-10-24-previewing-email-instead-of-sending-prior-to-production/development_letter_opener.png' alt='Previewing an Email with Letter Opener in Development' /&gt;&lt;/p&gt;

&lt;h3 id='how_does_letter_opener_actually_work'&gt;How does letter_opener actually work?&lt;/h3&gt;

&lt;p&gt;Rails goes through the standard flow to create the mail object and when it&amp;#8217;s ready to deliver the message it calls letter_opener&amp;#8217;s &lt;code&gt;deliver!&lt;/code&gt; method because we registered letter_opener as the &lt;em&gt;action_mailer.delivery_method&lt;/em&gt;. Letter_opener saves the email to your file system as an html file then uses &lt;a href='https://github.com/copiousfreetime/launchy'&gt;launchy&lt;/a&gt; to open it in a browser using the file:// protocol.&lt;/p&gt;

&lt;p&gt;There will be a couple of problems once we move onto a server which brings us to &lt;em&gt;staging&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id='using_letter_opener_on_staging'&gt;Using &lt;em&gt;letter_opener&lt;/em&gt; on staging&lt;/h2&gt;

&lt;p&gt;If you&amp;#8217;re like me you probably have a staging environment where you or your stakeholders can validate your app before releasing it to production and your end users. There are two aspects of this environment that wont work with letter_opener the way it did in development&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need to use http:// not file:// to preview the emails because the browser is not on the same file system where the emails are written&lt;/li&gt;

&lt;li&gt;We may not be able to write to the file system. For example if we have deployed to heroku.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had to make some changes to &lt;em&gt;letter_opener&lt;/em&gt; to support this kind of server environment. The fork is available at &lt;a href='https://github.com/alexrothenberg/letter_opener/tree/on_a_server'&gt;https://github.com/alexrothenberg/letter_opener/tree/on_a_server&lt;/a&gt; and I&amp;#8217;ll update the article if my pull requests are merged back in.&lt;/p&gt;

&lt;p&gt;We need to make a few changes to our application.&lt;/p&gt;

&lt;p&gt;1 - Update our &lt;code&gt;Gemfile&lt;/code&gt; to use the fork from github&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;  &lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;letter_opener&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;  &lt;span class='ss'&gt;:git&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;git://github.com/alexrothenberg/letter_opener.git&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:branch&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;on_a_server&amp;quot;&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;2 - Add a debugging UI link so users can get to the &amp;#8220;preview emails&amp;#8221; page in something like &lt;code&gt;layouts/application.html.haml&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;link_to&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Preview Emails&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;letter_opener_letters_path&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;env&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;staging?&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;3 - If you cannot write to the filesystem let letter_opener know in your &lt;code&gt;config/environments/staging.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;  &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;action_mailer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;delivery_method&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;:letter_opener&lt;/span&gt;
  &lt;span class='no'&gt;LetterOpener&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cannot_write_to_file_system!&lt;/span&gt;
  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can see it all in action.&lt;/p&gt;

&lt;p&gt;First, we request an invite.&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-10-24-previewing-email-instead-of-sending-prior-to-production/invite_requested_on_server.png' alt='Previewing an Email with Letter Opener in Development' /&gt;&lt;/p&gt;

&lt;p&gt;Then, we click the link &amp;#8220;view the Emails that users would have received&amp;#8221; link at the bottom and see an &amp;#8220;inbox&amp;#8221; of everything the app sent.&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-10-24-previewing-email-instead-of-sending-prior-to-production/preview_inbox_on_server.png' alt='Previewing an Email with Letter Opener in Development' /&gt;&lt;/p&gt;

&lt;p&gt;Fincally clicking one message lets us preview it just as we did in devellopment&lt;/p&gt;

&lt;p&gt;&lt;img src='http://www.alexrothenberg.com/images/2011-10-24-previewing-email-instead-of-sending-prior-to-production/preview_email_on_server.png' alt='Previewing an Email with Letter Opener in Development' /&gt;&lt;/p&gt;

&lt;p&gt;We are able to run on heroku without writing to the file system by using the &lt;a href='https://github.com/defunkt/fakefs'&gt;FakeFS gem&lt;/a&gt; which simulates the filesystem in memory. FakeFS is designed to be used for testing and one caveat to be aware of is that your old emails will disappear if heroku recycles your dyno due to inactivity.&lt;/p&gt;

&lt;p&gt;I hope you think &lt;em&gt;letter_opener&lt;/em&gt; is useful and give it a try the next time you need to send email. Remember here&amp;#8217;s a &lt;a href='http://awesome-site-staging.heroku.com/'&gt;live demo at http://awesome-site-staging.heroku.com/&lt;/a&gt; of the site we&amp;#8217;ve been talking about here.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/yZX5n6ZtDFA" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2011/10/24/using-letter-opener-to-view-sent-email-on-a-server.html?utm_medium=rss</feedburner:origLink></entry>
 
 <entry>
   <title>Chaining ActiveRecord Scopes from Different Models</title>
   <link href="http://feedproxy.google.com/~r/alexrothenberg/~3/t_B4VBVDzCI/chaining-active_record-scopes-from-different-models.html" />
   <published>2011-10-17T00:00:00-07:00</published>
   <updated>2011-10-17T00:00:00-07:00</updated>
   <id>http://www.alexrothenberg.com/2011/10/17/chaining-active_record-scopes-from-different-models</id>
   <content type="html">&lt;p&gt;You probably know you can chain ActiveRecord scopes together and it combines it all together into one sql statement, but did you know that you can also use scopes from associated models in your chain?&lt;/p&gt;

&lt;p&gt;Let me show you with an example. Let&amp;#8217;s say we have a discussion site where users can post comments with two simple models:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:comments&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Comment&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;belongs_to&lt;/span&gt; &lt;span class='ss'&gt;:user&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We could display a list of all users with &lt;code&gt;User.all&lt;/code&gt; or even display them alphabetically with &lt;code&gt;User.order(:name)&lt;/code&gt; (yes I&amp;#8217;d create a scope if I were doing this for real).&lt;/p&gt;

&lt;p&gt;What if I wanted to display the users sorted so that the ones with the most recent comment was first?&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;includes&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comments&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;merge&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Comment&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;comments.created_at desc&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&amp;#8217;s a lot going on there, let&amp;#8217;s break it down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;User.includes(:comments)&lt;/code&gt; - tells active record to query both the &lt;em&gt;users&lt;/em&gt; and &lt;em&gt;comments&lt;/em&gt; tables&lt;/li&gt;

&lt;li&gt;&lt;code&gt;Comment.order(&amp;#39;comments.created_at desc&amp;#39;)&lt;/code&gt; - sorts the results by the date of the comment (we need to specify the table and column name since created_at is also a column on the &lt;em&gt;users&lt;/em&gt; table)&lt;/li&gt;

&lt;li&gt;&lt;code&gt;merge(Comment.XXX)&lt;/code&gt; - lets us use a scope from the &lt;em&gt;Comment&lt;/em&gt; model even though we&amp;#8217;re dealing with &lt;em&gt;Users&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When ActiveRecord and ActiveRelation take all this and convert it into a sql statement it will join the &lt;em&gt;users&lt;/em&gt; and &lt;em&gt;comments&lt;/em&gt; tables and order by the comments.created_at column. Here&amp;#8217;s the sql I actually get in irb (boy am I glad I didn&amp;#8217;t have to type that sql myself!).&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;includes&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comments&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;merge&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Comment&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;comments.created_at desc&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
  &lt;span class='no'&gt;SQL&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='n'&gt;ms&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;  &lt;span class='no'&gt;SELECT&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t0_r0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t0_r1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;created_at&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t0_r2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='s2'&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;updated_at&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t0_r3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t1_r0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;user_id&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t1_r1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;body&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t1_r2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;created_at&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t1_r3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;updated_at&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;t1_r4&lt;/span&gt;
               &lt;span class='no'&gt;FROM&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;users&amp;quot;&lt;/span&gt;
               &lt;span class='no'&gt;LEFT&lt;/span&gt; &lt;span class='no'&gt;OUTER&lt;/span&gt; &lt;span class='no'&gt;JOIN&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt; &lt;span class='no'&gt;ON&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;user_id&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;id&amp;quot;&lt;/span&gt;
               &lt;span class='no'&gt;ORDER&lt;/span&gt; &lt;span class='no'&gt;BY&lt;/span&gt; &lt;span class='n'&gt;comments&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;created_at&lt;/span&gt; &lt;span class='n'&gt;desc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id='adding_scopes_to_make_it_usable'&gt;Adding Scopes to make it usable&lt;/h2&gt;

&lt;p&gt;It works to type all that but in a real application you&amp;#8217;d add scopes to make it easier to work with. Let&amp;#8217;s do that!&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:comments&lt;/span&gt;
  &lt;span class='n'&gt;scope&lt;/span&gt; &lt;span class='ss'&gt;:by_most_recent_comment&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;includes&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comments&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;merge&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Comment&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;most_recent_first&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;scope&lt;/span&gt; &lt;span class='ss'&gt;:with_recent_comments&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;includes&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:comments&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;merge&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Comment&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;recently&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;month&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ago&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Comment&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;belongs_to&lt;/span&gt; &lt;span class='ss'&gt;:user&lt;/span&gt;
  &lt;span class='n'&gt;scope&lt;/span&gt; &lt;span class='ss'&gt;:most_recent_first&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;comments.created_at desc&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;scope&lt;/span&gt; &lt;span class='ss'&gt;:recently&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;lambda&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;date&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;where&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;comments.created_at &amp;gt;= ?&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;date&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can write some nice simple scopes like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;User.by_most_recent_comment&lt;/code&gt; to get all users sorted so the ones with recent comments are at the top&lt;/li&gt;

&lt;li&gt;&lt;code&gt;User.with_recent_comments&lt;/code&gt; to get all users who have commented in the past month&lt;/li&gt;

&lt;li&gt;&lt;code&gt;User.with_recent_comments.by_most_recent_comment&lt;/code&gt; to get users who have commented in the past month sorted by the date of their most recent comment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy scoping!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/alexrothenberg/~4/t_B4VBVDzCI" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://www.alexrothenberg.com/2011/10/17/chaining-active_record-scopes-from-different-models.html?utm_medium=rss</feedburner:origLink></entry>
 

</feed>
