<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:series="http://unfoldingneurons.com/"
> <channel><title>RubySource</title> <atom:link href="http://rubysource.com/feed/" rel="self" type="application/rss+xml" /><link>http://rubysource.com</link> <description>RubySource provides advice, tutorials, commentary, and insight into the Ruby and Rails ecosystem.</description> <lastBuildDate>Thu, 17 May 2012 13:43:44 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" /> <item><title>Deploying Your Rails App to the Cloud with Unicorn, Nginx, and Capistrano</title><link>http://rubysource.com/deploying-your-rails-app-to-the-cloud-with-unicorn-nginx-and-capistrano/</link> <comments>http://rubysource.com/deploying-your-rails-app-to-the-cloud-with-unicorn-nginx-and-capistrano/#comments</comments> <pubDate>Thu, 17 May 2012 13:43:44 +0000</pubDate> <dc:creator>Benjamin Iandavid Rodriguez</dc:creator> <category><![CDATA[Getting Started]]></category> <category><![CDATA[Rails Tutorials]]></category> <category><![CDATA[capistrano]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3722</guid> <description><![CDATA[We all know how easy it is to create a Rails app, but what about when your app is ready for production? The first thing you need to do is to set up your server and install the proper libraries, so fire&#8230;]]></description> <content:encoded><![CDATA[<p>We all know how easy it is to create a Rails app, but what about when your app is ready for production?</p><p>The first thing you need to do is to set up your server and install the proper libraries, so fire up a terminal window and SSH into your server, after you have done this you will need to execute a few commands.</p><h2>Configuring the server</h2><div
class="gistem"></div><style type="text/css">@import "http://gist.github.com/stylesheets/gist/embed.css"; .gistem .highlight {background: inherit; !important;}</style><p>Once you&#8217;re done configuring your server you need to install Ruby, for this you can either use RVM or rbenv, but I&#8217;m going to leave this choice to you.</p><h2>Uploading to github</h2><p>Next we&#8217;re going to upload our app to github. Head on over to the <a
href="http://github.com" target="_blank">github </a>and create a new repository.  Once you&#8217;ve done this, fire up a terminal on your local machine and &#8220;cd&#8221; into the root of your rails app.  Now, I&#8217;m going to assume you already have some experience with git commands.  If not, never fear, we&#8217;ve got this covered.  Once you&#8217;re in the root of your rails app run the following commands:</p><div
class="gistem"><div
id="gist-2625327" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>git init</div><div class='line' id='LC2'>git add .</div><div class='line' id='LC3'>git commit -m &quot;&lt;message&gt;&quot;</div><div class='line' id='LC4'>git remote add origin git@github.com:&lt;username&gt;/&lt;git repo&gt;.git</div><div class='line' id='LC5'>git push origin master</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2625327/8d56ca398a90f445aac4b04dc036ee24e61d4ee7/gistfile1.txt" style="float:right;">view raw</a> <a
href="https://gist.github.com/2625327#file_gistfile1.txt" style="float:right;margin-right:10px;color:#666">gistfile1.txt</a> <a
href="https://gist.github.com/2625327">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>What we&#8217;ve done here is initialize an empty git repository inside the root of your rails app and add all your files to the git repo (If you need to ignore files you can edit the .gitignore file.) Next, we provided a message to the commit and added the remote <strong>origin</strong>, which will track our local development. Finally, we pushed the changes in our master branch to github.</p><h2>Installing Capistrano</h2><p>We&#8217;re almost there! It&#8221;s time to setup Capistrano for deployment, and the first step is too add it to the Gemfile:</p><pre>gem 'capistrano'</pre><p>Save the Gemfile and run the</p><pre>bundle</pre><p>command to install the gem. Next we&#8217;re going to capify our project, on a terminal window run:</p><p><code>capify .</code></p><p>This will create 2 files: one is the Cap file which will be placed in your root folder. By the way, if you&#8217;re using the rails asset pipeline, you will need to uncomment the line -load &#8216;deploy/assets&#8217;-. The second file is the deploy.rb file which will be placed inside the config folder of your rails app, In deploy.rb you will configure all the commands capistrano will be running on our remote server. You can copy and paste the example provided here:</p><div
class="gistem"><div
id="gist-2562546" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nb">require</span> <span class="s2">&quot;bundler/capistrano&quot;</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="c1"># Define your server here</span></div><div class='line' id='LC4'><span class="n">server</span> <span class="s2">&quot;&lt;server&gt;&quot;</span><span class="p">,</span> <span class="ss">:web</span><span class="p">,</span> <span class="ss">:app</span><span class="p">,</span> <span class="ss">:db</span><span class="p">,</span> <span class="n">primary</span><span class="p">:</span> <span class="kp">true</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'><span class="c1"># Set application settings</span></div><div class='line' id='LC7'><span class="n">set</span> <span class="ss">:application</span><span class="p">,</span> <span class="s2">&quot;&lt;app_name&gt;&quot;</span></div><div class='line' id='LC8'><span class="n">set</span> <span class="ss">:user</span><span class="p">,</span> <span class="s2">&quot;&lt;deployment_user&gt;&quot;</span> <span class="c1"># As defined on your server</span></div><div class='line' id='LC9'><span class="n">set</span> <span class="ss">:deploy_to</span><span class="p">,</span> <span class="s2">&quot;/home/</span><span class="si">#{</span><span class="n">user</span><span class="si">}</span><span class="s2">/apps/</span><span class="si">#{</span><span class="n">application</span><span class="si">}</span><span class="s2">&quot;</span> <span class="c1"># Directory in which the deployment will take place</span></div><div class='line' id='LC10'><span class="n">set</span> <span class="ss">:deploy_via</span><span class="p">,</span> <span class="ss">:remote_cache</span></div><div class='line' id='LC11'><span class="n">set</span> <span class="ss">:use_sudo</span><span class="p">,</span> <span class="kp">false</span></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'><span class="n">set</span> <span class="ss">:scm</span><span class="p">,</span> <span class="s2">&quot;git&quot;</span></div><div class='line' id='LC14'><span class="n">set</span> <span class="ss">:repository</span><span class="p">,</span> <span class="s2">&quot;git@github.com:&lt;git_user&gt;/</span><span class="si">#{</span><span class="n">application</span><span class="si">}</span><span class="s2">.git&quot;</span></div><div class='line' id='LC15'><span class="n">set</span> <span class="ss">:branch</span><span class="p">,</span> <span class="s2">&quot;master&quot;</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'><span class="n">default_run_options</span><span class="o">[</span><span class="ss">:pty</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span></div><div class='line' id='LC18'><span class="n">ssh_options</span><span class="o">[</span><span class="ss">:forward_agent</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'><span class="n">after</span> <span class="s2">&quot;deploy&quot;</span><span class="p">,</span> <span class="s2">&quot;deploy:cleanup&quot;</span> <span class="c1"># keep only the last 5 releases</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'><span class="n">namespace</span> <span class="ss">:deploy</span> <span class="k">do</span></div><div class='line' id='LC23'>&nbsp;&nbsp;<span class="sx">%w[start stop restart]</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">command</span><span class="o">|</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">desc</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">command</span><span class="si">}</span><span class="s2"> unicorn server&quot;</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">task</span> <span class="n">command</span><span class="p">,</span> <span class="n">roles</span><span class="p">:</span> <span class="ss">:app</span><span class="p">,</span> <span class="n">except</span><span class="p">:</span> <span class="p">{</span><span class="n">no_release</span><span class="p">:</span> <span class="kp">true</span><span class="p">}</span> <span class="k">do</span></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">run</span> <span class="s2">&quot;/etc/init.d/unicorn_</span><span class="si">#{</span><span class="n">application</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">command</span><span class="si">}</span><span class="s2">&quot;</span> <span class="c1"># Using unicorn as the app server</span></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC28'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC29'><br/></div><div class='line' id='LC30'>&nbsp;&nbsp;<span class="n">task</span> <span class="ss">:setup_config</span><span class="p">,</span> <span class="n">roles</span><span class="p">:</span> <span class="ss">:app</span> <span class="k">do</span></div><div class='line' id='LC31'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">sudo</span> <span class="s2">&quot;ln -nfs </span><span class="si">#{</span><span class="n">current_path</span><span class="si">}</span><span class="s2">/config/nginx.conf /etc/nginx/sites-enabled/</span><span class="si">#{</span><span class="n">application</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC32'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">sudo</span> <span class="s2">&quot;ln -nfs </span><span class="si">#{</span><span class="n">current_path</span><span class="si">}</span><span class="s2">/config/unicorn_ini.sh /etc/init.d/unicorn_</span><span class="si">#{</span><span class="n">application</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">run</span> <span class="s2">&quot;mkdir -p </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/config&quot;</span></div><div class='line' id='LC34'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">put</span> <span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="s2">&quot;config/database.yml&quot;</span><span class="p">),</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/config/database.yml&quot;</span></div><div class='line' id='LC35'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Now edit the config files in </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">.&quot;</span></div><div class='line' id='LC36'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC37'>&nbsp;&nbsp;<span class="n">after</span> <span class="s2">&quot;deploy:setup&quot;</span><span class="p">,</span> <span class="s2">&quot;deploy:setup_config&quot;</span></div><div class='line' id='LC38'><br/></div><div class='line' id='LC39'>&nbsp;&nbsp;<span class="n">task</span> <span class="ss">:symlink_config</span><span class="p">,</span> <span class="n">roles</span><span class="p">:</span> <span class="ss">:app</span> <span class="k">do</span></div><div class='line' id='LC40'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">run</span> <span class="s2">&quot;ln -nfs </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/config/database.yml </span><span class="si">#{</span><span class="n">release_path</span><span class="si">}</span><span class="s2">/config/database.yml&quot;</span></div><div class='line' id='LC41'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC42'>&nbsp;&nbsp;<span class="n">after</span> <span class="s2">&quot;deploy:finalize_update&quot;</span><span class="p">,</span> <span class="s2">&quot;deploy:symlink_config&quot;</span></div><div class='line' id='LC43'><br/></div><div class='line' id='LC44'>&nbsp;&nbsp;<span class="n">desc</span> <span class="s2">&quot;Make sure local git is in sync with remote.&quot;</span></div><div class='line' id='LC45'>&nbsp;&nbsp;<span class="n">task</span> <span class="ss">:check_revision</span><span class="p">,</span> <span class="n">roles</span><span class="p">:</span> <span class="ss">:web</span> <span class="k">do</span></div><div class='line' id='LC46'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">unless</span> <span class="sb">`git rev-parse HEAD`</span> <span class="o">==</span> <span class="sb">`git rev-parse origin/master`</span></div><div class='line' id='LC47'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;WARNING: HEAD is not the same as origin/master&quot;</span></div><div class='line' id='LC48'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Run `git push` to sync changes.&quot;</span></div><div class='line' id='LC49'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">exit</span></div><div class='line' id='LC50'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC51'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC52'>&nbsp;&nbsp;<span class="n">before</span> <span class="s2">&quot;deploy&quot;</span><span class="p">,</span> <span class="s2">&quot;deploy:check_revision&quot;</span></div><div class='line' id='LC53'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2562546/ac64cd6d90a0a5a59b38b447202f5622352eac30/deploy.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2562546#file_deploy.rb" style="float:right;margin-right:10px;color:#666">deploy.rb</a> <a
href="https://gist.github.com/2562546">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>As you can see, this deploy file uses unicorn as the server (which I recommend because of its stability, but you can use the server of your choice.) After you are finished setting up your deploy file with your custom configuration, create two files under your config folder: one named <strong>nginx.conf</strong>, and another one named <strong>unicorn.rb</strong>. Feel free to use the examples provided here:</p><p>Nginx.conf<br
/><div
class="gistem"><div
id="gist-2562680" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>upstream unicorn {</div><div class='line' id='LC2'>&nbsp;&nbsp;server unix:/tmp/unicorn.&lt;app_name&gt;.sock fail_timeout=0;</div><div class='line' id='LC3'>}</div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'>server {</div><div class='line' id='LC6'>	listen 80 default deferred;</div><div class='line' id='LC7'>	server_name &lt;your_servername&gt;;</div><div class='line' id='LC8'>	if ($host = &#39;&lt;your_servername&gt;&#39; ) {</div><div class='line' id='LC9'>		rewrite ^/(.*)$ http://&lt;your_servername&gt;/$1 permanent;</div><div class='line' id='LC10'>	}</div><div class='line' id='LC11'>&nbsp;&nbsp;root /home/deployer/apps/&lt;app_name&gt;/current/public;</div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'>&nbsp;&nbsp;location ^~ /assets/ {</div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;gzip_static on;</div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;expires max;</div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;add_header Cache-Control public;</div><div class='line' id='LC17'>&nbsp;&nbsp;}</div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>&nbsp;&nbsp;try_files $uri/index.html $uri @unicorn;</div><div class='line' id='LC20'>&nbsp;&nbsp;</div><div class='line' id='LC21'>&nbsp;&nbsp;location @unicorn {</div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;proxy_set_header Host $http_host;</div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;proxy_redirect off;</div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;proxy_pass http://unicorn;</div><div class='line' id='LC26'>&nbsp;&nbsp;}</div><div class='line' id='LC27'><br/></div><div class='line' id='LC28'>&nbsp;&nbsp;error_page 500 502 503 504 /500.html;</div><div class='line' id='LC29'>&nbsp;&nbsp;client_max_body_size 4G;</div><div class='line' id='LC30'>&nbsp;&nbsp;keepalive_timeout 10;</div><div class='line' id='LC31'>}</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2562680/ff40383c9a938fdb96930732cce8b4a20ff3b1c4/nginx.conf" style="float:right;">view raw</a> <a
href="https://gist.github.com/2562680#file_nginx.conf" style="float:right;margin-right:10px;color:#666">nginx.conf</a> <a
href="https://gist.github.com/2562680">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Unicorn.rb<br
/><div
class="gistem"><div
id="gist-2564049" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="c1"># Define your root directory</span></div><div class='line' id='LC2'><span class="n">root</span> <span class="o">=</span> <span class="s2">&quot;/home/deployer/apps/gifroll/current&quot;</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'><span class="c1"># Define worker directory for Unicorn</span></div><div class='line' id='LC5'><span class="n">working_directory</span> <span class="n">root</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="c1"># Location of PID file</span></div><div class='line' id='LC8'><span class="n">pid</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">root</span><span class="si">}</span><span class="s2">/tmp/pids/unicorn.pid&quot;</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'><span class="c1"># Define Log paths</span></div><div class='line' id='LC11'><span class="n">stderr_path</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">root</span><span class="si">}</span><span class="s2">/log/unicorn.log&quot;</span></div><div class='line' id='LC12'><span class="n">stdout_path</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">root</span><span class="si">}</span><span class="s2">/log/unicorn.log&quot;</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'><span class="c1"># Listen on a UNIX data socket</span></div><div class='line' id='LC15'><span class="n">listen</span> <span class="s2">&quot;/tmp/unicorn.gifroll.sock&quot;</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'><span class="c1"># 16 worker processes for production environment</span></div><div class='line' id='LC18'><span class="n">worker_processes</span> <span class="mi">16</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'><span class="c1"># Load rails before forking workers for better worker spawn time</span></div><div class='line' id='LC21'><span class="n">preload_app</span> <span class="kp">true</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'><span class="c1"># Restart workes hangin&#39; out for more than 240 secs</span></div><div class='line' id='LC24'><span class="n">timeout</span> <span class="mi">240</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2564049/a902a18e79acedb7d67c2b672c355b64993b2dc4/unicorn.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2564049#file_unicorn.rb" style="float:right;margin-right:10px;color:#666">unicorn.rb</a> <a
href="https://gist.github.com/2564049">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><h2>Final Steps</h2><p>Create one final file under your config folder called <strong>unicorn_ini.sh</strong> edit the file and copy and paste the following code:</p><div
class="gistem"><div
id="gist-2564093" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="c">#!/bin/sh</span></div><div class='line' id='LC2'><span class="c">### BEGIN INIT INFO</span></div><div class='line' id='LC3'><span class="c"># Provides:          unicorn</span></div><div class='line' id='LC4'><span class="c"># Required-Start:    $remote_fs $syslog</span></div><div class='line' id='LC5'><span class="c"># Required-Stop:     $remote_fs $syslog</span></div><div class='line' id='LC6'><span class="c"># Default-Start:     2 3 4 5</span></div><div class='line' id='LC7'><span class="c"># Default-Stop:      0 1 6</span></div><div class='line' id='LC8'><span class="c"># Short-Description: Manage unicorn server</span></div><div class='line' id='LC9'><span class="c"># Description:       Start, stop, restart unicorn server for a specific application.</span></div><div class='line' id='LC10'><span class="c">### END INIT INFO</span></div><div class='line' id='LC11'><span class="nb">set</span> -e</div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'><span class="c"># Feel free to change any of the following variables for your app:</span></div><div class='line' id='LC14'><span class="nv">TIMEOUT</span><span class="o">=</span><span class="k">${</span><span class="nv">TIMEOUT</span><span class="p">-60</span><span class="k">}</span></div><div class='line' id='LC15'><span class="nv">APP_ROOT</span><span class="o">=</span>/home/deployer/apps/&lt;app_name&gt;/current</div><div class='line' id='LC16'><span class="nv">PID</span><span class="o">=</span><span class="nv">$APP_ROOT</span>/tmp/pids/unicorn.pid</div><div class='line' id='LC17'><span class="nv">CMD</span><span class="o">=</span><span class="s2">&quot;cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production&quot;</span></div><div class='line' id='LC18'><span class="nv">AS_USER</span><span class="o">=</span>&lt;user&gt;</div><div class='line' id='LC19'><span class="nb">set</span> -u</div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'><span class="nv">OLD_PIN</span><span class="o">=</span><span class="s2">&quot;$PID.oldbin&quot;</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'>sig <span class="o">()</span> <span class="o">{</span></div><div class='line' id='LC24'>&nbsp;&nbsp;<span class="nb">test</span> -s <span class="s2">&quot;$PID&quot;</span> <span class="o">&amp;&amp;</span> <span class="nb">kill</span> -<span class="nv">$1</span> <span class="sb">`</span>cat <span class="nv">$PID</span><span class="sb">`</span></div><div class='line' id='LC25'><span class="o">}</span></div><div class='line' id='LC26'><br/></div><div class='line' id='LC27'>oldsig <span class="o">()</span> <span class="o">{</span></div><div class='line' id='LC28'>&nbsp;&nbsp;<span class="nb">test</span> -s <span class="nv">$OLD_PIN</span> <span class="o">&amp;&amp;</span> <span class="nb">kill</span> -<span class="nv">$1</span> <span class="sb">`</span>cat <span class="nv">$OLD_PIN</span><span class="sb">`</span></div><div class='line' id='LC29'><span class="o">}</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'>run <span class="o">()</span> <span class="o">{</span></div><div class='line' id='LC32'>&nbsp;&nbsp;<span class="k">if</span> <span class="o">[</span> <span class="s2">&quot;$(id -un)&quot;</span> <span class="o">=</span> <span class="s2">&quot;$AS_USER&quot;</span> <span class="o">]</span>; <span class="k">then</span></div><div class='line' id='LC33'><span class="k">    </span><span class="nb">eval</span> <span class="nv">$1</span></div><div class='line' id='LC34'>&nbsp;&nbsp;<span class="k">else</span></div><div class='line' id='LC35'><span class="k">    </span>su -c <span class="s2">&quot;$1&quot;</span> - <span class="nv">$AS_USER</span></div><div class='line' id='LC36'>&nbsp;&nbsp;<span class="k">fi</span></div><div class='line' id='LC37'><span class="o">}</span></div><div class='line' id='LC38'><br/></div><div class='line' id='LC39'><span class="k">case</span> <span class="s2">&quot;$1&quot;</span> in</div><div class='line' id='LC40'>start<span class="o">)</span></div><div class='line' id='LC41'>&nbsp;&nbsp;sig 0 <span class="o">&amp;&amp;</span> <span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;Already running&quot;</span> <span class="o">&amp;&amp;</span> <span class="nb">exit </span>0</div><div class='line' id='LC42'>&nbsp;&nbsp;run <span class="s2">&quot;$CMD&quot;</span></div><div class='line' id='LC43'>&nbsp;&nbsp;;;</div><div class='line' id='LC44'>stop<span class="o">)</span></div><div class='line' id='LC45'>&nbsp;&nbsp;sig QUIT <span class="o">&amp;&amp;</span> <span class="nb">exit </span>0</div><div class='line' id='LC46'>&nbsp;&nbsp;<span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;Not running&quot;</span></div><div class='line' id='LC47'>&nbsp;&nbsp;;;</div><div class='line' id='LC48'>force-stop<span class="o">)</span></div><div class='line' id='LC49'>&nbsp;&nbsp;sig TERM <span class="o">&amp;&amp;</span> <span class="nb">exit </span>0</div><div class='line' id='LC50'>&nbsp;&nbsp;<span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;Not running&quot;</span></div><div class='line' id='LC51'>&nbsp;&nbsp;;;</div><div class='line' id='LC52'>restart|reload<span class="o">)</span></div><div class='line' id='LC53'>&nbsp;&nbsp;sig HUP <span class="o">&amp;&amp;</span> <span class="nb">echo </span>reloaded OK <span class="o">&amp;&amp;</span> <span class="nb">exit </span>0</div><div class='line' id='LC54'>&nbsp;&nbsp;<span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;Couldn&#39;t reload, starting &#39;$CMD&#39; instead&quot;</span></div><div class='line' id='LC55'>&nbsp;&nbsp;run <span class="s2">&quot;$CMD&quot;</span></div><div class='line' id='LC56'>&nbsp;&nbsp;;;</div><div class='line' id='LC57'>upgrade<span class="o">)</span></div><div class='line' id='LC58'>&nbsp;&nbsp;<span class="k">if </span>sig USR2 <span class="o">&amp;&amp;</span> sleep 2 <span class="o">&amp;&amp;</span> sig 0 <span class="o">&amp;&amp;</span> oldsig QUIT</div><div class='line' id='LC59'>&nbsp;&nbsp;<span class="k">then</span></div><div class='line' id='LC60'><span class="k">    </span><span class="nv">n</span><span class="o">=</span><span class="nv">$TIMEOUT</span></div><div class='line' id='LC61'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">while </span><span class="nb">test</span> -s <span class="nv">$OLD_PIN</span> <span class="o">&amp;&amp;</span> <span class="nb">test</span> <span class="nv">$n</span> -ge 0</div><div class='line' id='LC62'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">do</span></div><div class='line' id='LC63'><span class="k">      </span><span class="nb">printf</span> <span class="s1">&#39;.&#39;</span> <span class="o">&amp;&amp;</span> sleep 1 <span class="o">&amp;&amp;</span> <span class="nv">n</span><span class="o">=</span><span class="k">$((</span> <span class="nv">$n</span> <span class="o">-</span> <span class="m">1</span> <span class="k">))</span></div><div class='line' id='LC64'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">done</span></div><div class='line' id='LC65'><span class="k">    </span><span class="nb">echo</span></div><div class='line' id='LC66'><br/></div><div class='line' id='LC67'><span class="nb">    </span><span class="k">if </span><span class="nb">test</span> <span class="nv">$n</span> -lt 0 <span class="o">&amp;&amp;</span> <span class="nb">test</span> -s <span class="nv">$OLD_PIN</span></div><div class='line' id='LC68'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">then</span></div><div class='line' id='LC69'><span class="k">      </span><span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;$OLD_PIN still exists after $TIMEOUT seconds&quot;</span></div><div class='line' id='LC70'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">exit </span>1</div><div class='line' id='LC71'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">fi</span></div><div class='line' id='LC72'><span class="k">    </span><span class="nb">exit </span>0</div><div class='line' id='LC73'>&nbsp;&nbsp;<span class="k">fi</span></div><div class='line' id='LC74'><span class="k">  </span><span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;Couldn&#39;t upgrade, starting &#39;$CMD&#39; instead&quot;</span></div><div class='line' id='LC75'>&nbsp;&nbsp;run <span class="s2">&quot;$CMD&quot;</span></div><div class='line' id='LC76'>&nbsp;&nbsp;;;</div><div class='line' id='LC77'>reopen-logs<span class="o">)</span></div><div class='line' id='LC78'>&nbsp;&nbsp;sig USR1</div><div class='line' id='LC79'>&nbsp;&nbsp;;;</div><div class='line' id='LC80'>*<span class="o">)</span></div><div class='line' id='LC81'>&nbsp;&nbsp;<span class="nb">echo</span> &gt;&amp;2 <span class="s2">&quot;Usage: $0 &lt;start|stop|restart|upgrade|force-stop|reopen-logs&gt;&quot;</span></div><div class='line' id='LC82'>&nbsp;&nbsp;<span class="nb">exit </span>1</div><div class='line' id='LC83'>&nbsp;&nbsp;;;</div><div class='line' id='LC84'><span class="k">esac</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2564093/965c3b33099364218cc9ee1e033c658572ae179c/unicorn_ini.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2564093#file_unicorn_ini.sh" style="float:right;margin-right:10px;color:#666">unicorn_ini.sh</a> <a
href="https://gist.github.com/2564093">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>We have all the files we need, so let&#8217;s setup the server to deploy our app.  Push the latest changes to github and after that run the following command:</p><pre>cap deploy:setup</pre><p>This will create 2 folders under the /&lt;user&gt;/apps/&lt;app_name&gt; on your server. as well as some links for the unicorn and nginx configration files to place them in the proper location. After the setup is complete &#8220;cd&#8221; into the /&lt;user&gt;/apps/&lt;app_name&gt;/shared/config and edit the database.yml file to work with your database.</p><p>Once you&#8217;ve done this, give the server access to the git repository by running the following command on our development machine:</p><pre>ssh-add</pre><p>Now we&#8217;re all setup and ready to deploy. Let&#8217;s try this out by running:</p><pre>cap deploy:cold</pre><p>And watch it go.  A &#8220;cold&#8221; deploy will run the migrations and do a server start and restart.  In my experience, it will rarely work the first time you will usually get some errors here and there.  The errors will usually point you in the right direction.</p><p>If everything went OK, congratulations!  You just deployed your app using unicorn, nginx and capistrano. Hope you liked it!</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/deploying-your-rails-app-to-the-cloud-with-unicorn-nginx-and-capistrano/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Introduction to Using Redis with Rails</title><link>http://rubysource.com/introduction-to-using-redis-with-rails/</link> <comments>http://rubysource.com/introduction-to-using-redis-with-rails/#comments</comments> <pubDate>Tue, 15 May 2012 13:30:12 +0000</pubDate> <dc:creator>Peter Brindisi</dc:creator> <category><![CDATA[Misc]]></category> <category><![CDATA[Ruby Tutorials]]></category> <category><![CDATA[redis]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3768</guid> <description><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/05/redis-300dpi-150x150.png" class="attachment-thumbnail wp-post-image" alt="redis-300dpi" title="redis-300dpi" />Redis is a key-value store that stands out from others, like memcached, in that it has built-in support for data structures like lists, sets, and hashes, and that it can persist data to disk. As such, it is quite useful as both&#8230;]]></description> <content:encoded><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/05/redis-300dpi-150x150.png" class="attachment-thumbnail wp-post-image" alt="redis-300dpi" title="redis-300dpi" /><p><a
href="http://rubysource.com/?attachment_id=3775" rel="attachment wp-att-3775"><img
class="alignleft size-medium wp-image-3775" title="redis-300dpi" src="http://cdn.rubysource.com/files/2012/05/redis-300dpi-300x100.png" alt="" width="300" height="100" /></a><a
href="http://redis.io">Redis</a> is a key-value store that stands out from others, like <a
href="http://memcached.org/">memcached</a>, in that it has built-in support for data structures like lists, sets, and hashes, and that it can persist data to disk. As such, it is quite useful as both a cache store and as a full-fledged, NoSQL data store. In this article, we will walk through a basic usage example in order to learn how to use Redis within a Rails application.</p><h2>A Basic Example</h2><p>Imagine that we have written a simple blogging platform in Rails. We use MySQL as our main database, which is where we store all post content, comments, and user accounts. Let&#8217;s say it&#8217;s hosted at myrailsblog.com. We get regular traffic directly to posts, thanks to our game-changing social media strategy. There are direct links to these posts all over the internet.</p><p>The one thing we&#8217;re not crazy about is that a URL to a post looks like this: http://myrailsblog.com/321. Aside from the fact that it&#8217;s a little ugly and exposes internal ids, we could get better search engine rankings if we implemented <a
href="http://en.wikipedia.org/wiki/Slug_%28web_publishing%29">slugs</a> for post URLs. We would like the URL to look like this instead: http://myrailsblog.com/using-redis-with-rails.</p><p>So we decide that from now on, posts are going to have slugs which are auto-generated from the title. We will find posts by slug rather than by id. When a new post is created, it will generate a slug which gets stored in a new column of our posts table. However, our platform allows editing of post titles, and furthermore, we would like authors to be able to customize the slug in case the auto-generated one turns out to be undesirable for any reason. This means that a post URL can change after it has been published. What happens when someone follows an old URL? Rather than awkwardly spitting back a 404, our platform should find the correct post from the old slug. This means that all old slugs need to continue to reference the post.</p><p>Our natural approach as Rails developers to finding a post by its slug would be to implement something like this:<br
/><div
class="gistem"><div
id="gist-2596145" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="c1"># app/controllers/posts_controller.rb</span></div><div class='line' id='LC2'><span class="k">class</span> <span class="nc">PostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span></div><div class='line' id='LC3'>&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">before_filter</span> <span class="ss">:find_post</span></div><div class='line' id='LC5'>&nbsp;&nbsp;</div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="kp">protected</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">find_post</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">unless</span> <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">:slug</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">first</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># what do we do here?</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC13'><span class="k">end</span></div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'><span class="c1"># app/models/post.rb</span></div><div class='line' id='LC16'><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span></div><div class='line' id='LC17'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">to_param</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">slug</span></div><div class='line' id='LC19'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC20'><span class="k">end</span></div><div class='line' id='LC21'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596145/14e0fe02fc4ed29e0401b9e839372a62ccd477a7/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596145#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596145">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>The questions start flying when we think about the &#8220;what do we do here?&#8221; bit. Conceptually, we need to check the :id parameter against the post&#8217;s old slugs to see if any of them match. One possible approach would be to create a <code>Slug</code> model, and set <code>Post</code>s to <code>have_many :slugs</code>.</p><p>However, we can solve this problem a little more elegantly with Redis by mapping slugs to post ids. We can utilize the hash data type to store all slugs under one main key. We will get O(1) lookup time, and we won&#8217;t need to migrate the database. In the end, finding a post will look like this instead:<br
/><div
class="gistem"><div
id="gist-2596151" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'># app/controllers/post_controller.rb</div><div class='line' id='LC2'>class PostsController &lt; ApplicationController</div><div class='line' id='LC3'>&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;before_filter :find_post</div><div class='line' id='LC5'>&nbsp;&nbsp;</div><div class='line' id='LC6'>&nbsp;&nbsp;protected</div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;def find_post</div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@post = Post.find(Slug[params[:id]])</div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;rescue ActiveRecord::RecordNotFound</div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redirect_to root_path  </div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC13'>end</div><div class='line' id='LC14'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596151/08eb1840506aa06cfe550264c099385ebab5f5ce/gistfile1.txt" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596151#file_gistfile1.txt" style="float:right;margin-right:10px;color:#666">gistfile1.txt</a> <a
href="https://gist.github.com/2596151">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><h2>Adding Redis</h2><p>Getting up and running with Redis is as simple as compiling the server, running it, and connecting to it. Downloading and installing Redis is outside the scope of this article, but there are detailed instructions <a
href="http://redis.io/download">here</a>. Once it has been successfully installed, it can be run by opening a new terminal window and typing <code>redis-server</code>.</p><p>Now that the server is running, we need to let our app talk to it. First, we need to install the redis gem. With bundler, this is as simple as adding the following to the Gemfile, and running <code>bundle install</code>:<br
/><div
class="gistem"><div
id="gist-2596166" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>gem &#39;redis&#39;</div><div class='line' id='LC2'>gem &#39;redis-namespace&#39;</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596166/19cb48e846cfb406951df58a1265bde93c415d64/gistfile1.txt" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596166#file_gistfile1.txt" style="float:right;margin-right:10px;color:#666">gistfile1.txt</a> <a
href="https://gist.github.com/2596166">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>We then need to connect to the server and store that connection as a global resource. Additionally, we are going to use the &#8220;redis-namespace&#8221; gem to organize everything under one application-wide namespace. This will help enormously down the road when multiple apps use the same Redis server. Create a file called &#8220;redis.rb&#8221; in your config/initializers folder with the following:<br
/><div
class="gistem"><div
id="gist-2596173" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="vg">$redis</span> <span class="o">=</span> <span class="no">Redis</span><span class="o">::</span><span class="no">Namespace</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&quot;my_app&quot;</span><span class="p">,</span> <span class="ss">:redis</span> <span class="o">=&gt;</span> <span class="no">Redis</span><span class="o">.</span><span class="n">new</span><span class="p">)</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596173/bbf7bda39af354399400f3fed35e98ba2510e0a0/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596173#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596173">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>That&#8217;s it. We may now issue Redis commands directly to the <code>$redis</code> global variable. Let&#8217;s test it out by opening a Rails console and executing the following commands:<br
/><div
class="gistem"><div
id="gist-2596180" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>&gt; <span class="nv">$redis</span>.set<span class="o">(</span><span class="s2">&quot;foo&quot;</span>, <span class="s2">&quot;bar&quot;</span><span class="o">)</span></div><div class='line' id='LC2'>&nbsp;<span class="o">=</span>&gt; <span class="s2">&quot;OK&quot;</span> </div><div class='line' id='LC3'>&gt; <span class="nv">$redis</span>.get<span class="o">(</span><span class="s2">&quot;foo&quot;</span><span class="o">)</span></div><div class='line' id='LC4'>&nbsp;<span class="o">=</span>&gt; <span class="s2">&quot;bar&quot;</span> </div><div class='line' id='LC5'>&gt; <span class="nv">$redis</span>.get<span class="o">(</span><span class="s2">&quot;baz&quot;</span><span class="o">)</span></div><div class='line' id='LC6'>&nbsp;<span class="o">=</span>&gt; nil</div><div class='line' id='LC7'>&gt; <span class="nv">$redis</span>.del<span class="o">(</span><span class="s2">&quot;foo&quot;</span><span class="o">)</span></div><div class='line' id='LC8'><span class="o">=</span>&gt; 1 </div><div class='line' id='LC9'>&gt; <span class="nv">$redis</span>.get<span class="o">(</span><span class="s2">&quot;foo&quot;</span><span class="o">)</span></div><div class='line' id='LC10'>&nbsp;<span class="o">=</span>&gt; nil </div><div class='line' id='LC11'>&gt;</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596180/be5ab746905e0546fe3d82ded1155fc6513096d3/gistfile1.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596180#file_gistfile1.sh" style="float:right;margin-right:10px;color:#666">gistfile1.sh</a> <a
href="https://gist.github.com/2596180">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Cool. We can see that the instance methods of the Redis class are the same as the the Redis commands, found <a
href="http://redis.io/http://redis.io/commands">here</a>. For simple get/set operations, we can also use array notation:<br
/><div
class="gistem"><div
id="gist-2596185" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>&gt; <span class="nv">$redis</span><span class="o">[</span><span class="s2">&quot;foo&quot;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;bar&quot;</span></div><div class='line' id='LC2'>&nbsp;<span class="o">=</span>&gt; <span class="s2">&quot;bar&quot;</span> </div><div class='line' id='LC3'>&gt; <span class="nv">$redis</span><span class="o">[</span><span class="s2">&quot;foo&quot;</span><span class="o">]</span></div><div class='line' id='LC4'>&nbsp;<span class="o">=</span>&gt; <span class="s2">&quot;bar&quot;</span> </div><div class='line' id='LC5'>&gt; <span class="nv">$redis</span><span class="o">[</span><span class="s2">&quot;baz&quot;</span><span class="o">]</span></div><div class='line' id='LC6'>&nbsp;<span class="o">=</span>&gt; nil </div><div class='line' id='LC7'>&gt; <span class="nv">$redis</span>.del<span class="o">(</span><span class="s2">&quot;foo&quot;</span><span class="o">)</span></div><div class='line' id='LC8'>&nbsp;<span class="o">=</span>&gt; 1 </div><div class='line' id='LC9'>&gt; <span class="nv">$redis</span><span class="o">[</span><span class="s2">&quot;foo&quot;</span><span class="o">]</span></div><div class='line' id='LC10'>&nbsp;<span class="o">=</span>&gt; nil </div><div class='line' id='LC11'>&gt;</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596185/13fb2414799ebe4c293462fd42db7bcc0c12508c/gistfile1.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596185#file_gistfile1.sh" style="float:right;margin-right:10px;color:#666">gistfile1.sh</a> <a
href="https://gist.github.com/2596185">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Now we are ready to dig a little deeper.</p><h2>The <code>Slug</code> Model</h2><h3>Fetching</h3><p>The Slug model has one main purpose: to turn a slug into a post id. We are going to utilize the hash data type in order to avoid polluting the key space with a bunch of slugs. As such, all slugs will reside under the single key, &#8220;slugs.&#8221; Our first pass at <code>app/models/slug.rb</code> looks like this:<br
/><div
class="gistem"><div
id="gist-2596191" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Slug</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">[]</span><span class="p">(</span><span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">hget</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="kp">private</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">redis</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vg">$redis</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">hash</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;post_ids&quot;</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC17'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC18'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596191/37c5a4ffe02b5eb68c4b70a6434e5b40cb8c232d/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596191#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596191">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>We simply fetch the value of a specific slug within the hash &#8220;slugs.&#8221; If the key doesn&#8217;t exist, Redis returns <code>nil</code>, which will cause our <code>PostsController</code> to redirect to the root path. Fetching is easy, but we need to be able to store slug/id mappings for this to have any effect.</p><h3>Storing</h3><p>Storing values is simple enough as well. We can add a <code>PostObserver</code> to our app with the following code:<br
/><div
class="gistem"><div
id="gist-2596197" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">PostObserver</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">after_save</span><span class="p">(</span><span class="n">post</span><span class="p">)</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Slug</span><span class="o">[</span><span class="n">post</span><span class="o">.</span><span class="n">slug</span><span class="o">]</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">to_s</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'><span class="k">end</span></div><div class='line' id='LC7'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596197/ea8efd33a688f6070d360b8f7736093dc6ac11e7/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596197#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596197">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Then, we can modify our <code>Slug</code> model to allow for mapping a slug to a post id:<br
/><div
class="gistem"><div
id="gist-2596202" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Slug</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">[]</span><span class="p">(</span><span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">hget</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">[]=</span><span class="p">(</span><span class="n">slug</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">hset</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">slug</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="kp">private</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">redis</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vg">$redis</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">hash</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;post_ids&quot;</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC21'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC22'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596202/10530232cc429c51576dfe1938c8dd973b528f48/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596202#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596202">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><h3>Deleting</h3><p>The last thing we&#8217;ll need to handle is clearing out unused slugs when a post is destroyed. This is because we will need to validate slug uniqueness against all slugs. If a slug points to a post that no longer exists, it will be unusable for future posts. We will revisit this a bit later.</p><p>As it turns out, deleting slugs presents an interesting problem: how do we know which slugs reference a particular post id? As we have seen, we can easily find a post id given a slug, but we don&#8217;t have an easy way to find all slugs that reference a given post id. With our current implementation, we would have to search every slug in the hash and select only those which map to a given post id. It would be a linear-time operation over the total number of slugs, and this is way too slow. This problem is also a good excuse to introduce Redis&#8217;s &#8220;set&#8221; data type.</p><p>The change is just a bit of simple bookkeeping. Every post id will have an associated set whose members are the slugs which map to that post id. That is, whenever we map a slug to a post id, we will also add that slug to the set of slugs associated with that post id. Moreover, as it is possible to update a slug to point to a different post id, we will first need to remove that slug from the set of slugs for the old post id.<br
/> Here&#8217;s the change:<br
/><div
class="gistem"><div
id="gist-2596216" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>class Slug</div><div class='line' id='LC2'>&nbsp;&nbsp;class &lt;&lt; self</div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;def [](slug)</div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.hget(hash, slug)</div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;def []=(slug, id)</div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if old = self[slug]</div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.srem(set(old), slug)</div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.hset(hash, slug, id)</div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.sadd(set(id), slug)</div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;private</div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def redis</div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$redis</div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def hash</div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;post_ids&quot;</div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def set(id)</div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;post_slugs_#{id}&quot;</div><div class='line' id='LC28'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end</div><div class='line' id='LC29'>&nbsp;&nbsp;end</div><div class='line' id='LC30'>end</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596216/6aa0d6928a83012b60846cad17d289ae4ce6cade/gistfile1.txt" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596216#file_gistfile1.txt" style="float:right;margin-right:10px;color:#666">gistfile1.txt</a> <a
href="https://gist.github.com/2596216">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Now that we have an efficient way to fetch all slugs for a given post id, we are ready to implement the <code>destroy</code> method:<br
/><div
class="gistem"><div
id="gist-2596220" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Slug</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">[]</span><span class="p">(</span><span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">hget</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">[]=</span><span class="p">(</span><span class="n">slug</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="n">old</span> <span class="o">=</span> <span class="nb">self</span><span class="o">[</span><span class="n">slug</span><span class="o">]</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">srem</span><span class="p">(</span><span class="n">set</span><span class="p">(</span><span class="n">old</span><span class="p">),</span> <span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">hset</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">slug</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">sadd</span><span class="p">(</span><span class="n">set</span><span class="p">(</span><span class="nb">id</span><span class="p">),</span> <span class="n">slug</span><span class="p">)</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">destroy</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">smembers</span><span class="p">(</span><span class="n">set</span><span class="p">(</span><span class="nb">id</span><span class="p">))</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">slug</span><span class="o">|</span> <span class="n">redis</span><span class="o">.</span><span class="n">hdel</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">slug</span><span class="p">)</span> <span class="p">}</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redis</span><span class="o">.</span><span class="n">del</span><span class="p">(</span><span class="n">set</span><span class="p">(</span><span class="nb">id</span><span class="p">))</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="kp">private</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">redis</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vg">$redis</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC26'><br/></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">hash</span></div><div class='line' id='LC28'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;post_ids&quot;</span></div><div class='line' id='LC29'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">set</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC32'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;post_slugs_</span><span class="si">#{</span><span class="nb">id</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC34'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC35'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596220/49e5978c522c4a0e3deffe483422b8cedd4a1e22/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596220#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596220">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>We will destroy the mapping from the <code>PostObserver</code>:<br
/><div
class="gistem"><div
id="gist-2596225" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">PostObserver</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">after_save</span><span class="p">(</span><span class="n">post</span><span class="p">)</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Slug</span><span class="o">[</span><span class="n">post</span><span class="o">.</span><span class="n">slug</span><span class="o">]</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">id</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'>&nbsp;&nbsp;</div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">after_destroy</span><span class="p">(</span><span class="n">post</span><span class="p">)</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Slug</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">post</span><span class="o">.</span><span class="n">id</span><span class="p">)</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC10'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC11'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596225/04d97b4b8736e96071da3e5b2d28b944e86df8f1/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596225#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596225">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><h2>Uniqueness</h2><p>As mentioned above, another important aspect of our slug implementation is making sure that a post never has a slug that is mapped to another. In our <code>Post</code> model, we must implement a validation that ensures there is a slug, and that it either does not point to a post id, or it points to the same post we&#8217;re currently saving (in the case of an update):<br
/><div
class="gistem"><div
id="gist-2596235" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="c1"># app/models/post.rb</span></div><div class='line' id='LC2'><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span></div><div class='line' id='LC3'>&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">validate</span> <span class="ss">:ensure_slug_uniqueness</span></div><div class='line' id='LC5'>&nbsp;&nbsp;</div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="kp">protected</span></div><div class='line' id='LC7'>&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># validate</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">ensure_slug_uniqueness</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># we also want to ensure the slug is not blank</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="nb">self</span><span class="o">.</span><span class="n">slug</span><span class="o">.</span><span class="n">blank?</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="ss">:slug</span><span class="p">,</span> <span class="s2">&quot;can&#39;t be blank&quot;</span><span class="p">)</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># if this is a new post, the id is nil</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># otherwise, the slug should point to this post&#39;s id</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">unless</span> <span class="no">Slug</span><span class="o">[</span><span class="nb">self</span><span class="o">.</span><span class="n">slug</span><span class="o">].</span><span class="n">nil?</span> <span class="o">||</span> <span class="no">Slug</span><span class="o">[</span><span class="nb">self</span><span class="o">.</span><span class="n">slug</span><span class="o">]</span> <span class="o">==</span> <span class="nb">self</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">to_s</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="ss">:slug</span><span class="p">,</span> <span class="s2">&quot;is already taken&quot;</span><span class="p">)</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC22'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596235/7a9bcbd72fc3681d36949ffcc7a42531a5deaddd/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596235#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596235">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Note that this means that slug auto-generation needs to take place before validation.</p><h2>Save Post IDs as Slugs</h2><p>The last little bit of bookkeeping involves mapping post ids to themselves in Redis. As mentioned above, there are links all over the internet in this format: http://myrailsblog.com/321. After we launch our slug implementation, when someone visits a post URL, we will look for &#8220;321&#8243; in Redis to see if it maps to a post id. Unless we want all of these old URLs to redirect to the homepage, we&#8217;ll need to run a simple script:<br
/><div
class="gistem"><div
id="gist-2596239" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="no">Post</span><span class="o">.</span><span class="n">find_each</span> <span class="p">{</span> <span class="o">|</span><span class="n">post</span><span class="o">|</span> <span class="no">Slug</span><span class="o">[</span><span class="n">post</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">to_s</span><span class="o">]</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">to_s</span> <span class="p">}</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596239/ec4df98d704e8edc80ea1be635327dd50d80cbe8/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596239#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596239">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><p>Of course, the other option is to modify the <code>PostController</code> to also find Posts by id in the event that Redis has no mapping, but this is slightly less efficient:<br
/><div
class="gistem"><div
id="gist-2596245" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="c1"># app/controller/posts_controller.rb</span></div><div class='line' id='LC2'><span class="k">class</span> <span class="nc">PostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span></div><div class='line' id='LC3'>&nbsp;&nbsp;</div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">before_filter</span> <span class="ss">:find_post</span></div><div class='line' id='LC5'>&nbsp;&nbsp;</div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="kp">protected</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">find_post</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="nb">id</span> <span class="o">=</span> <span class="no">Slug</span><span class="o">[</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]]</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">rescue</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">RecordNotFound</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">redirect_to</span> <span class="n">root_url</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC17'><span class="k">end</span></div><div class='line' id='LC18'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2596245/6c45d493c45f8fccb98ca37eb09fb862a5e67da1/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2596245#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2596245">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></p><h2>All Done</h2><p>With that, we have successfully implemented slugs for our blogging platform. Even if an old post URL is followed, our platform will still find the correct post. Moreover, we did all of this without needing to modify the database schema, and we get the benefits of the speedy lookup time of Redis.</p><p>I hope you have found this example useful, and continue to find creative ways to use Redis on your own.</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/introduction-to-using-redis-with-rails/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>A Conversation With Mark Bates About How To Learn CoffeeScript</title><link>http://rubysource.com/a-conversation-with-mark-bates-about-how-to-learn-coffeescript/</link> <comments>http://rubysource.com/a-conversation-with-mark-bates-about-how-to-learn-coffeescript/#comments</comments> <pubDate>Fri, 11 May 2012 13:30:07 +0000</pubDate> <dc:creator>Pat Shaughnessy</dc:creator> <category><![CDATA[Interviews]]></category> <category><![CDATA[CoffeeScript]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3749</guid> <description><![CDATA[I’d heard second hand for a long time that Mark Bates was a great public speaker, but it wasn’t until the March meeting of Boston.rb that I had a chance to find out for myself. He gave a great presentation called Testing&#8230;]]></description> <content:encoded><![CDATA[<div
id="attachment_3748" class="wp-caption alignleft" style="width: 187px"><a
href="http://books.markbates.com"><img
src="http://cdn.rubysource.com/files/2012/05/programming_in_coffeescript.png" alt="" width="177" height="228" /></a><p
class="wp-caption-text">Mark's <a
href='http://books.markbates.com'>latest book</a> was just <br
/>published by Addison-Wesley</p></div><p>I’d heard second hand for a long time that <a
href="http://metabates.com/">Mark Bates</a> was a great public speaker, but it wasn’t until the March meeting of <a
href="http://bostonrb.org/">Boston.rb</a> that I had a chance to find out for myself. He gave a great presentation called <a
href="http://www.slideshare.net/markykang/testing-rich-client-side-apps-with-jasmine">Testing Rich Client Side Apps with Jasmine</a>, about how to use Jasmine and other tools to test web applications that use <a
href="http://coffeescript.org/">CoffeeScript</a> on the client. It was informative, entertaining and thought provoking. Despite being an experienced Ruby developer I’d never taken the time to learn how to use CoffeeScript properly in my applications. But listening to Mark and seeing his examples inspired me; I thought to myself: “This is something I need to learn and use as soon as possible.”</p><p>I was excited a few weeks later when I had a chance to interview Mark myself – what better way to get started learning CoffeeScript! First I took the time to read his book, <a
href="http://books.markbates.com">Programming in CoffeeScript</a>, which was fantastic and very helpful. Then we spent about an hour talking about CoffeeScript: how it was invented, what other languages inspired and influenced CoffeeScript, how a Ruby developer should start to learn it, testing and more… I’ve typed in the highlights of our conversation here. If you’re a Ruby developer interested in learning more about CoffeeScript here’s your chance to hear from a well known developer, author and thought leader about how to get started.</p><h2>Who Is Mark Bates?</h2><div
id="attachment_3747" class="wp-caption alignright" style="width: 238px"><img
src="http://cdn.rubysource.com/files/2012/05/mark.jpg" alt="" width="228" height="237" /><p
class="wp-caption-text">Mark Bates</p></div><p>Mark Bates is founder and chief architect of the Boston-based development and consulting company, Meta42 Labs. He has developed web applications since 1996. Since discovering CoffeeScript, he has become a leader in its programming community, presenting to user groups and speaking at high profile conferences around the world, such as RubyConf, RailsConf, and jQueryConf. He is the author of the books <a
href="http://books.markbates.com">Distributed Programming with Ruby</a> and <a
href="http://books.markbates.com">Programming in CoffeeScript</a>.</p><p>Q: Hi Mark – thanks for talking with me this morning…</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>You’re lucky I’m awake! I accidentally took a few Tylenol PM this morning, the fact that I’m sitting upright right now is amazing…</p><h2>RailsConf</h2><p>Q: I heard you did a great presentation <a
href="http://www.slideshare.net/markykang/coffeescript-for-the-rubyist">CoffeeScript for the Rubyist</a> at RailsConf on April 23rd. How did it go? How was the conference in general?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>The talk went great, excellent reception. It helped that I had an excellent time slot, morning of the first day, most people were still sober, of course being drunk probably would’ve helped my presentation! Judging by the people who came up to me during the conference, and on even on the plane ride home, I must’ve done something right, because I’m sure I made a few converts.</p><p>The conference overall was really good. The theme that DHH put forth was progress. Keep looking towards the future, learn the new technologies, keep growing as a developer. CoffeeScript got quite a high profile mention during the keynote and it was mentioned, a lot, throughout a lot of the other talks I saw. I would say that I heard more people discussing CoffeeScript, Backbone, and Ember more than Rails itself. Really showed where people are heading with their development.</p><h2>Should you even use CoffeeScript?</h2><p>Q: There are some people out there who prefer the “pure” JavaScript syntax and don’t like this idea of precompiling it. Is it even a good idea to use CoffeeScript?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>To them honestly I would say: “fine, if you want to keep writing it that way it’s up to you.” I look at it as very similar to why I moved to Ruby from Java. Ruby obviously has some features that Java doesn’t have, and Java has some features that Ruby doesn’t have – all languages do. I can write a web app in Java, just as well in I can in Ruby…</p><p>Q: We all used to do that…</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>But I prefer writing it in Ruby because I can write a lot less code to do the same thing. That’s the way I feel about CoffeeScript.</p><div
style="clear: left"></div><p>Q: Is there also something about the beauty or elegance of the CoffeeScript code?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Oh yea – again, the same thing with Java to Ruby. I can write a lot less code; it’s much cleaner and easier to read; it’s easier to maintain. I get a lot of people saying that they hate the fact that CoffeeScript is whitespace significant. To them I always say: “Well, how are you formatting your JavaScript?” You should be doing that anyway – you should be formatting your code nicely. At that point the whitespace significance really becomes insignificant whitespace, as you’re doing that already.</p><h2>Should you learn CoffeeScript first, or JavaScript?</h2><p>Q: Should I learn JavaScript first, before CoffeeScript? I remember <a
href="http://techiferous.com">Wyatt Greene</a> at that Boston.rb meeting did another great talk right before you, <a
href="http://techiferous.com/slides/javascript/">From Ruby to JavaScript</a>. One of the things he said was that he wanted to learn JavaScript thoroughly first, before starting to learn CoffeeScript. Do you think that’s a good idea?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>I think it’s a must-have idea. In my book, there are numerous places where I actually recommend to the reader: “Put the book down – go read something on JavaScript first and then come back again” if they don’t know it. It is very important that you know what it is doing. You will learn some really interesting things about JavaScript from CoffeeScript; I certainly did. It’s made me a better JavaScript developer, but I was a JavaScript developer first.</p><p>Q: Is it OK to be a casual JavaScript developer? Or do you really need to thoroughly understand it before trying to learn CoffeeScript?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>You should be at least an intermediate JavaScript developer, because some of those concepts get a little hairy and CoffeeScript takes some of that stuff away from you. If you’re beginning and you don’t really understand what makes a function a function or how callbacks work or how prototypes work, you should brush up on all of that first.</p><p>Q: …because CoffeeScript might make it harder to understand?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Not harder, but it abstracts it all away. Knowing what JavaScript can do, and how to it, means you can really take more advantage of what CoffeeScript has to offer.</p><p>Q: It makes it easier not to learn JavaScript thoroughly, I guess.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Exactly. I can see people easily jumping into CoffeeScript and generating some pretty cool code without knowing what it’s really generating. The minute it generates something they don’t understand it’s going to really bite them in the ass, you know? When they don’t understand why the scope is not correct. Or, why something is not equal to another thing their lack of JavaScript is really going to hurt them.</p><h2>How would you go about learning CoffeeScript?</h2><p>Q: If you’re a Rails developer or a Ruby developer of some sort, what’s the right way to learn CoffeeScript?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>I hope reading my book, <a
href="http://books.markbates.com">Programming in CoffeeScript</a> would be a great way to learn it. For Rubyists, there’s a little bit of Ruby in the book, but I wanted to make it accessible to everybody, not just Rubyists. I used Node.js to build the sample application in the last three chapters for a couple of reasons:</p><ol><li>Because Node.js and CoffeeScript are quite commonly linked together and you see a lot of Node apps being written in CoffeeScript.</li><li>Because I could write the back end in CoffeeScript. It was a great way to just delve into the language and to see real examples of the language being used.</li></ol><p>Q: I thought that was really cool! I kept thinking to myself: “I can’t believe I’m writing a server in CoffeeScript!”</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Yea! It’s pretty cool. In the book we write the server and the front end in the same language, in CoffeeScript, which I thought was actually kind of fun and kind of cool. But if I had done it in Rails, then I would have alienated PHP people, Python people, Java people, and there was no need to write it in a different language because I could write it in the language of the book, which was a really fun exercise.</p><p>For Rails people, it’s pretty straightforward. You fire up Rails and the support for CoffeeScript is just there. Just start using it.</p><p>Q: I think that’s where I am: I’m a Rails developer but I’m not a CoffeeScript expert. I use it here or there; it just sort of works. Again, it’s almost too easy – you don’t really need to spend the time to learn it deeply.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>It is easy with Rails to just start using CoffeeScript and get to a point where you think you know it and know it well. It’s not a horribly large language; it’s actually pretty simple. But there are a lot of really cool little hidden things in this language.</p><p>Q: If I’m a Rails developer, should I just install Node.js and learn it the way you present in the book? Or is it ok just to use it inside the Asset Pipeline?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>I think it’s ok just to use it inside the asset pipeline. Using it is more important than playing with it. That’s the way I feel about any language. I don’t learn a language by building tinker toy little apps, I learn by using it for an actual app. That’s how I learn: sink or swim. If you’ve got an application you’re building and you need JavaScript, just start using CoffeeScript! You can mix and mingle regular JavaScript and CoffeeScript files in the same application. You’ll learn more about CoffeeScript, or any language, by using it for real than you ever would just by creating a little “to do” app. You starting saying: “I really need to learn how to do X, Y and Z to solve this problem.”</p><p>Q: And so you have to learn about X, Y, and Z.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Exactly; it’s not a contrived problem. Your business is barking for this feature and you’ve got to learn how to do it.</p><h2>Origins of CoffeeScript</h2><p>Q: What inspired CoffeeScript? Was it inspired Ruby? Or by Python? Where are its roots?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>It was inspired by both, and Lisp, Smalltalk, whatever excited <a
href="http://twitter.com/jashkenas">Jeremy Ashkenas</a>. Which is why if you look at it, it looks like a bunch of different languages. It’s got the significant whitespace of Python, and some things like the @ symbol and class definitions that are very Rubyesque. Jeremy wrote it originally because he had read <a
href="http://createyourproglang.com/">How To Create Your Own Freaking Awesome Programming Language</a> by <a
href="http://macournoyer.com/">Marc-André Cournoyer</a>. He read this book and started tinkering with it. The original implementation of CoffeeScript was in Ruby – the original compiler.</p><p>Q: Is it all in C code now, or something?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>No, it’s all written in CoffeeScript!</p><div
style="clear: left"></div><p>Q: It’s written in CoffeeScript itself? Sounds like Rubinius….</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Yea. Exactly. It’s one of those things that always blows my mind. Rubinius blows my mind in that it’s a Ruby implementation written in Ruby that runs on Ruby. How the world doesn’t implode when someone starts up a Rubinius server I will never know.</p><p>With something like a compiler it’s a little easier because you write the first compiler in something else, and then you have the language to use to write the next compiler. The first compiler was written in Ruby. But then he took version X which was written in Ruby and then wrote the CoffeeScript compiler in version X of CoffeeScript. If you <a
href="https://github.com/jashkenas/coffee-script">go to github</a>, it’s all in CoffeeScript!</p><p>Q: With Rubinius it’s great how if you’re not sure how something works, e.g. “How does the String.slice method work?” – you can just go and look at the Ruby source code.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Yea with CoffeeScript it’s all in CoffeeScript. If you want to know how class support works in CoffeeScript – you just go read the CoffeeScript source code and you’ll see it right there. It’s actually pretty readable source code. It much more approachable than, for example, the Ruby source code.</p><h2>Ember.js vs. Backbone.js</h2><p>Q: I wanted to ask you about <a
href="http://emberjs.com/">Ember.js</a> vs. <a
href="http://documentcloud.github.com/backbone/">Backbone.js</a>. I don’t know if that’s a controversy or not; they’re sort of just different things. You’ve said you use Backbone a lot – I’m curious why? And have you tried Ember? What’s the difference between them?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>In some respects they are wildly different. I think a better comparison would be Backbone.js vs. <a
href="http://spinejs.com/">Spine</a> or <a
href="http://knockoutjs.com/">Knockout</a>. I like Backbone a lot – I find it incredibly simple, easy to use and easy to understand. It makes a lot of sense to me. Backbone let’s you create models, collections and views to manage your HTML, and deal with the events that happen on these models and collections, dynamically call functions to, say, re-render that once it’s been updated.</p><p>Ember takes a different approach and says that you have to mark up your HTML in a certain way. And if you do that, then auto-updating of attributes will happen automatically. If I mark up my &lt;h1&gt; correctly that has the name of a user, and in my User object I change the name of that user then that &lt;h1&gt; tag will get updated without me having to do anything else.</p><p>Q: So in Backbone you need to do that updating yourself?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Exactly. But I think Ember’s updating is simultaneously both slick and dangerous.</p><div
style="clear: left"></div><p>Q: Because it might be happening when you don’t expect it to?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>There might be times when you don’t want to update elements on the page, for whatever reason. I’m also anti-adding-extra-markup to my pages, if I don’t need it, which is one of the reasons why I don’t like Knockout. Knockout works almost entirely by adding extra data elements to your HTML. If down the line you want to change something you end up with all of this extra cruft.</p><p>One of the things I like about Backbone is that it cuts down the amount of markup I have to put in there. I don’t need IDs so much any more or even class names, just so I can find a particular element. They all get scoped to views. If you write your views correctly you can do a generic search for a list element; you don’t need to worry about making sure it’s the right set of list elements.</p><p>I’m actually going to be writing a new Backbone eBook soon.</p><p>Q: Fantastic! By the way, how did you get your start writing books? It’s very impressive you’ve already published two…</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>I got started writing by getting <a
href="http://obiefernandez.com/">Obie Fernandez</a> drunk in a hot tub! Obie had seen me talk at RubyConf 2008 about distributed programming and we were in the hot tub hanging out with the <a
href="http://hashrocket.com/">Hashrocket</a> folks, we were having a few drinks, and I mentioned to them that a couple people had asked me after my talk about where to get more information on distributed programming. Then Obie told me he was the Ruby series editor at Addison-Wesley and that distributed programming would be a great book topic… and it went from there.</p><h2>Testing CoffeeScript</h2><p>Q: And do you want to tell readers about the presentation I saw at Boston.rb in March? I was struck how even Boston.rb members didn’t do much JavaScript testing…</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>At that talk, there were 100 people there, and I asked the room: “How many people test their Ruby code?” and 100 people raised their hand. And that’s very indicative of the Ruby community. And then I asked the room: “How many people write JavaScript code?” And again everybody raised their hand. Then I asked “How many people test their JavaScript code?” And just 6 people raised their hand.</p><p>Q: And I was not one of them, trust me.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>That’s just an awful number. Only 6% of the Ruby developers in that room test their JavaScript? And they all write JavaScript! So why is it ok to test Ruby and not JavaScript?</p><p>Q: I think the reason why, in my opinion, is that JavaScript is the stunted little stepson of the family and no one really takes it seriously. And in the past few years most people did not write much JavaScript; there were just a few lines here or their for validation, a little Ajax, etc. So I’m answering your question – you’re supposed to be answering the questions! But now suddenly people realize: “I can write big apps just with JavaScript.”</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>I also think there’s a fear that JavaScript would be too difficult to test. If it needs to run in a browser, how am I going to do that? How am I going to test DOM interaction? How am I going to test AJAX?</p><p>Q: For Rubyists who have this fear… what should people do to learn how to test their JavaScript?</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>That’s a great question! There’s a lot of great resources out there: look at <a
href="http://pivotal.github.com/jasmine/">Jasmine</a>, look at <a
href="http://visionmedia.github.com/mocha/">Mocha</a>, look at <a
href="http://chaijs.com/">Chai</a>. There are a lot of really great libraries that resemble the libraries you’re already using to test Ruby.</p><p>Q: Yea in your presentation I was struck how much Jasmine looks like RSpec.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>Exactly. And same thing with Mocha and Chai: they look very much like RSpec.</p><p>One of the things that I’m very surprised at, to the point where if I had the time and the inclination I’d probably sit down and try to write a big pull request for Rails 4, is how Rails doesn’t include testing tools for JavaScript.</p><p>When they introduced the asset pipeline and CoffeeScript in Rails 3.1 – and I think CoffeeScript lowers the barrier to entry for testing in JavaScript because it feels so much more natural like you’re writing Ruby – why didn’t they include testing tools? That weighs in my mind. From very early on in Rails, there was a test framework right there and there was so much emphasis on: “Look how easy it is to test your code.”</p><p>Q: To be honest, that’s a big reason why it caught on – because it was there in the first place.</p><div
style="float: left;padding: 5px 10px 0px 0px"><img
src="http://cdn.rubysource.com/files/2012/05/mark_small.jpg" alt="" /></div><p>… and because it was easy to do. Rails made it super easy to do. I’ll be honest – in all my years of writing Java, ask me how many Java unit tests I wrote! It was not a lot; I’m not going to lie to you! And Rails came along, and wow there was Test::Unit, which I was not a fan of, but it was right there, baked right in and I could start testing easily.</p><p>So why the team never put in testing tools for JavaScript blows my mind. What I demonstrated at Boston.rb should have been in the framework. And every time Rails generates one of these .coffee files for you, it should generate a test for you.</p><h2>Thanks!</h2><p>Q: Thanks for all your time, Mark!</p><p>Sure thing, thanks Pat.</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/a-conversation-with-mark-bates-about-how-to-learn-coffeescript/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>DDD for Rails Developers. Part 3: Aggregates.</title><link>http://rubysource.com/ddd-for-rails-developers-part-3-aggregates/</link> <comments>http://rubysource.com/ddd-for-rails-developers-part-3-aggregates/#comments</comments> <pubDate>Wed, 09 May 2012 13:30:29 +0000</pubDate> <dc:creator>Victor Savkin</dc:creator> <category><![CDATA[Best Practices]]></category> <category><![CDATA[ddd]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3715</guid> <description><![CDATA[My Previous Articles About DDD for Rails Developers In Part 1, I talked about using the layered architecture for tackling domain complexity. I showed a few typical violations of the layered architecture and gave some advice on how to fix them. In&#8230;]]></description> <content:encoded><![CDATA[<h2>My Previous Articles About DDD for Rails Developers</h2><p><a
href="http://rubysource.com/ddd-for-rails-developers-part-1-layered-architecture/">In Part 1</a>, I talked about using the layered architecture for tackling domain complexity. I showed a few typical violations of the layered architecture and gave some advice on how to fix them.</p><p><a
href="http://rubysource.com/ddd-for-rails-developers-part-2-entities-and-values/">In Part 2</a>, I started talking about the building blocks of Domain Driven Design. I wrote about an important distinction between Entities and Value Objects. I also gave some advice on how to implement Value Objects in Rails.</p><h2>Aggregates</h2><p>This time I&#8217;d like to go into another building block of Domain Driven Design. I&#8217;d like to talk about Aggregates.</p><p>We&#8217;ve all experienced this situation before:</p><p>You start with nicely designed groups of objects. All the objects have clear responsibilities, and all interactions among them are explicit. Then, you have to consider additional requirements, such as transactions, integration with external systems, event generation. Satisfying all of them and not making all the objects interconnected is a nontrivial task. What usually happens is database hooks, conditional validations, and remote calls are added on an ad hoc basis. The result is more connections among objects. Hence, the boundaries of object groups become fuzzy and enforcing invariants becomes harder. Remember all the cases when you were thinking, &#8220;Maybe I need to reload this object?&#8221; It indicates that your objects are interconnected, and you cannot reason about your code with confidence. Instead, you just guess.</p><p>Defining Aggregates is a good remedy for the described situation.</p><ul><li>&#8220;An Aggregate is a cluster of associated objects that are treated as a unit for the purpose of data changes.&#8221; (see <a
href="#resources">Resources</a>)</li><li>An Aggregate consists of a few Entities and Value Objects, one of which is chosen to be the root of the Aggregate.</li><li>All external references are restricted to the root. Objects outside the Aggregate can hold references to the root only.</li><li>Accessing other members of the Aggregate happens through the root. Therefore, nobody (outside the Aggregate) should hold references to those objects.</li><li>As all external objects can hold reference only to the root, enforcing invariants becomes easier.</li><li>Aggregates help to reduce the number of bidirectional associations among objects in the system because you are allowed to store references only to the root. That significantly simplifies the design and reduces the number of blindsided changes in the object graph.</li></ul><h2>Example</h2><p>It may sound too abstract, so I&#8217;d like to show you an example. I&#8217;m going to model an online bookstore. The main responsibility of the model will be selling and shipping books. Hopefully the example will bring some clarity to the definition of Aggregates and will demonstrate how they can be implemented in Rails.</p><h3>Sketch</h3><p>This is a sketch illustrating all the classes that will form the model.</p><p><img
src="http://cdn.rubysource.com/files/2012/04/order_item.png" alt="Sketch" /></p><p>As you can see, I have:</p><ul><li>Entities: Order, Item, User, Book, Payment</li><li>Value Objects: Address</li><li>Services: ShipmentService, PaymentService</li><li>View: OrderPresenter</li></ul><h3>Defining Aggregate Boundaries</h3><p>Now, after I am done with sketching, I can establish Aggregate boundaries and choose roots.</p><p>There are a few rules of thumb to use:</p><ul><li>Entities forming the parent-child relationship, most likely, should form an Aggregate. In this case, the parent class becomes the root.</li><li>Entities that are semantically close to each other are good candidates for forming an Aggregate as well. For instance, Book and Payment have no obvious connections with each other. Having them inside an Aggregate is awkward. On the other hand, Order and Item are closely related. Thus, we should consider putting them inside an Aggregate.</li><li>If two Entities have to be modified inside a transaction, they should be parts of the same Aggregate.</li></ul><p>Note:<br
/> These rules should just help you get started. After your first sketch you should look at all the invariants that need to be maintained and finalize your Aggregate boundaries based on them.</p><p>As you may have already guessed, Order and Item form an Aggregate. What other Entities should we include? Including Book does not make much sense because I can easily imagine clients using Book without Order (e.g. you may need to display the list of all available books in the store).<br
/> Including User into the same Aggregate with Order is not the best idea either. Just imagine if User becomes the root, you will have to access all the orders of a user through the user itself. Furthermore, updating two orders of the same user simultaneously will be tricky. Clearly neither Book nor User should be a part of the Aggregate.</p><p>The situation with the Payment class is different. Conceptually, Payment is an important part of Order. Also, you cannot simultaneously pay for an Order and modify it. It’s decided, Payment becomes a part of the Aggregate.</p><p>An updated sketch with the defined boundary:</p><p><img
src="http://cdn.rubysource.com/files/2012/04/order_item_boundaries.png" alt="Sketch 2" /></p><h3>Implementation</h3><p>Let&#8217;s get started with the code. First, let&#8217;s define the Book and User classes:</p><div
class="gistem"><div
id="gist-2432865" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Book</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">DataMapper</span><span class="o">::</span><span class="no">Resource</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:id</span><span class="p">,</span> <span class="no">Serial</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:title</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:author</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:price</span><span class="p">,</span> <span class="no">Decimal</span></div><div class='line' id='LC8'><span class="k">end</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'><span class="k">class</span> <span class="nc">User</span></div><div class='line' id='LC11'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">DataMapper</span><span class="o">::</span><span class="no">Resource</span></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:id</span><span class="p">,</span> <span class="no">Serial</span></div><div class='line' id='LC14'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:name</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:address_country</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC17'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:address_city</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>&nbsp;&nbsp;<span class="n">validates_presence_of</span> <span class="ss">:name</span></div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">address</span><span class="o">=</span> <span class="n">address</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">address_country</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">country</span></div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">address_city</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">city</span></div><div class='line' id='LC24'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">address</span></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Address</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">address_country</span><span class="p">,</span> <span class="n">address_city</span><span class="p">)</span></div><div class='line' id='LC28'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC29'><span class="k">end</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'><span class="k">class</span> <span class="nc">Address</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:country</span><span class="p">,</span> <span class="ss">:city</span><span class="p">)</span></div><div class='line' id='LC32'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432865/8c30dd78d39b32dfee9c80d3a40b71f00a245e7b/book_user_address.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432865#file_book_user_address.rb" style="float:right;margin-right:10px;color:#666">book_user_address.rb</a> <a
href="https://gist.github.com/2432865">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>There is nothing really interesting here. There are two Entities (Book and User) and one Value Object (Address). It gets interesting when we implement the Order and Item classes:</p><div
class="gistem"><div
id="gist-2432874" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Order</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">DataMapper</span><span class="o">::</span><span class="no">Resource</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:id</span><span class="p">,</span> <span class="no">Serial</span><span class="p">,</span> <span class="n">key</span><span class="p">:</span> <span class="kp">true</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:status</span><span class="p">,</span> <span class="no">Enum</span><span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:ready</span><span class="p">,</span> <span class="ss">:paid</span><span class="p">,</span> <span class="ss">:shipped</span><span class="p">,</span> <span class="ss">:closed</span><span class="p">,</span> <span class="ss">:canceled</span><span class="o">]</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="n">belongs_to</span> <span class="ss">:buyer</span><span class="p">,</span> <span class="s1">&#39;User&#39;</span></div><div class='line' id='LC8'>&nbsp;&nbsp;<span class="n">has</span> <span class="n">n</span><span class="p">,</span> <span class="ss">:items</span></div><div class='line' id='LC9'>&nbsp;&nbsp;<span class="n">has</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">:payment</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:shipping_address_country</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC12'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:shipping_address_city</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">shipping_address</span><span class="o">=</span> <span class="n">address</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">shipping_address_country</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">country</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">shipping_address_city</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">city</span></div><div class='line' id='LC17'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">shipping_address</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Address</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">shipping_address_country</span><span class="p">,</span> <span class="n">shipping_address_city</span><span class="p">)</span></div><div class='line' id='LC21'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'><br/></div><div class='line' id='LC24'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">make</span> <span class="n">buyer</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">create</span> <span class="n">buyer</span><span class="p">:</span> <span class="n">buyer</span><span class="p">,</span> <span class="n">shipping_address</span><span class="p">:</span> <span class="n">buyer</span><span class="o">.</span><span class="n">address</span><span class="p">,</span> <span class="n">status</span><span class="p">:</span> <span class="ss">:new</span></div><div class='line' id='LC26'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC27'><br/></div><div class='line' id='LC28'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get</span> <span class="n">buyer</span><span class="p">,</span> <span class="nb">id</span></div><div class='line' id='LC29'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1">#...</span></div><div class='line' id='LC30'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC31'><br/></div><div class='line' id='LC32'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">all_orders</span> <span class="n">buyer</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1">#...</span></div><div class='line' id='LC34'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC35'><br/></div><div class='line' id='LC36'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">active_orders</span> <span class="n">buyer</span></div><div class='line' id='LC37'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1">#...</span></div><div class='line' id='LC38'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC39'><br/></div><div class='line' id='LC40'><br/></div><div class='line' id='LC41'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">make_item</span> <span class="n">book</span><span class="p">,</span> <span class="n">quantity</span></div><div class='line' id='LC42'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">ensure_status</span> <span class="ss">:new</span></div><div class='line' id='LC43'><br/></div><div class='line' id='LC44'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">amount</span> <span class="o">=</span> <span class="n">book</span><span class="o">.</span><span class="n">price</span> <span class="o">*</span> <span class="n">quantity</span></div><div class='line' id='LC45'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">items</span><span class="o">.</span><span class="n">create</span> <span class="n">book</span><span class="p">:</span> <span class="n">book</span><span class="p">,</span> <span class="n">quantity</span><span class="p">:</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">amount</span><span class="p">:</span> <span class="n">amount</span></div><div class='line' id='LC46'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC47'><br/></div><div class='line' id='LC48'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">make_payment</span> <span class="n">masked_card</span></div><div class='line' id='LC49'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">ensure_status</span> <span class="ss">:ready</span></div><div class='line' id='LC50'><br/></div><div class='line' id='LC51'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">payment</span> <span class="o">=</span> <span class="no">Payment</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">masked_card</span><span class="p">:</span> <span class="n">masked_card</span><span class="p">,</span> <span class="n">amount</span><span class="p">:</span> <span class="n">total_amount</span><span class="p">)</span></div><div class='line' id='LC52'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">self</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="ss">:paid</span></div><div class='line' id='LC53'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">save</span></div><div class='line' id='LC54'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC55'><br/></div><div class='line' id='LC56'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">mark_as_ready</span></div><div class='line' id='LC57'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1">#...</span></div><div class='line' id='LC58'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC59'><br/></div><div class='line' id='LC60'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">mark_as_shipped</span></div><div class='line' id='LC61'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1">#...</span></div><div class='line' id='LC62'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC63'><br/></div><div class='line' id='LC64'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">total_amount</span></div><div class='line' id='LC65'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1">#...</span></div><div class='line' id='LC66'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC67'><br/></div><div class='line' id='LC68'>&nbsp;&nbsp;<span class="kp">private</span></div><div class='line' id='LC69'><br/></div><div class='line' id='LC70'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">ensure_status</span> <span class="n">required_status</span></div><div class='line' id='LC71'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">raise</span> <span class="no">InvalidOrderStatus</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span> <span class="k">if</span> <span class="n">status</span> <span class="o">!=</span> <span class="n">required_status</span></div><div class='line' id='LC72'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC73'><span class="k">end</span></div><div class='line' id='LC74'><br/></div><div class='line' id='LC75'><span class="k">class</span> <span class="nc">Item</span></div><div class='line' id='LC76'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">DataMapper</span><span class="o">::</span><span class="no">Resource</span></div><div class='line' id='LC77'><br/></div><div class='line' id='LC78'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:id</span><span class="p">,</span> <span class="no">Serial</span></div><div class='line' id='LC79'>&nbsp;&nbsp;<span class="n">belongs_to</span> <span class="ss">:book</span></div><div class='line' id='LC80'><br/></div><div class='line' id='LC81'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:quantity</span><span class="p">,</span> <span class="nb">Integer</span></div><div class='line' id='LC82'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:amount</span><span class="p">,</span> <span class="no">Decimal</span></div><div class='line' id='LC83'><span class="k">end</span></div><div class='line' id='LC84'><br/></div><div class='line' id='LC85'><span class="k">class</span> <span class="nc">Payment</span></div><div class='line' id='LC86'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">DataMapper</span><span class="o">::</span><span class="no">Resource</span></div><div class='line' id='LC87'><br/></div><div class='line' id='LC88'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:id</span><span class="p">,</span> <span class="no">Serial</span></div><div class='line' id='LC89'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:masked_card</span><span class="p">,</span> <span class="nb">String</span></div><div class='line' id='LC90'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:amount</span><span class="p">,</span> <span class="no">Decimal</span></div><div class='line' id='LC91'><br/></div><div class='line' id='LC92'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:created_at</span><span class="p">,</span> <span class="no">DateTime</span></div><div class='line' id='LC93'>&nbsp;&nbsp;<span class="n">property</span> <span class="ss">:updated_at</span><span class="p">,</span> <span class="no">DateTime</span></div><div class='line' id='LC94'><span class="k">end</span></div><div class='line' id='LC95'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432874/4fed53d4c9ea1e1c37ee7d7b8aaa0c63dc6a25b8/order_item_payment.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432874#file_order_item_payment.rb" style="float:right;margin-right:10px;color:#666">order_item_payment.rb</a> <a
href="https://gist.github.com/2432874">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>That is how the creation of an order may look like:</p><div
class="gistem"><div
id="gist-2432877" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">buyer</span> <span class="o">=</span> <span class="n">current_user</span></div><div class='line' id='LC2'><span class="n">order</span> <span class="o">=</span> <span class="no">Order</span><span class="o">.</span><span class="n">make</span> <span class="n">buyer</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'><span class="c1">#At this moment: </span></div><div class='line' id='LC5'><span class="c1">#order.shipping_address == buyers_address</span></div><div class='line' id='LC6'><span class="c1">#order.status == :new</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'><span class="n">order</span><span class="o">.</span><span class="n">make_item</span> <span class="n">book1</span><span class="p">,</span> <span class="mi">2</span></div><div class='line' id='LC9'><span class="n">order</span><span class="o">.</span><span class="n">make_item</span> <span class="n">book2</span><span class="p">,</span> <span class="mi">3</span></div><div class='line' id='LC10'><span class="n">order</span><span class="o">.</span><span class="n">mark_as_ready</span></div><div class='line' id='LC11'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432877/694af44472f4fb557ed5d54cf1a4d0bdca75e5e8/create_order_with_items.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432877#file_create_order_with_items.rb" style="float:right;margin-right:10px;color:#666">create_order_with_items.rb</a> <a
href="https://gist.github.com/2432877">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Reading the same order from the database may look like this:</p><div
class="gistem"><div
id="gist-2432879" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">order</span> <span class="o">=</span> <span class="no">Order</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">buyer</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC2'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432879/ebc4184f393927b3081536c8b9c2c952348e7474/getting_order_from_db.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432879#file_getting_order_from_db.rb" style="float:right;margin-right:10px;color:#666">getting_order_from_db.rb</a> <a
href="https://gist.github.com/2432879">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>There are a few important things I&#8217;d like to point out:</p><ul><li>All items and payments are created through an instance of Order.</li><li>I don&#8217;t update attributes of an Order directly.</li><li>I don&#8217;t use DataMapper methods directly. I wrote a few methods to decouple our model from DataMapper as much as possible.</li><li>There are no bidirectional associations. Every order knows about its items, but the items don&#8217;t have any references to their orders. Why? Because they don&#8217;t need to. Order is the root; therefore, any client can get an item only through its order. This means that an item&#8217;s order will always be known. <em>* Bidirectional associations are a bad practice established in the Rails community. If you can avoid it, please, do it. *</em></li><li>A user does not know about its orders. Apart from the fact that it&#8217;s an unnecessary bidirectional association, it also makes testing much harder.</li></ul><p>Don&#8217;t believe me? Take a look at this line of code:</p><div
class="gistem"><div
id="gist-2432887" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="vi">@all_orders</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">orders</span><span class="o">.</span><span class="n">active</span></div><div class='line' id='LC2'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432887/034ec53115be6d276c1310f66d161eed4fa76420/active_orders1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432887#file_active_orders1.rb" style="float:right;margin-right:10px;color:#666">active_orders1.rb</a> <a
href="https://gist.github.com/2432887">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>It looks so simple. Some people might even say that it&#8217;s nicer than this one:</p><div
class="gistem"><div
id="gist-2432889" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="vi">@all_orders</span> <span class="o">=</span> <span class="no">Order</span><span class="o">.</span><span class="n">active_orders</span><span class="p">(</span><span class="n">user</span><span class="p">)</span></div><div class='line' id='LC2'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432889/5bcc0f08f9c8688b81b7910dd0cc815632b70441/active_orders2.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432889#file_active_orders2.rb" style="float:right;margin-right:10px;color:#666">active_orders2.rb</a> <a
href="https://gist.github.com/2432889">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>However, if you try to stub it in your test you may write something similar to this:</p><div
class="gistem"><div
id="gist-2432894" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">order</span> <span class="o">=</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC2'><span class="n">stub</span><span class="p">(</span><span class="n">orders</span> <span class="o">=</span> <span class="no">Object</span><span class="o">.</span><span class="n">new</span><span class="p">)</span><span class="o">.</span><span class="n">active</span> <span class="p">{</span><span class="o">[</span><span class="n">order</span><span class="o">]</span><span class="p">}</span></div><div class='line' id='LC3'><span class="n">stub</span><span class="p">(</span><span class="n">user</span> <span class="o">=</span> <span class="no">Object</span><span class="o">.</span><span class="n">new</span><span class="p">)</span><span class="o">.</span><span class="n">orders</span> <span class="p">{</span><span class="n">orders</span><span class="p">}</span></div><div class='line' id='LC4'><span class="n">stub</span><span class="p">(</span><span class="no">UserSession</span><span class="p">)</span><span class="o">.</span><span class="n">current_user</span> <span class="p">{</span><span class="n">user</span><span class="p">}</span></div><div class='line' id='LC5'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432894/789199d776f5ff704229c688abb8fc61e72aab3a/stubbing_active_orders_1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432894#file_stubbing_active_orders_1.rb" style="float:right;margin-right:10px;color:#666">stubbing_active_orders_1.rb</a> <a
href="https://gist.github.com/2432894">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Now, compare it with the stubbing of <code>Order.active_orders(user)</code>:</p><div
class="gistem"><div
id="gist-2432897" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">order</span> <span class="o">=</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC2'><span class="n">stub</span><span class="p">(</span><span class="no">UserSession</span><span class="p">)</span><span class="o">.</span><span class="n">current_user</span> <span class="p">{</span><span class="n">user</span><span class="p">}</span></div><div class='line' id='LC3'><span class="n">stub</span><span class="p">(</span><span class="no">Order</span><span class="p">)</span><span class="o">.</span><span class="n">active_orders</span><span class="p">(</span><span class="n">user</span><span class="p">){</span><span class="o">[</span><span class="n">order</span><span class="o">]</span><span class="p">}</span></div><div class='line' id='LC4'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432897/234844dc255d8a8fc05521e766985ecc6d7c70bc/stubbing_active_orders_2.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432897#file_stubbing_active_orders_2.rb" style="float:right;margin-right:10px;color:#666">stubbing_active_orders_2.rb</a> <a
href="https://gist.github.com/2432897">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>The second test is much easier to read and understand. In addition, in large applications such models as User tend to grow. In a few years, you may get the User class having many orders, promotions, friends, wish lists etc.</p><h3>Accessing Our Model in View</h3><p>Please, don&#8217;t be mistaken. The fact that Order is the root of the Aggregate does not mean I cannot access its payment or items. Of course, I can. The only rule is not to store references to those objects. For instance, I&#8217;d probably need a presenter. As presenters store references to the objects they present, creating ItemPresenter or PaymentPresenter is a violation of the Aggregate boundary. Instead, we can create OrderPresenter and pass an instance of Order to it. OrderPresenter can access the order&#8217;s items or payment through the order itself.</p><div
class="gistem"><div
id="gist-2432899" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">OrderPresenter</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:order</span><span class="p">)</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">render_items</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">order</span><span class="o">.</span><span class="n">items</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">item_row</span> <span class="n">item</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>&nbsp;&nbsp;<span class="kp">private</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">item_row</span> <span class="n">item</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;&lt;div&gt;</span><span class="si">#{</span><span class="n">item</span><span class="o">.</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="si">}</span><span class="s2"> - </span><span class="si">#{</span><span class="n">item</span><span class="o">.</span><span class="n">quantity</span><span class="si">}</span><span class="s2"> - </span><span class="si">#{</span><span class="n">item</span><span class="o">.</span><span class="n">amount</span><span class="si">}</span><span class="s2">&lt;/div&gt;&quot;</span></div><div class='line' id='LC12'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC13'><span class="k">end</span></div><div class='line' id='LC14'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432899/d4c987b72d93972404804e054fa45a37f5377942/order_presenter.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432899#file_order_presenter.rb" style="float:right;margin-right:10px;color:#666">order_presenter.rb</a> <a
href="https://gist.github.com/2432899">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h3>Invariants</h3><p>Remember that Aggregates are not only about access restriction. They also define the boundaries of invariants and transactions. There is a common opinion that those invariants must be enforced by the root. It&#8217;s also common to make the root responsible for managing all transactions. Although sometimes it may make perfect sense, there are lots of situations when it does not. Just to give you an example, let&#8217;s take a look at the PaymentService class:</p><div
class="gistem"><div
id="gist-2432901" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">module</span> <span class="nn">PaymentService</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">process</span> <span class="n">order</span><span class="p">,</span> <span class="n">credit_card</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">with_transaction</span> <span class="k">do</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">order</span><span class="o">.</span><span class="n">make_payment</span> <span class="n">mask_card</span><span class="p">(</span><span class="n">credit_card</span><span class="p">)</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">make_remote_call</span> <span class="n">credit_card</span><span class="p">,</span> <span class="n">order</span><span class="o">.</span><span class="n">payment</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'>&nbsp;&nbsp;<span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC10'><span class="k">end</span></div><div class='line' id='LC11'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2432901/d438bafb9ec02a0d58e1e6acc87febaf1e6706a8/payment_service.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2432901#file_payment_service.rb" style="float:right;margin-right:10px;color:#666">payment_service.rb</a> <a
href="https://gist.github.com/2432901">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>PaymentService has several responsibilities. First, it masks the credit card. Next, it updates the database to mark the order as paid. After that, it makes a remote call to some external service that processes credit card transactions. Also, it wraps everything into a transaction. PaymentService ensures an important invariant that updating the status of the order and making the remote call must be done together. An alternative would be to make Order responsible for calling external services. The result would be a violation of Single Responsibility Principle and a few nasty dependencies of Order on external payment systems.</p><h2>Wrapping Up</h2><p>Sometimes invariants need to be applied not to discrete objects, but to clusters of objects. Defining Aggregates and restricting access to Aggregate members is an arrangement that makes enforcing all the invariants possible.</p><h2>Resources</h2><p><a
name="resources"></a><br
/> * <a
href="http://domaindrivendesign.org/node/88">Aggregate | Domain-Driven Design Community</a></p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/ddd-for-rails-developers-part-3-aggregates/feed/</wfw:commentRss> <slash:comments>6</slash:comments> <series:name><![CDATA[DDD for Rails Developers]]></series:name> </item> <item><title>Using Sinatra Helpers to Clean Up Your Code</title><link>http://rubysource.com/using-sinatra-helpers-to-clean-up-your-code/</link> <comments>http://rubysource.com/using-sinatra-helpers-to-clean-up-your-code/#comments</comments> <pubDate>Mon, 07 May 2012 13:30:10 +0000</pubDate> <dc:creator>Darren Jones</dc:creator> <category><![CDATA[Best Practices]]></category> <category><![CDATA[Sinatra]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3710</guid> <description><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/05/shutterstock_19458208-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="Image courtesy of Shutterstock" title="shutterstock_19458208" />A couple of months ago, I wrote an article about how I rapidly built my personal site using Sinatra. While I was building the site I started thinking about the best way to add JavaScript files to the pages. After playing with&#8230;]]></description> <content:encoded><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/05/shutterstock_19458208-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="Image courtesy of Shutterstock" title="shutterstock_19458208" /><div
id="attachment_3711" class="wp-caption alignleft" style="width: 310px"><a
href="http://rubysource.com/?attachment_id=3711" rel="attachment wp-att-3711"><img
class="size-medium wp-image-3711" title="shutterstock_19458208" src="http://cdn.rubysource.com/files/2012/05/shutterstock_19458208-300x300.jpg" alt="" width="300" height="300" /></a><p
class="wp-caption-text">Image courtesy of Shutterstock</p></div><p>A couple of months ago, I wrote <a
href="http://rubysource.com/sinatra-heroku-super-fast-deployment/">an article</a> about how I rapidly built my <a
href="http://daz4126.com/">personal site</a> using <a
href="http://www.sinatrarb.com/">Sinatra</a>. While I was building the site I started thinking about the best way to add JavaScript files to the pages. After playing with it for a while, I ended up using some custom helper methods to add JavaScript files to any page.</p><p>It&#8217;s actually very easy to add javascript files to pages in Sinatra using the layout file. For example, if you want to include JQuery and a custom JavaScript file (called <code>application.js</code>) on all pages, then all you need to do is put the following lines of code in your layout file:</p><div
class="gistem"><div
id="gist-2569440" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;</span><span class="nt">&gt;&lt;/script&gt;</span></div><div class='line' id='LC2'><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">&quot;/javascripts/application.js&quot;</span><span class="nt">&gt;&lt;/script&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569440/7abcabcbb526696dbffa230555e1ac6ba25a8243/gistfile1.html" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569440#file_gistfile1.html" style="float:right;margin-right:10px;color:#666">gistfile1.html</a> <a
href="https://gist.github.com/2569440">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>(This assumes that the Javascript files are in the public folder).</p><p>Putting this code in the layout file means that they will be included on every page. However, some JavaScript files might only be needed on certain pages. For example, you might only want the file <code>email.js</code> to be loaded on the contact page. It turns out that there&#8217;s a simple way to add things dynamically to layout files &#8211; instance variables. These can be set in the routes and then refererred to in the views. I use the instance variable <code>@js</code> to add a custom JavaScript file to a route:</p><div
class="gistem"><div
id="gist-2569442" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">=</span> <span class="s2">&quot;custom.js&quot;</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="n">slim</span> <span class="ss">:index</span></div><div class='line' id='LC4'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569442/d676935f52fb287a594fda43c4bac0c86fc229b2/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569442#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569442">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>I can then add an extra line to my layout file that will include the relevant script tag if the <code>@js</code> variable has been set.</p><div
class="gistem"><div
id="gist-2569446" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;</span><span class="nt">&gt;&lt;/script&gt;</span></div><div class='line' id='LC2'><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">&quot;/application.js&quot;</span><span class="nt">&gt;&lt;/script&gt;</span></div><div class='line' id='LC3'><span class="err">&lt;</span>%= &quot;<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">&quot;/#{@js}&quot;</span><span class="nt">&gt;&lt;/script&gt;</span>&quot; if @js %&gt;</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569446/dab8358a8d6c4674aa8879c1c793f85b5267d79e/gistfile1.html" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569446#file_gistfile1.html" style="float:right;margin-right:10px;color:#666">gistfile1.html</a> <a
href="https://gist.github.com/2569446">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>This is a big improvement. It means that you can add a javascript file on a route-by-route basis. The only problem is that you can only add one file per route. To get round this, you can change the instance variable to an array of JavaScript files:</p><div
class="gistem"><div
id="gist-2569451" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&quot;custom.js&quot;</span><span class="p">,</span><span class="s2">&quot;sorter.js&quot;</span><span class="p">,</span><span class="s2">&quot;colorpicker.js&quot;</span><span class="o">]</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="n">slim</span> <span class="ss">:index</span></div><div class='line' id='LC4'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569451/dd863d5dbc1090173bcb0373f9c28937b56cf6e4/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569451#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569451">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>This also requires the line in the layout file to be changed so that it iterates through each string in the array and adds a script tag for each JavaScript file:</p><div
class="gistem"><div
id="gist-2569461" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;%</span> <span class="k">if</span> <span class="vi">@js</span> <span class="cp">%&gt;</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="cp">&lt;%</span> <span class="vi">@js</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span> <span class="cp">%&gt;</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="s2">/javascripts/</span><span class="si">#{</span><span class="n">script</span><span class="si">}</span><span class="s2">.js</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span> <span class="cp">%&gt;</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div><div class='line' id='LC5'><span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569461/cada785373868950ed3397e5fac0253fa8d960b2/gistfile1.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569461#file_gistfile1.erb" style="float:right;margin-right:10px;color:#666">gistfile1.erb</a> <a
href="https://gist.github.com/2569461">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>So far, so good &#8211; we are now able inject custom JavaScript files into the layout of individual routes. Since we&#8217;re looping through all of the custom JavaScript files in the <code>@js</code> array, why not just add scripts that will appear on all pages (application.js and JQuery) to the array as well? This can be achieved by creating another array, using Sinatra&#8217;s <code>settings</code> method, called <code>settings.javascripts</code>. This array can be placed anywhere in your ruby file (I like to keep it near the top) and contains all the JavaScript files that are to be included on all the pages:</p><div
class="gistem"><div
id="gist-2569466" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="o">=</span> <span class="o">[</span> <span class="s2">&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;</span><span class="p">,</span> <span class="s2">&quot;application.js&quot;</span> <span class="o">]</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569466/2f44176fa59477f96c1c363de12dba4c30590b14/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569466#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569466">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>These two arrays need to be added together to create one big array to iterate over. The uniq method is used to avoid any repetition in case a file gets added to the <code>@js</code> array that has already been placed in the <code>settings.javascripts</code> array:</p><div
class="gistem"><div
id="gist-2569481" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;%</span> <span class="n">javascripts</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@js</span> <span class="p">?</span> <span class="vi">@js</span> <span class="o">+</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="p">:</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span><span class="p">)</span><span class="o">.</span><span class="n">uniq</span> <span class="cp">%&gt;</span></div><div class='line' id='LC2'><span class="cp">&lt;%</span> <span class="n">javascripts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span> <span class="cp">%&gt;</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="s2">/javascripts/</span><span class="si">#{</span><span class="n">script</span><span class="si">}</span><span class="s2">.js</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span> <span class="cp">%&gt;</span></div><div class='line' id='LC4'><span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569481/24952e6c62fdad2909305b0819451d9723849979/gistfile1.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569481#file_gistfile1.erb" style="float:right;margin-right:10px;color:#666">gistfile1.erb</a> <a
href="https://gist.github.com/2569481">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>This amount of logic in a view looks messy, which is always a bad sign. The best way to deal with lots of view logic is to abstract it into a helper method. Helpers are easy to set up in Sinatra, you simply open up a block and place the methods inside. These methods are then available inside routes and views. Here&#8217;s a helper that can be used to clean up the layout:</p><div
class="gistem"><div
id="gist-2569487" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">helpers</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">javascripts</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">javascripts</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@js</span> <span class="p">?</span> <span class="vi">@js</span> <span class="o">+</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="p">:</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span><span class="p">)</span><span class="o">.</span><span class="n">uniq</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">javascripts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">html</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="s2">/</span><span class="si">#{</span><span class="n">script</span><span class="si">}</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC8'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569487/944f58b4100f0ff99f6cfb1dcc834cb0c0590d05/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569487#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569487">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Now all that&#8217;s needed in the layout file is this line:</p><div
class="gistem"><div
id="gist-2569490" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;%=</span> <span class="n">javascripts</span> <span class="cp">%&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569490/4ee5c4faa5540a59ded706a95fbf674c7c1b830e/gistfile1.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569490#file_gistfile1.erb" style="float:right;margin-right:10px;color:#666">gistfile1.erb</a> <a
href="https://gist.github.com/2569490">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Much cleaner.</p><p>Since we&#8217;re now using a helper method, why not allow it to have arguments? This would allow us to add JavaScript files from within the layout file like before. This doesn&#8217;t take too much extra effort &#8211; adding an asterix before the name of the method&#8217;s parameter allows the method to take as many arguments as required. This means that we can add as many JavaScript files as we need for each route. Each argument forms part of the <code>args</code> array, which can now simply be added to the big array of JavaScript files that is iterated over in the layout (a bonus here is that we don&#8217;t have to check if it exists as it defaults to an empty array if no arguments are given):</p><div
class="gistem"><div
id="gist-2569493" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">helpers</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">javascripts</span> <span class="o">*</span><span class="n">scripts</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">javascripts</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@js</span> <span class="p">?</span> <span class="vi">@js</span> <span class="o">+</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="o">+</span> <span class="n">args</span> <span class="p">:</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="o">+</span> <span class="n">args</span><span class="p">)</span><span class="o">.</span><span class="n">uniq</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">javascripts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">html</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="s2">/</span><span class="si">#{</span><span class="n">script</span><span class="si">}</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span>    </div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC8'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569493/cbf4d453dc56fc1c1e5b918cf3f71230466e9aec/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569493#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569493">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Now we have three ways to add JavaScript files to a page:</p><ol><li>In the settings (this is for JavaScript files that are global to the whole site)</li><li>In the layout file using the <code>javascripts</code> helper. This is used for JavaScript files that are common to all pages using this layout.</li><li>In the handler using the <code>@js</code> instance variable. This is used to add JavaScript files on a route by route basis.</li></ol><p>It may seem at first that methods 1 and 2 are virtually the same thing &#8211; a way to add global JavaScript files. But it is not uncommon to have different layouts for a site and it may be likely that each layout will require specific JavaScript files. The <code>settings.javascripts</code> array should only be used for truly global JavaScript that are used throughout the site. The <code>javascripts</code> helper can be used to add JavaScript files on a layout by layout basis.</p><p>We can improve on this further by using a helper method to set the <code>@js</code> instansce method. Rather than setting it directly in the handler, we can use a helper method called <code>js</code> that could be used to add any custom js files that are required for that route. This is also a good opportunity to clean up the <code>javascripts</code> helper method as it&#8217;s looking a bit brittle (it assumes that <code>settings.javascripts</code> exists at the moment).</p><div
class="gistem"><div
id="gist-2569499" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">helpers</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">js</span> <span class="o">*</span><span class="n">scripts</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">||=</span> <span class="o">[]</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">=</span> <span class="n">args</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">javascripts</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="s1">&#39;javascripts&#39;</span><span class="p">)</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">args</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="vi">@js</span> <span class="k">if</span> <span class="vi">@js</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span><span class="o">.</span><span class="n">flatten</span><span class="o">.</span><span class="n">uniq</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span> </div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="si">#{</span><span class="n">path_to</span> <span class="n">script</span><span class="si">}</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span></div><div class='line' id='LC15'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC16'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569499/46fa7a469fbc36202223c697eb4fb6fc97a1afc2/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569499#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569499">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Our new <code>js</code> helper method means that things look a bit neater when called in the handler. This is especially true since the files are no longer required to be placed inside an array:</p><div
class="gistem"><div
id="gist-2569501" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="n">js</span> <span class="s2">&quot;custom.js&quot;</span><span class="p">,</span><span class="s2">&quot;sorter.js&quot;</span><span class="p">,</span><span class="s2">&quot;colorpicker.js&quot;</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="n">slim</span> <span class="ss">:index</span></div><div class='line' id='LC4'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569501/ee820262f4af67bed59e89ce12ce97e93af59903/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569501#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569501">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>To finish off, let&#8217;s make it easier to add JQuery and other popular libraries when they&#8217;re required. This involves writing another helper method that uses shortcut symbols in place of the full url:</p><div
class="gistem"><div
id="gist-2569503" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">helpers</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">js</span> <span class="o">*</span><span class="n">scripts</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">||=</span> <span class="o">[]</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">=</span> <span class="n">args</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">javascripts</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="s1">&#39;javascripts&#39;</span><span class="p">)</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">args</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="vi">@js</span> <span class="k">if</span> <span class="vi">@js</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span><span class="o">.</span><span class="n">flatten</span><span class="o">.</span><span class="n">uniq</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span> </div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="si">#{</span><span class="n">path_to</span> <span class="n">script</span><span class="si">}</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span></div><div class='line' id='LC15'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">path_to</span> <span class="n">script</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">case</span> <span class="n">script</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:jquery</span> <span class="k">then</span> <span class="s1">&#39;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&#39;</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:rightjs</span> <span class="k">then</span> <span class="s1">&#39;http://cdn.rightjs.org/right-2.3.0.js&#39;</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:backbone</span> <span class="k">then</span> <span class="s1">&#39;http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js&#39;</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:underscore</span> <span class="k">then</span> <span class="s1">&#39;http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js&#39;</span></div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span> <span class="n">script</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="s1">&#39;.js&#39;</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC25'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC26'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569503/1b7240a574afd9bc658701091bc431a2aea5f350/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569503#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569503">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Now if you want to add a specific library all you need is the following code inside a route handler:</p><div
class="gistem"><div
id="gist-2569505" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="n">js</span> <span class="ss">:backbone</span><span class="p">,</span> <span class="ss">:custom</span></div><div class='line' id='LC3'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569505/b9e0386cd2c017c7c65847a790c99e6a49dbccd2/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569505#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569505">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Notice that custom JavaScript files can now be entered as symbols and don&#8217;t need to have &#8216;.js&#8217; appended to the end, as this is all done in the <code>path_to</code> helper method.</p><p>With the helper method working how we want, it&#8217;s time to go modular and move it into a separate file. We first need to create a file called <code>javascripts.rb</code> and save it in a folder called &#8216;sinatra&#8217; in the root directory of the app. All of the code inside the helpers block is then placed inside a module called <code>JavaScripts</code>. This module is then placed inside a module called <code>Sinatra</code> (which is considered best practice when extending Sinatra with modules). The last thing to do is make sure to require <code>Sinatra::Base</code> at the top of the file.</p><div
class="gistem"><div
id="gist-2569507" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nb">require</span> <span class="s1">&#39;sinatra/base&#39;</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="k">module</span> <span class="nn">Sinatra</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="k">module</span> <span class="nn">JavaScripts</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">js</span> <span class="o">*</span><span class="n">scripts</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">||=</span> <span class="o">[]</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">=</span> <span class="n">args</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">javascripts</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="s1">&#39;javascripts&#39;</span><span class="p">)</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">args</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="vi">@js</span> <span class="k">if</span> <span class="vi">@js</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span><span class="o">.</span><span class="n">flatten</span><span class="o">.</span><span class="n">uniq</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span> </div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="si">#{</span><span class="n">path_to</span> <span class="n">script</span><span class="si">}</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">path_to</span> <span class="n">script</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">case</span> <span class="n">script</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:jquery</span> <span class="k">then</span> <span class="s1">&#39;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&#39;</span></div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:rightjs</span> <span class="k">then</span> <span class="s1">&#39;http://cdn.rightjs.org/right-2.3.0.js&#39;</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:backbone</span> <span class="k">then</span> <span class="s1">&#39;http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js&#39;</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:underscore</span> <span class="k">then</span> <span class="s1">&#39;http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js&#39;</span></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span> <span class="n">script</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="s1">&#39;.js&#39;</span></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC28'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC29'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'>&nbsp;&nbsp;<span class="n">helpers</span> <span class="no">JavaScripts</span></div><div class='line' id='LC32'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569507/4f138fce44535839e27739257730314433e1685e/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569507#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569507">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>The line <code>helpers JavaScripts</code> right at the end of the Sinatra module registers the helper methods so that they are available for use in the application.</p><p>To use these helper methods, all you need is the following line of code in the main application file (<code>main.rb</code>):</p><div
class="gistem"><div
id="gist-2569510" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nb">require</span> <span class="s1">&#39;./sinatra/helpers&#39;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569510/2945b341a0a8c9f33dc0a384408f2d0be5150754/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569510#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569510">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>We now have a convenient little helper that allows us to easily add JavaScript files exactly where they&#8217;re needed. A nice touch is that JavaScript files can also be added on a conditional basis in route handlers, like so:</p><div
class="gistem"><div
id="gist-2569513" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">get</span> <span class="s1">&#39;/&#39;</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="n">js</span> <span class="ss">:backbone</span><span class="p">,</span> <span class="ss">:application</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="n">js</span> <span class="ss">:admin</span> <span class="k">if</span> <span class="n">logged_in?</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="n">slim</span> <span class="ss">:index</span></div><div class='line' id='LC5'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569513/f1d816c1372f0f93ba7ff5ccd1d863bdf00af303/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569513#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569513">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h2>More Helper Methods</h2><p>The method outlined above is not limited ot JavaScript files. A similar method could be used for adding CSS in a fine-grained way:</p><div
class="gistem"><div
id="gist-2569515" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">module</span> <span class="nn">Sinatra</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">module</span> <span class="nn">JavaScripts</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">js</span> <span class="o">*</span><span class="n">scripts</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">||=</span> <span class="o">[]</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@js</span> <span class="o">=</span> <span class="n">args</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">javascripts</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">settings</span><span class="o">.</span><span class="n">javascripts</span> <span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="s1">&#39;javascripts&#39;</span><span class="p">)</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="n">args</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span> <span class="o">&lt;&lt;</span> <span class="vi">@js</span> <span class="k">if</span> <span class="vi">@js</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">js</span><span class="o">.</span><span class="n">flatten</span><span class="o">.</span><span class="n">uniq</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">script</span><span class="o">|</span> </div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;&lt;script src=</span><span class="se">&quot;</span><span class="si">#{</span><span class="n">path_to</span> <span class="n">script</span><span class="si">}</span><span class="se">&quot;</span><span class="s2">&gt;&lt;/script&gt;&quot;</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">path_to</span> <span class="n">script</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">case</span> <span class="n">script</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:jquery</span> <span class="k">then</span> <span class="s1">&#39;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&#39;</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:rightjs</span> <span class="k">then</span> <span class="s1">&#39;http://cdn.rightjs.org/right-2.3.0.js&#39;</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:backbone</span> <span class="k">then</span> <span class="s1">&#39;http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js&#39;</span></div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">when</span> <span class="ss">:underscore</span> <span class="k">then</span> <span class="s1">&#39;http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js&#39;</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span> <span class="n">script</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="s1">&#39;.js&#39;</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC27'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC28'><br/></div><div class='line' id='LC29'>&nbsp;&nbsp;<span class="k">module</span> <span class="nn">StyleSheets</span></div><div class='line' id='LC30'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">css</span> <span class="o">*</span><span class="n">files</span></div><div class='line' id='LC31'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@css</span> <span class="o">||=</span> <span class="o">[]</span></div><div class='line' id='LC32'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="vi">@css</span> <span class="o">=</span> <span class="n">args</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC34'><br/></div><div class='line' id='LC35'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">styles</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span></div><div class='line' id='LC36'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">css</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC37'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">css</span> <span class="o">&lt;&lt;</span> <span class="n">settings</span><span class="o">.</span><span class="n">css</span> <span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="s1">&#39;css&#39;</span><span class="p">)</span></div><div class='line' id='LC38'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">css</span> <span class="o">&lt;&lt;</span> <span class="n">args</span></div><div class='line' id='LC39'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">css</span> <span class="o">&lt;&lt;</span> <span class="vi">@css</span> <span class="k">if</span> <span class="vi">@css</span></div><div class='line' id='LC40'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">css</span><span class="o">.</span><span class="n">flatten</span><span class="o">.</span><span class="n">uniq</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">stylesheet</span><span class="o">|</span> </div><div class='line' id='LC41'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;&lt;link href=</span><span class="se">&quot;</span><span class="s2">/</span><span class="si">#{</span><span class="n">stylesheet</span><span class="si">}</span><span class="s2">.css</span><span class="se">&quot;</span><span class="s2"> media=</span><span class="se">&quot;</span><span class="s2">screen, projection</span><span class="se">&quot;</span><span class="s2"> rel=</span><span class="se">&quot;</span><span class="s2">stylesheet</span><span class="se">&quot;</span><span class="s2"> /&gt;&quot;</span></div><div class='line' id='LC42'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span><span class="o">.</span><span class="n">join</span>    </div><div class='line' id='LC43'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC44'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC45'><br/></div><div class='line' id='LC46'>&nbsp;&nbsp;<span class="n">helpers</span> <span class="no">JavaScripts</span><span class="p">,</span> <span class="no">StyleSheets</span></div><div class='line' id='LC47'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569515/610f41d746b11b34d3ddd58785a6feee503b47a4/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569515#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2569515">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>I&#8217;ve taken the whole concept further and built a module called <code>HeadCleaner</code> that has a helper method for all the common items found inside the head of an html page such as <code>meta</code>, <code>title</code>, <code>webfont</code>s etc. This means that you can just use method calls to create a very minimal looking head section of the layout file such as the one shown below:</p><div
class="gistem"><div
id="gist-2569519" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nt">&lt;head&gt;</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">meta</span> <span class="cp">%&gt;</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">title_tag</span> <span class="cp">%&gt;</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">favicon</span> <span class="cp">%&gt;</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">ie_shim</span> <span class="cp">%&gt;</span></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">webfonts</span> <span class="cp">%&gt;</span></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">javascripts</span> <span class="cp">%&gt;</span></div><div class='line' id='LC8'>&nbsp;&nbsp;<span class="cp">&lt;%=</span> <span class="n">styles</span> <span class="ss">:mobile</span><span class="p">,</span><span class="ss">:general</span><span class="p">,</span><span class="ss">:ie</span> <span class="cp">%&gt;</span></div><div class='line' id='LC9'><span class="nt">&lt;/head&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2569519/6649f3c013349f957d9e10f2a030365de32dcf89/gistfile1.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2569519#file_gistfile1.erb" style="float:right;margin-right:10px;color:#666">gistfile1.erb</a> <a
href="https://gist.github.com/2569519">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>You can see the code <a
href="https://github.com/daz4126/sinatra-head-cleaner">here</a>.</p><p>It&#8217;s by no means perfect and I&#8217;d appreciate any feedback or help in developing this further (fork the code on github!). Ideas that I have for developing this are:</p><ul><li>Allow users to set the path to javascripts</li><li>Have a separate yaml file with the shortcuts to external libraries in it so that it can be easily edited</li><li>Integrate with a JavaScript loader rather than having a separate script tag for each JavaScript file.</li></ul><p>I hope these helpers are, well, helpful. If you&#8217;ve developed any helpers in your Sinatra development, do tell in the comments. Cheers!</p><p>(Main Image courtesy of <a
href="http://www.shutterstock.com/cat.mhtml?lang=en&amp;search_source=search_form&amp;version=llv1&amp;anyorall=all&amp;safesearch=1&amp;searchterm=javascript&amp;search_group=#id=19458208&amp;src=0f0f26712d5878c7893e5e19e877e2dd-1-11" target="_blank">Shutterstock</a>)</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/using-sinatra-helpers-to-clean-up-your-code/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Rails: User/Password Authentication from Scratch, Part II</title><link>http://rubysource.com/rails-userpassword-authentication-from-scratch-part-ii/</link> <comments>http://rubysource.com/rails-userpassword-authentication-from-scratch-part-ii/#comments</comments> <pubDate>Thu, 03 May 2012 13:30:32 +0000</pubDate> <dc:creator>Karim El Husseiny</dc:creator> <category><![CDATA[Rails Tutorials]]></category> <category><![CDATA[Ruby on Rails]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3631</guid> <description><![CDATA[In Part One of this series, I explained how to create a Rails application with a sign up form. This article concludes the process of user authentication Authentication Method As we can now save encrypted passwords in the database, it&#8217;s time to&#8230;]]></description> <content:encoded><![CDATA[<div><p>In <a
href="http://rubysource.com/?p=3611">Part One</a> of this series, I explained how to create a Rails application with a sign up form. This article concludes the process of user authentication</p><h2>Authentication Method</h2><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3620" rel="attachment wp-att-3620"><img
class="alignnone size-large wp-image-3620" src="http://cdn.rubysource.com/files/2012/04/authenticate_method-530x466.jpg" alt="" width="530" height="466" /></a></div><p>As we can now save encrypted passwords in the database, it&#8217;s time to setup the authentication method that will take a username/email and password to find out if that matches a user in the database. We need a query to match username/email and, if found, encrypt the entered password and compare it with the encrypted password in the database.</p><p>Let&#8217;s write an authentication method in the <em>user</em> model</p><div
class="gistem"><div
id="gist-2426082" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">authenticate</span><span class="p">(</span><span class="n">username_or_email</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="n">login_password</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">)</span></div><div class='line' id='LC2'>	<span class="k">if</span>  <span class="no">EMAIL_REGEX</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">username_or_email</span><span class="p">)</span>    </div><div class='line' id='LC3'>		<span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by_email</span><span class="p">(</span><span class="n">username_or_email</span><span class="p">)</span></div><div class='line' id='LC4'>	<span class="k">else</span></div><div class='line' id='LC5'>		<span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by_username</span><span class="p">(</span><span class="n">username_or_email</span><span class="p">)</span></div><div class='line' id='LC6'>	<span class="k">end</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>	<span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">match_password</span><span class="p">(</span><span class="n">login_password</span><span class="p">)</span></div><div class='line' id='LC9'>		<span class="k">return</span> <span class="n">user</span></div><div class='line' id='LC10'>	<span class="k">else</span></div><div class='line' id='LC11'>		<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC12'>	<span class="k">end</span></div><div class='line' id='LC13'><span class="k">end</span>   </div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'><span class="k">def</span> <span class="nf">match_password</span><span class="p">(</span><span class="n">login_password</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">)</span></div><div class='line' id='LC16'>	<span class="n">encrypted_password</span> <span class="o">==</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">hash_secret</span><span class="p">(</span><span class="n">login_password</span><span class="p">,</span> <span class="n">salt</span><span class="p">)</span></div><div class='line' id='LC17'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426082/516e90f5fcd5df3dd40a23086473cd6b8b41d0d6/user.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426082#file_user.rb" style="float:right;margin-right:10px;color:#666">user.rb</a> <a
href="https://gist.github.com/2426082">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>We see that if <em>login_password</em> matches, we return the user object. Otherwise, the method returns false.</p><hr
/></div><div><h2>Sessions Controller and Login form template</h2><p>With our authentication method in place, let&#8217;s generate the <em>sessions</em> controller. The SessionsController will have create login, home, profile and setting actions.</p><div
class="gistem"><div
id="gist-2426085" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nv">$ </span>rails g controller sessions login, home, profile, setting </div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426085/03b288e0188d79a3a072fa59c7c3c93b8ad87544/generate_session_controller.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426085#file_generate_session_controller.sh" style="float:right;margin-right:10px;color:#666">generate_session_controller.sh</a> <a
href="https://gist.github.com/2426085">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Let&#8217;s focus on the <em>login</em> template. The act of logging in consists of a form that accepts username/email and password, passing the values to the <em>login_attempt</em> action.</p><div
class="gistem"><div
id="gist-2426090" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;%</span> <span class="vi">@page_title</span> <span class="o">=</span> <span class="s2">&quot;UserAuth | Login&quot;</span> <span class="cp">-%&gt;</span></div><div class='line' id='LC2'><span class="nt">&lt;div</span> <span class="na">class=</span><span class="err"> </span><span class="s">&quot;Sign_Form&quot;</span><span class="nt">&gt;</span></div><div class='line' id='LC3'>	<span class="nt">&lt;h1&gt;</span>Log in<span class="nt">&lt;/h1&gt;</span></div><div class='line' id='LC4'>	<span class="cp">&lt;%=</span> <span class="n">form_tag</span><span class="p">(</span><span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;login_attempt&#39;</span><span class="p">)</span> <span class="k">do</span> <span class="cp">%&gt;</span></div><div class='line' id='LC5'>		<span class="nt">&lt;p&gt;</span>Username or Email:<span class="nt">&lt;/br&gt;</span> <span class="cp">&lt;%=</span> <span class="n">text_field_tag</span><span class="p">(</span><span class="ss">:username_or_email</span><span class="p">)</span> <span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span> </div><div class='line' id='LC6'>		<span class="nt">&lt;p&gt;</span>Password:<span class="nt">&lt;/br&gt;</span> <span class="cp">&lt;%=</span> <span class="n">password_field_tag</span> <span class="ss">:login_password</span> <span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span> </div><div class='line' id='LC7'>		<span class="cp">&lt;%=</span> <span class="n">submit_tag</span><span class="p">(</span><span class="s2">&quot;Log In&quot;</span><span class="p">)</span> <span class="cp">%&gt;</span></div><div class='line' id='LC8'>	<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div><div class='line' id='LC9'><span class="nt">&lt;/div&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426090/e81c15d6cc7ba6cc388c4a3c474432bcdf6bf7a4/login.html.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426090#file_login.html.erb" style="float:right;margin-right:10px;color:#666">login.html.erb</a> <a
href="https://gist.github.com/2426090">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3621" rel="attachment wp-att-3621"><img
class="alignnone size-large wp-image-3621" src="http://cdn.rubysource.com/files/2012/04/login-530x444.jpg" alt="" width="530" height="444" /></a></div><hr
/></div><div><h2>Creating `login_attempt` Action</h2><p>The <em>login_attempt</em> action accepts params from the login form and passes it to the authentication method we created previously. If the user is found, we redirect to the <em>home</em> action. Otherwise, we&#8217;ll render the <em>login</em> template again.</p><div
class="gistem"><div
id="gist-2426093" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'>				<span class="k">def</span> <span class="nf">login</span></div><div class='line' id='LC4'>					<span class="c1">#Login Form</span></div><div class='line' id='LC5'>				<span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>				<span class="k">def</span> <span class="nf">login_attempt</span></div><div class='line' id='LC8'>					<span class="n">authorized_user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:username_or_email</span><span class="o">]</span><span class="p">,</span><span class="n">params</span><span class="o">[</span><span class="ss">:login_password</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC9'>					<span class="k">if</span> <span class="n">authorized_user</span></div><div class='line' id='LC10'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;Wow Welcome again, you logged in as </span><span class="si">#{</span><span class="n">authorized_user</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC11'>						<span class="n">redirect_to</span><span class="p">(</span><span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;home&#39;</span><span class="p">)</span></div><div class='line' id='LC12'>					<span class="k">else</span></div><div class='line' id='LC13'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;Invalid Username or Password&quot;</span></div><div class='line' id='LC14'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:color</span><span class="o">]=</span> <span class="s2">&quot;invalid&quot;</span></div><div class='line' id='LC15'>						<span class="n">render</span> <span class="s2">&quot;login&quot;</span>	</div><div class='line' id='LC16'>					<span class="k">end</span></div><div class='line' id='LC17'>				<span class="k">end</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426093/dceb386dc39231be49048fe32cf8e7471fbba9a3/sessions_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426093#file_sessions_controller.rb" style="float:right;margin-right:10px;color:#666">sessions_controller.rb</a> <a
href="https://gist.github.com/2426093">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3622" rel="attachment wp-att-3622"><img
class="alignnone size-large wp-image-3622" src="http://cdn.rubysource.com/files/2012/04/invalid_login-530x357.jpg" alt="" width="530" height="357" /></a></div><p>There is something missing here. We need to save the logged-in state of the user. If we don&#8217;t, we&#8217;ll have to authenticate before each requested action, which is not DRY and can be expensive. We&#8217;ll use the session to keep track of the user state, checking it before every request from the user.</p><p>Let&#8217;s see how that is done.</p><hr
/></div><div><h2>Cookies, Sessions and Super-Cookies</h2><h3>Cookies:</h3><p>We know that when a user sends a request to the web server, it is treated as a new request. The web server is stateless, meaning, it doesn&#8217;t know or care about previous requests. A simple solution to passing state between requests is cookies. The web server sends data in a cookie file to the browser, which saves it and sends the cookie data back with each request.</p><p>Using cookies in Rails is a snap:</p><div
class="gistem"><div
id="gist-2426097" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">cookies</span><span class="o">[</span><span class="ss">:name</span><span class="o">]=</span> <span class="s2">&quot;Azzurrio&quot;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426097/41d59184ea7a046be009aeb935afa427e35406ae/cookie.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426097#file_cookie.rb" style="float:right;margin-right:10px;color:#666">cookie.rb</a> <a
href="https://gist.github.com/2426097">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h3>Sessions:</h3><p>However, due to some limitations of cookies, such as 4kb max size and the fact that they can be read, altered or deleted, we will use sessions. Using sessions, the web server sends an ID in the cookie file to the browser. The ID is sent back on each request and is used to pull data out of the session, which is stored on the server.</p><p>Using sessions in Rails is, like cookies, simple:</p><div
class="gistem"><div
id="gist-2426102" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">session</span><span class="o">[</span><span class="ss">:name</span><span class="o">]=</span> <span class="s2">&quot;Azzurrio&quot;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426102/430faf9002537e2caac5832100f23f9a8497ac83/session.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426102#file_session.rb" style="float:right;margin-right:10px;color:#666">session.rb</a> <a
href="https://gist.github.com/2426102">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h3>Super-Cookie:</h3><p>Writing the session information to persistant storage, such as a file or database, doesn&#8217;t scale well. Instead, we will use cookie storage, which is very fast and secure. This so-called super-cookie is encrypted and placed in the session, ensuring that the user can not read or alter it.</p><p><strong>Session Configuration:</strong>Inside the <em>config/initializers</em> folder, you&#8217;ll find two configuration files for sessions. The first is <em>session_store.rb</em>, which is used to configure the storage option you want to use ( <em>cookie_store</em> is the default option.) The second is <em>secret_token.rb</em>, which contains the string that Rails use to encrypt the cookie file.</p><p>Now let&#8217;s get back to our application and save the login state in the session if the user is authorized. Back in the <em>login_attempt</em> action</p><div
class="gistem"><div
id="gist-2426109" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">def</span> <span class="nf">login_attempt</span></div><div class='line' id='LC2'>					<span class="n">authorized_user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:username_or_email</span><span class="o">]</span><span class="p">,</span><span class="n">params</span><span class="o">[</span><span class="ss">:login_password</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC3'>					<span class="k">if</span> <span class="n">authorized_user</span></div><div class='line' id='LC4'>						<span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">authorized_user</span><span class="o">.</span><span class="n">id</span></div><div class='line' id='LC5'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;Wow Welcome again, you logged in as </span><span class="si">#{</span><span class="n">authorized_user</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC6'>						<span class="n">redirect_to</span><span class="p">(</span><span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;home&#39;</span><span class="p">)</span></div><div class='line' id='LC7'>					<span class="k">else</span></div><div class='line' id='LC8'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;Invalid Username or Password&quot;</span></div><div class='line' id='LC9'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:color</span><span class="o">]=</span> <span class="s2">&quot;invalid&quot;</span></div><div class='line' id='LC10'>						<span class="n">render</span> <span class="s2">&quot;login&quot;</span>	</div><div class='line' id='LC11'>					<span class="k">end</span></div><div class='line' id='LC12'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426109/75ade9970faf173909e73a4750f3b71988572184/sessions_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426109#file_sessions_controller.rb" style="float:right;margin-right:10px;color:#666">sessions_controller.rb</a> <a
href="https://gist.github.com/2426109">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Here, we&#8217;ve created a <em>user_id</em> session key, storing the authorized user id. This will be retrieved on subsequent requests.</p><p><strong>Note:</strong>It is a good practice to always store the ID that refers to the object in the session file, not the object itself. The reasoning here is, simply, an ID value is small and a whole user object is much larger. The ID can be used to retrieve the user, if needed.</p><blockquote
class="pullquote pqRight"><p>&#8220;Always store the ID that refers to the object in the session file, not the object itself&#8221;</p></blockquote><hr
/></div><div><h2>Access Restriction</h2><p>We need to check the session file every time the user requests some protected action. To do this, we are going use the <em>before_filter</em> method.</p><p><em>before_filter</em> is a method to perform a function before the specific action is executed. It smells a lot like callbacks, but filters are for controllers while callbacks are for models.</p><p>The <em>before_filter</em> method takes the name of the method to run before the action. The second parameter is a list of actions that we want to filter. Let&#8217;s add it to our application.</p><p>In the ApplicationController, which is the superclass of all our controllers, add:</p><div
class="gistem"><div
id="gist-2426123" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="kp">protected</span> </div><div class='line' id='LC2'>			<span class="k">def</span> <span class="nf">authenticate_user</span></div><div class='line' id='LC3'>				<span class="k">unless</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span></div><div class='line' id='LC4'>					<span class="n">redirect_to</span><span class="p">(</span><span class="ss">:controller</span> <span class="o">=&gt;</span> <span class="s1">&#39;sessions&#39;</span><span class="p">,</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;login&#39;</span><span class="p">)</span></div><div class='line' id='LC5'>					<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC6'>				<span class="k">else</span></div><div class='line' id='LC7'>					<span class="c1"># set current user object to @current_user object variable</span></div><div class='line' id='LC8'>					<span class="vi">@current_user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> </div><div class='line' id='LC9'>					<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC10'>				<span class="k">end</span></div><div class='line' id='LC11'>			<span class="k">end</span></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'>			<span class="k">def</span> <span class="nf">save_login_state</span></div><div class='line' id='LC14'>				<span class="k">if</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span></div><div class='line' id='LC15'>					<span class="n">redirect_to</span><span class="p">(</span><span class="ss">:controller</span> <span class="o">=&gt;</span> <span class="s1">&#39;sessions&#39;</span><span class="p">,</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;home&#39;</span><span class="p">)</span></div><div class='line' id='LC16'>					<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC17'>				<span class="k">else</span></div><div class='line' id='LC18'>					<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC19'>				<span class="k">end</span></div><div class='line' id='LC20'>			<span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426123/22fbf9fee56e245586245ed62cf4742f298ef5cc/application_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426123#file_application_controller.rb" style="float:right;margin-right:10px;color:#666">application_controller.rb</a> <a
href="https://gist.github.com/2426123">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>The <em>authenticate_user</em> method checks if the <em>user_id</em> session is available. If so, it assigns the user object to the <em>@current_user</em> instance variable and returns true, allowing the action to be executed. If not, return false and redirect the to the login page. The other method, called <em>save_login_state</em>, prevents the user from accessing the signup and login pages whilst logged in.</p><p>Add the <em>before_filter</em> calls to <em>SessionsController</em></p><div
class="gistem"><div
id="gist-2426129" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">before_filter</span> <span class="ss">:authenticate_user</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="ss">:home</span><span class="p">,</span> <span class="ss">:profile</span><span class="p">,</span> <span class="ss">:setting</span><span class="o">]</span></div><div class='line' id='LC2'><span class="n">before_filter</span> <span class="ss">:save_login_state</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="ss">:login</span><span class="p">,</span> <span class="ss">:login_attempt</span><span class="o">]</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426129/7afc14aaf40a92aa153f68f7e4c98ce48240f4ee/sessions_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426129#file_sessions_controller.rb" style="float:right;margin-right:10px;color:#666">sessions_controller.rb</a> <a
href="https://gist.github.com/2426129">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>and to <em>UsersController</em></p><div
class="gistem"><div
id="gist-2426131" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">before_filter</span> <span class="ss">:save_login_state</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="o">]</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426131/87c4ce0b779a78dd8c185848afdad3fe26084e8e/users_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426131#file_users_controller.rb" style="float:right;margin-right:10px;color:#666">users_controller.rb</a> <a
href="https://gist.github.com/2426131">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>The <em>@current_user</em> value can be used in the templates, now, to present information about the logged in user.</p><div
class="gistem"><div
id="gist-2426136" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nt">&lt;h2</span> <span class="na">class=</span><span class="s">&#39;User_Header&#39;</span><span class="nt">&gt;</span> <span class="cp">&lt;%=</span><span class="vi">@current_user</span><span class="o">.</span><span class="n">username</span><span class="cp">%&gt;</span> Profile <span class="nt">&lt;h2&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426136/e57b64a0d5c95c0bb09dd3a405534f97912d44dc/profile.html.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426136#file_profile.html.erb" style="float:right;margin-right:10px;color:#666">profile.html.erb</a> <a
href="https://gist.github.com/2426136">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3624" rel="attachment wp-att-3624"><img
class="alignnone size-large wp-image-3624" src="http://cdn.rubysource.com/files/2012/04/profile-530x134.jpg" alt="" width="530" height="134" /></a></div><hr
/></div><div><h2>Logout</h2><p>Our cycle would be incomplete without some way to log out of the session. Logging out, as I mentioned before, clears out the session variable and redirects to the login page.</p><div
class="gistem"><div
id="gist-2426138" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">def</span> <span class="nf">logout</span></div><div class='line' id='LC2'>	<span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="kp">nil</span></div><div class='line' id='LC3'>	<span class="n">redirect_to</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;login&#39;</span></div><div class='line' id='LC4'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426138/60b836409a4c78ae5ee88acb5280cc1c14c2f016/sessions_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426138#file_sessions_controller.rb" style="float:right;margin-right:10px;color:#666">sessions_controller.rb</a> <a
href="https://gist.github.com/2426138">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><hr
/></div><div><h2>Routes Configuration</h2><p>Finally, before you can test your code, edit your routes file as follows:</p><div
class="gistem"><div
id="gist-2426145" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">root</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;sessions#login&quot;</span></div><div class='line' id='LC2'><span class="n">match</span> <span class="s2">&quot;signup&quot;</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;users#new&quot;</span></div><div class='line' id='LC3'><span class="n">match</span> <span class="s2">&quot;login&quot;</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;sessions#login&quot;</span></div><div class='line' id='LC4'><span class="n">match</span> <span class="s2">&quot;logout&quot;</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;sessions#logout&quot;</span></div><div class='line' id='LC5'><span class="n">match</span> <span class="s2">&quot;home&quot;</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;sessions#home&quot;</span></div><div class='line' id='LC6'><span class="n">match</span> <span class="s2">&quot;profile&quot;</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;sessions#profile&quot;</span></div><div class='line' id='LC7'><span class="n">match</span> <span class="s2">&quot;setting&quot;</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="s2">&quot;sessions#setting&quot;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426145/eafa32c1df63d6a81f2418110ffa28ec67321e08/routes.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426145#file_routes.rb" style="float:right;margin-right:10px;color:#666">routes.rb</a> <a
href="https://gist.github.com/2426145">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Now, run your server, login, navigate to other actions, and logout. Pretty sweet, eh?</p><hr
/></div><div><h2>Final Thoughts</h2><p>You&#8217;ve just learned how to implement password-based authentication in your app from scratch. Of course, there are existing libraries that handle authentication for Rails, peforming the tasks shown in this article for you.</p><p>Let&#8217;s take a quick look on the most common libraries:</p><ul><li><a
href="https://github.com/plataformatec/devise"><strong>Devise</strong></a>: Devise is the most common library in Rails used for authentication with a <a
href="https://www.ruby-toolbox.com/projects/devise/popularity">24.874</a> popularity rating according to the <a
href="https://www.ruby-toolbox.com/">ruby-toolbox</a> website. It is a flexible authentication solution based on Warden. It is Rack based and a complete MVC solution.</li><li><a
href="https://github.com/binarylogic/authlogic"><strong>Authlogic</strong></a>: Authlogic introduces a new type of model, allowing multiple authenticated models (as opposed to just a User model) It is a clean and simple ruby authentication solution, occupying second place in popularity rating with <a
href="https://www.ruby-toolbox.com/projects/authlogic/popularity">12.815</a>.</li><li><a
href="https://github.com/intridea/omniauth"><strong>OmniAuth</strong></a>: OmniAuth is a provider-based authentication framework for web applications. It is powerful and flexible, allowing the developer to do as little as possible by leveraging other authentication mechanisms (such as Facebook or Twitter) It&#8217;s popularity rating is <a
href="https://www.ruby-toolbox.com/projects/omniauth/popularity">11.128</a></li></ul><p>There are more libraries for handling password-based authentication, which you can review <a
href="https://www.ruby-toolbox.com/categories/rails_authentication#restful-authentication">here</a>.</p><hr
/></div><div><h2>Conclusion:</h2><p>In this tutorial, we covered the entire process of implementing simple, user and password-based authentication for your Rails application. We learned how to create a new user, encrypt the password before saving, and how to authenticate the user by utilizing the session. Finally, we created the logout action, which clears the session file and redirects the user to login page. Now you&#8217;ve an overall idea about how to create user authentication in Rails from scratch.</p><p>Thanks for reading, I hope it was useful and enjoyable.</p><hr
/></div> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/rails-userpassword-authentication-from-scratch-part-ii/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Rails: User/Password Authentication from Scratch, Part I</title><link>http://rubysource.com/rails-userpassword-authentication-from-scratch-part-i/</link> <comments>http://rubysource.com/rails-userpassword-authentication-from-scratch-part-i/#comments</comments> <pubDate>Tue, 01 May 2012 00:45:45 +0000</pubDate> <dc:creator>Karim El Husseiny</dc:creator> <category><![CDATA[Rails Tutorials]]></category> <category><![CDATA[Ruby on Rails]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3611</guid> <description><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/Tutorial_preview-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="Tutorial_preview" title="Tutorial_preview" />(Note: Source code accompanying this article can be found here.) Today we&#8217;re going to learn how to implement simple user authentication in a Rails application from scratch. We&#8217;ll examine best practices to help avoid common, often costly, mistakes. Introduction to User Authentication&#8230;]]></description> <content:encoded><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/Tutorial_preview-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="Tutorial_preview" title="Tutorial_preview" /><div><h2><a
href="http://rubysource.com/?attachment_id=3615" rel="attachment wp-att-3615"><img
class="alignleft size-full wp-image-3615" src="http://cdn.rubysource.com/files/2012/04/Tutorial_preview.jpg" alt="" width="200" height="200" /></a></h2><p><em>(Note: Source code accompanying this article can be found <a
href="https://github.com/RubySource/rails_simple_user_auth">here</a>.)</em></p><p>Today we&#8217;re going to learn how to implement simple user authentication in a Rails application from scratch. We&#8217;ll examine best practices to help avoid common, often costly, mistakes.</p><hr
/></div><div><h2>Introduction to User Authentication</h2><p>Password-protected actions are a common feature in most web applications, only allowing registered users in with a valid password. This is called &#8220;User Authentication&#8221;, and many Rails applications need it. Let&#8217;s start with a quick scenario of how the user authentication process works.</p><ul><li><strong>Signup:</strong> create a new user. This user is going to register with a username, password (which will be encrypted in the database), email, etc.</li><li><strong>Login:</strong> allow a user to sign in with her/his valid username and password. The authentication process happens by matching the username and password in the database, allowing the user access to the protected actions only if the given information matches the recorded values successfully. If not, the user will be redirected to the login page again.</li><li><strong>Access Restriction:</strong> create a session to hold the authenticated user ID after login, so navigation through additional protected actions can be done easily by just checking the <em>userID</em> in the current session.</li><li><strong>Logout:</strong> allow the user to sign out and set the authenticated <em>userID</em> in session file to nil.</li></ul><hr
/></div><div><h2>Generate User Model &amp; Controller</h2><p>First, let&#8217;s create our application named <em>User_Auth</em>. We&#8217;re using mysql as our database:</p><div
class="gistem"><div
id="gist-2426029" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nv">$ </span>rails new User_Auth -d mysql</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426029/c731822420650b8f18b44dd533328bfb8d144dc3/create_application.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426029#file_create_application.sh" style="float:right;margin-right:10px;color:#666">create_application.sh</a> <a
href="https://gist.github.com/2426029">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Navigate to the application directory in your terminal and generate the <em>User</em> Model and Controller. The controller will get a <em>new</em> method as a parameter.</p><div
class="gistem"><div
id="gist-2426040" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nv">$ </span>rails <span class="nb">cd</span> ./User_Auth</div><div class='line' id='LC2'><span class="nv">$ </span>rails g model user</div><div class='line' id='LC3'><span class="nv">$ </span>rails g controller users new</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426040/cb751662049feef5ab8d0566204f7a4b1dff7d80/generate_User_Model_Controller.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426040#file_generate_user_model_controller.sh" style="float:right;margin-right:10px;color:#666">generate_User_Model_Controller.sh</a> <a
href="https://gist.github.com/2426040">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><hr
/></div><div><h2>Setup the Database</h2><p>Now we need to create a <em>Users</em> table in the database. As you probably know, that is just a simple migration away.</p><div
class="gistem"><div
id="gist-2426031" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">CreateUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span></div><div class='line' id='LC2'>	 <span class="k">def</span> <span class="nf">change</span></div><div class='line' id='LC3'>		    <span class="n">create_table</span> <span class="ss">:users</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span></div><div class='line' id='LC4'>		    	<span class="n">t</span><span class="o">.</span><span class="n">string</span> <span class="ss">:username</span> </div><div class='line' id='LC5'>		    	<span class="n">t</span><span class="o">.</span><span class="n">string</span> <span class="ss">:email</span></div><div class='line' id='LC6'>		    	<span class="n">t</span><span class="o">.</span><span class="n">string</span> <span class="ss">:encrypted_password</span> </div><div class='line' id='LC7'>		    	<span class="n">t</span><span class="o">.</span><span class="n">string</span> <span class="ss">:salt</span></div><div class='line' id='LC8'>		      	<span class="n">t</span><span class="o">.</span><span class="n">timestamps</span></div><div class='line' id='LC9'>		    <span class="k">end</span></div><div class='line' id='LC10'>	<span class="k">end</span></div><div class='line' id='LC11'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426031/ba4a239bf41ac547aef2757dba1136f6005880e6/create_users.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426031#file_create_users.rb" style="float:right;margin-right:10px;color:#666">create_users.rb</a> <a
href="https://gist.github.com/2426031">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>We&#8217;ve added 4 columns in the <em>Users</em> table (username, email, encrypted_password &amp; salt) Remember that we never store passwords in plain text, always encrypting the value first before saving it to the database. There are different types of encryption techniques, which we&#8217;re are going to look at later in this tutorial.</p><blockquote
class="pullquote pqRight"><p>&#8220;We never store passwords in plain text, it should always be encrypted first before saving it to the database.&#8221;</p></blockquote><p>After we have created the model and migration successfully, create the database and migrate it to create the <em>Users</em> table.</p><div
class="gistem"><div
id="gist-2426034" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nv">$ </span>rake db:create</div><div class='line' id='LC2'><span class="nv">$ </span>rake db:migrate</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426034/457731dc472abd37c3d7777a9931268fae6ffe22/database_setup.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426034#file_database_setup.sh" style="float:right;margin-right:10px;color:#666">database_setup.sh</a> <a
href="https://gist.github.com/2426034">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><hr
/></div><div><h2>Creating the New User Action</h2><p>Next, let&#8217;s write the <em>new</em> and <em>create</em> actions in the UsersController.</p><div
class="gistem"><div
id="gist-2426049" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>  </div><div class='line' id='LC2'>				<span class="k">def</span> <span class="nf">new</span></div><div class='line' id='LC3'>					<span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span> </div><div class='line' id='LC4'>				<span class="k">end</span></div><div class='line' id='LC5'>				<span class="k">def</span> <span class="nf">create</span></div><div class='line' id='LC6'>					<span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:user</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC7'>						<span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span></div><div class='line' id='LC8'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;You Signed up successfully&quot;</span></div><div class='line' id='LC9'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:color</span><span class="o">]=</span> <span class="s2">&quot;valid&quot;</span></div><div class='line' id='LC10'>					<span class="k">else</span></div><div class='line' id='LC11'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;Form is invalid&quot;</span></div><div class='line' id='LC12'>						<span class="n">flash</span><span class="o">[</span><span class="ss">:color</span><span class="o">]=</span> <span class="s2">&quot;invalid&quot;</span></div><div class='line' id='LC13'>					<span class="k">end</span></div><div class='line' id='LC14'>					<span class="n">render</span> <span class="s2">&quot;new&quot;</span></div><div class='line' id='LC15'>				<span class="k">end</span></div><div class='line' id='LC16'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426049/b3b27404938c79c868cdfa611cfef6c5988012d8/users_controller.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426049#file_users_controller.rb" style="float:right;margin-right:10px;color:#666">users_controller.rb</a> <a
href="https://gist.github.com/2426049">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>We&#8217;ve created two main actions:</p><ul><li><strong><em>new</em>: </strong>simply create a new user object to be rendered in the <em>new</em> template (we&#8217;ll see it soon ). The template includes a sign up form. The data in this form will be sent to the <em>create </em>action, described next&#8230;</li><li><strong><em>create</em>:</strong> creates the user based on the parameters passed from the <em>new</em> template and saves it to the database. If the user is created successfully, redirect the user to wherever you choose after the signup action. Here we place &#8220;You signed up successfully&#8221; in the flash hash to indicate success, otherwise render the new template again.</li></ul><hr
/></div><div><h2>Sign-up Form template</h2><p>Before writing the signup form, I created a simple layout in the <em>application.html.erb</em> file inside the views/layout directory.</p><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3616" rel="attachment wp-att-3616"><img
class="alignnone size-large wp-image-3616" src="http://cdn.rubysource.com/files/2012/04/layout_and_stylesheet-530x466.jpg" alt="" width="530" height="466" /></a></div><p>Now, let&#8217;s write signup form inside the <em>new</em> template.</p><div
class="gistem"><div
id="gist-2426042" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;%</span> <span class="vi">@page_title</span> <span class="o">=</span> <span class="s2">&quot;UserAuth | Signup&quot;</span> <span class="cp">%&gt;</span></div><div class='line' id='LC2'>				<span class="nt">&lt;div</span> <span class="na">class=</span><span class="err"> </span><span class="s">&quot;Sign_Form&quot;</span><span class="nt">&gt;</span></div><div class='line' id='LC3'>					<span class="nt">&lt;h1&gt;</span>Sign Up<span class="nt">&lt;/h1&gt;</span></div><div class='line' id='LC4'>					<span class="cp">&lt;%=</span> <span class="n">form_for</span><span class="p">(</span><span class="ss">:user</span><span class="p">,</span> <span class="ss">:url</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:controller</span> <span class="o">=&gt;</span> <span class="s1">&#39;users&#39;</span><span class="p">,</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="n">create</span><span class="p">})</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="cp">%&gt;</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'>						<span class="nt">&lt;p&gt;</span> Username:<span class="nt">&lt;/br&gt;</span> <span class="cp">&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">text_field</span> <span class="ss">:username</span><span class="cp">%&gt;</span> <span class="nt">&lt;/p&gt;</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>						<span class="nt">&lt;p&gt;</span> Email:<span class="nt">&lt;/br&gt;</span> <span class="cp">&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">text_field</span> <span class="ss">:email</span><span class="cp">%&gt;</span> <span class="nt">&lt;/p&gt;</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>						<span class="nt">&lt;p&gt;</span> Password:<span class="nt">&lt;/br&gt;</span> <span class="cp">&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">password_field</span> <span class="ss">:password</span><span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>						<span class="nt">&lt;p&gt;</span> Password Confirmation:<span class="nt">&lt;/br&gt;</span> <span class="cp">&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">password_field</span> <span class="ss">:password_confirmation</span><span class="cp">%&gt;</span> <span class="nt">&lt;/p&gt;</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'>						<span class="cp">&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">submit</span> <span class="ss">:Signup</span> <span class="cp">%&gt;</span></div><div class='line' id='LC15'>					<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'>					<span class="cp">&lt;%</span> <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">any?</span> <span class="cp">%&gt;</span></div><div class='line' id='LC18'>						<span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">&quot;Signup_Errors&quot;</span><span class="nt">&gt;</span></div><div class='line' id='LC19'>							<span class="cp">&lt;%</span> <span class="k">for</span> <span class="n">message_error</span> <span class="k">in</span> <span class="vi">@user</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">full_messages</span> <span class="cp">%&gt;</span></div><div class='line' id='LC20'>							<span class="nt">&lt;li&gt;</span>* <span class="cp">&lt;%=</span> <span class="n">message_error</span> <span class="cp">%&gt;</span><span class="nt">&lt;/li&gt;</span></div><div class='line' id='LC21'>							<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div><div class='line' id='LC22'>						<span class="nt">&lt;/ul&gt;</span></div><div class='line' id='LC23'>					<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span></div><div class='line' id='LC24'><span class="nt">&lt;/div&gt;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426042/598b43b96a43f6ac9f90d7cec6f17e775789c4fa/new.html.erb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426042#file_new.html.erb" style="float:right;margin-right:10px;color:#666">new.html.erb</a> <a
href="https://gist.github.com/2426042">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Here, we&#8217;ve created a form for signup which takes a username, email, password and confirmation password from the user. These values will be sent it as <em>params[:user]</em> to the <em>create</em> action. The  <em>if statement</em> checks for errors in case the user enters invalid data.</p><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3617" rel="attachment wp-att-3617"><img
class="alignnone size-large wp-image-3617" src="http://cdn.rubysource.com/files/2012/04/signup-530x444.jpg" alt="" width="530" height="444" /></a></div><p>You also might have noticed that the signup form has two fields: <em>password</em> and <em>password_confirmation</em> that must match a user in database. We should add <em>attr_accessor</em> methods in the <em>user</em> model to handle these.</p><hr
/></div><div><h2>Adding Some Validations to the User Model</h2><p>In addition to <em>attr_accessors</em>, we need to add some validation rules to make sure that the input data fits our requirements.</p><div
class="gistem"><div
id="gist-2426047" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span></div><div class='line' id='LC2'>				<span class="kp">attr_accessor</span> <span class="ss">:password</span></div><div class='line' id='LC3'>				<span class="no">EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i</span></div><div class='line' id='LC4'>				<span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:presence</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:uniqueness</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:length</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:in</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="o">.</span><span class="n">.</span><span class="mi">20</span> <span class="p">}</span></div><div class='line' id='LC5'>				<span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:presence</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:uniqueness</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:format</span> <span class="o">=&gt;</span> <span class="no">EMAIL_REGEX</span></div><div class='line' id='LC6'>				<span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">:confirmation</span> <span class="o">=&gt;</span> <span class="kp">true</span> <span class="c1">#password_confirmation attr</span></div><div class='line' id='LC7'>				<span class="n">validates_length_of</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">:in</span> <span class="o">=&gt;</span> <span class="mi">6</span><span class="o">.</span><span class="n">.</span><span class="mi">20</span><span class="p">,</span> <span class="ss">:on</span> <span class="o">=&gt;</span> <span class="ss">:create</span></div><div class='line' id='LC8'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426047/6f8e7a5e9cc3ac6cc8d918d2961068fb5cee0817/user.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426047#file_user.rb" style="float:right;margin-right:10px;color:#666">user.rb</a> <a
href="https://gist.github.com/2426047">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3618" rel="attachment wp-att-3618"><img
class="alignnone size-large wp-image-3618" src="http://cdn.rubysource.com/files/2012/04/invalid_signup-530x444.jpg" alt="" width="530" height="444" /></a></div><p>We&#8217;ve created a signup page that creates a new user and validates the input data, but we didn&#8217;t encrypte the password. Before we do that, let&#8217;s talk about password encryption.</p><hr
/></div><div><h2>Password Encryption Techniques</h2><p>As previously mentioned, never store passwords in the database as plain text. Encrypting passwords is a best practice and fundamental to all user authentication approaches in Rails.</p><h3>Hashing Password</h3><p>Hashing is the process of applying mathematical functions and algorithms to a string of data to produce a unique output string. While creating a new user, the plain text password gets hashed then saved into the database. When the user signs in, the input password gets hashed and compared with the hashed password stored in the database. This technique is called one-way encryption, meaning, the same inputs with the same hashing function will always give the same output.</p><p>We can implement a hashed method using <em>SHA1</em> in Rails with just two lines of code</p><div
class="gistem"><div
id="gist-2426043" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nb">require</span> <span class="s1">&#39;digest/sha1&#39;</span></div><div class='line' id='LC2'><span class="n">encrypted_password</span><span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA1</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="n">password</span><span class="p">)</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426043/bfd573a9323eeb7a1528363e94220f2b75ccf3fb/password_hashing.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426043#file_password_hashing.rb" style="float:right;margin-right:10px;color:#666">password_hashing.rb</a> <a
href="https://gist.github.com/2426043">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h3>Salting Password</h3><p>Due to some drawbacks that exist in the hashing password technique, like <a
href="http://en.wikipedia.org/wiki/Rainbow_table">Rainbow tables</a>, salting a password is a more secure way to encrypt passwords. &#8216;Salt&#8217; is an additional string of data added to the password before encrypting it. It should be unique and random, to render the Rainbow tables flaw useless.</p><p>Encryption using salt:</p><div
class="gistem"><div
id="gist-2426044" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">salt</span><span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA1</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="s2">&quot;# We add {email} as unique value and </span><span class="si">#{</span><span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="si">}</span><span class="s2"> as random value&quot;</span><span class="p">)</span></div><div class='line' id='LC2'><span class="n">encrypted_password</span><span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA1</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="s2">&quot;Adding </span><span class="si">#{</span><span class="n">salt</span><span class="si">}</span><span class="s2"> to {password}&quot;</span><span class="p">)</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426044/1d90cb27e14321bfcadfbe5d80b6a48d8fe7e882/password_hashing_with_salt.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426044#file_password_hashing_with_salt.rb" style="float:right;margin-right:10px;color:#666">password_hashing_with_salt.rb</a> <a
href="https://gist.github.com/2426044">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h3>Bcrypt</h3><p>There&#8217;s another easy way to encrypt passwords rather than making a salt from scratch: use bcrypt. <em>bcrypt-ruby</em> is a ruby gem for encryption and we&#8217;ll use it in our application.</p><p>To install it, just add the following into gem file:</p><div
class="gistem"><div
id="gist-2426036" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>gem &#39;bcrypt-ruby&#39;, :require =&gt; &#39;bcrypt&#39; </div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426036/6bb0ae229f20511a3ff952837eec19b81c6ad6fc/Gemfile%20" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426036#file_gemfile " style="float:right;margin-right:10px;color:#666">Gemfile </a> <a
href="https://gist.github.com/2426036">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>and inside your application directory run:</p><div
class="gistem"><div
id="gist-2426053" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nv">$ </span>bundle install </div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426053/b7ac34be5892f581efa3d51144a88aa65b05c961/bundle.sh" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426053#file_bundle.sh" style="float:right;margin-right:10px;color:#666">bundle.sh</a> <a
href="https://gist.github.com/2426053">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>then you can simply write:</p><div
class="gistem"><div
id="gist-2426058" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">salt</span> <span class="o">=</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">generate_salt</span></div><div class='line' id='LC2'>&nbsp;<span class="n">encrypted_password</span> <span class="o">=</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">hash_secret</span><span class="p">(</span><span class="n">password</span><span class="p">,</span> <span class="n">salt</span><span class="p">)</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426058/72b7881a4a2d6790b85c4e754d58cf4a8d0b3b63/password_encryption_with_BCrypt.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426058#file_password_encryption_with_b_crypt.rb" style="float:right;margin-right:10px;color:#666">password_encryption_with_BCrypt.rb</a> <a
href="https://gist.github.com/2426058">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><hr
/></div><div><h2>Callbacks</h2><p>We need two functions: one to encrypt the actual password (plain text) before saving the user record and the other function to assign the password <em>attr_accessor</em> to nil. As we have already encrypted and stored password in the database, we will not use it anymore, and we can do this using the <em>before_save</em> and <em>after_save</em> callbacks.</p><p>Now, let&#8217;s add these functions and callbacks to the <em>user</em> model</p><div
class="gistem"><div
id="gist-2426075" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">before_save</span> <span class="ss">:encrypt_password</span></div><div class='line' id='LC2'><span class="n">after_save</span> <span class="ss">:clear_password</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'>	<span class="k">def</span> <span class="nf">encrypt_password</span></div><div class='line' id='LC5'>		 <span class="k">if</span> <span class="n">password</span><span class="o">.</span><span class="n">present?</span></div><div class='line' id='LC6'>			<span class="nb">self</span><span class="o">.</span><span class="n">salt</span> <span class="o">=</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">generate_salt</span></div><div class='line' id='LC7'>			<span class="nb">self</span><span class="o">.</span><span class="n">encrypted_password</span><span class="o">=</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">hash_secret</span><span class="p">(</span><span class="n">password</span><span class="p">,</span> <span class="n">salt</span><span class="p">)</span></div><div class='line' id='LC8'>		<span class="k">end</span></div><div class='line' id='LC9'>	<span class="k">end</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>	<span class="k">def</span> <span class="nf">clear_password</span></div><div class='line' id='LC12'>		<span class="nb">self</span><span class="o">.</span><span class="n">password</span> <span class="o">=</span> <span class="kp">nil</span></div><div class='line' id='LC13'>	<span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426075/e279b444bcefcaa8ca0bc78de9e2f07fdd732f97/user.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426075#file_user.rb" style="float:right;margin-right:10px;color:#666">user.rb</a> <a
href="https://gist.github.com/2426075">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><hr
/></div><div><h2>Mass Assignment Protection</h2><p>One of the most common security issues in Rails is called the &#8220;mass assignment vulnerability&#8221;, and it stems from the ActiveRecord convention of creating getters and setters for all the attribute values of an ActiveRecord object.</p><p>In the <em>create</em> action, rather than directly assign each attribute one by one, we use a hash of all the values that we want to assign to the attributes of subject. This is the aforementioned mass assignment and is the crux of the issue. There are attributes we do not want the user to be able to change via the form, but we aren&#8217;t checking the parameters.</p><p>To avoid it, there&#8217;s two methods in Rails to protect attributes from mass assignment</p><ul><li><strong>attr_protected:</strong> all attributes marked with <em>attr_protected</em> are ignored during mass assignment and all other attributes will be accessible.</li><li><strong>attr_accessible:</strong> all attributes marekd with <em>attr_accessible</em> are accessible during mass assignment and all other attributes will be protected.</li></ul><p>Finally let&#8217;s add the accessible attributes into <em>user</em> model</p><div
class="gistem"><div
id="gist-2426078" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">attr_accessible</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">:password_confirmation</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426078/b864ee844527bdf48a3df94443fbc154ce707db2/user.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426078#file_user.rb" style="float:right;margin-right:10px;color:#666">user.rb</a> <a
href="https://gist.github.com/2426078">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>This completes signup process! You can run your server, sign up as a new user and test it to ensure that password is encrypted in the database successfully!</p><div
class="tutorial_image"><a
href="http://rubysource.com/?attachment_id=3619" rel="attachment wp-att-3619"><img
class="alignnone size-large wp-image-3619" src="http://cdn.rubysource.com/files/2012/04/valid_signup-530x357.jpg" alt="" width="530" height="357" /></a></div><p><strong>Note: </strong>Don&#8217;t forget to add a <em>default route</em> in the routes file! You can just un-comment at in the end of file.</p><div
class="gistem"><div
id="gist-2426080" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">match</span> <span class="s1">&#39;:controller(/:action(/:id))(.:format)&#39;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2426080/f995797a2062d9dbcfa501d883679c69a747f8cb/routes.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2426080#file_routes.rb" style="float:right;margin-right:10px;color:#666">routes.rb</a> <a
href="https://gist.github.com/2426080">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><hr
/><h2>Still More to Do</h2><p>There is still more work before we can say the user authentication process is complete. In my next post, I&#8217;ll cover working with sessions and cookies, access restriction, and route configuration. Thanks for reading!</p></div> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/rails-userpassword-authentication-from-scratch-part-i/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>Authorizing your Rails app with Authority</title><link>http://rubysource.com/authorizing-your-rails-app-with-authority/</link> <comments>http://rubysource.com/authorizing-your-rails-app-with-authority/#comments</comments> <pubDate>Fri, 27 Apr 2012 13:30:02 +0000</pubDate> <dc:creator>Nathan Long</dc:creator> <category><![CDATA[Gems]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3638</guid> <description><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/shutterstock_7712551-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="shutterstock_771255" title="shutterstock_771255" />Imagine you&#8217;re writing a Rails app to organize conferences. As soon as you know what the app can do, you have to start deciding who can do what. Who is allowed to: Decide who will speak at the conference? Edit the presenter&#8230;]]></description> <content:encoded><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/shutterstock_7712551-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="shutterstock_771255" title="shutterstock_771255" /><p>Imagine you&#8217;re writing a Rails app to organize conferences. As soon as you know what the app can do, you have to start deciding who can do what. Who is allowed to:</p><ul><li>Decide who will speak at the conference?</li><li>Edit the presenter schedule?</li><li>Upload presentation slides?</li><li>Comment on those slides?</li><li>Create playlists of music?</li><li>Make a personal schedule of which talks to see?</li></ul><p>All these questions are about authorization: &#8220;what is this user authorized to do?&#8221; Obviously, it&#8217;s important to get this logic right: it needs to be correct, and it needs to be consistent. And it would be nice if that logic was grouped together, rather than scattered all over your app.</p><p>I&#8217;ve just released a gem to solve this problem cleanly. It&#8217;s called Authority (see <a
href="http://github.com/nathanl/authority">Github</a> and <a
href="http://rubygems.org/gems/authority">Rubygems</a>).</p><h2>Protecting Your Models</h2><p>Before I show you how Authority works, let&#8217;s talk about some of the general ideas.</p><p>Authority&#8217;s notion of permissions in your Rails app is focused on your models; in the example above, these would be presenters, comments, playlists, etc. In some cases, the question is very simple: a conference attendee cannot make any changes to any presenter, period. You can think of that as a &#8220;class-level&#8221; rule: the <code>Presenter</code> model is read-only for anyone who isn&#8217;t a conference organizer. If an attendee tries to visit the page for editing a presenter&#8217;s bio, we don&#8217;t have to ask any questions about this particular presenter to know that the action is not allowed.</p><p>In other cases, the question is more nuanced: attendees can edit their own personal schedule, but not anyone else&#8217;s. You can think of that as an &#8220;instance-level&#8221; rule: to know whether a conference attendee can edit a schedule, you have to look at that schedule instance and see who it belongs to.</p><p>Using Authority, you&#8217;d use class methods, like <code>def self.updatable_by?(user)</code> to set class-level rules, and instance methods, like <code>def deletable_by?(user)</code> , to set instance-level rules.</p><p>But where should those methods be written?</p><h2>Keeping Your Permissions DRY</h2><p>Obviously, different models have different rules, so you might think that authorization methods should go on the models themselves.</p><p>But it&#8217;s likely that some of your models share rules: anyone who can edit a <code>Presenter</code> can also edit the <code>PresenterSchedule</code>. If that&#8217;s true, it would be nice to keep that DRY: let the <code>Presenter</code> model and the <code>PresenterSchedule</code> model use the same authorization logic.</p><p>Authority accomplishes this by having the model delegate any questions of authorization to a specified Authorizer class. Models with the same rules can point to the same Authorizer.</p><h2>An Example</h2><p>To take a simple example from the gem&#8217;s README, suppose you two have categories of resources in your app: some for regular users and some for administrators. Using Authority, you&#8217;d have two authorizer classes. You might call them <code>BasicAuthorizer</code> and <code>AdminAuthorizer</code>.</p><p>You could group your models like this:</p><div
class="gistem"><div
id="gist-2437752" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>BasicAuthorizer   AdminAuthorizer  </div><div class='line' id='LC2'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+                +          </div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+-+       +------+          </div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+       +      +          </div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Comment Article Edition</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/d38d297e3ed9a3411c3a/983d6de086e73823ac130c6a53ca6f3885b34960/gistfile1.txt" style="float:right;">view raw</a> <a
href="https://gist.github.com/d38d297e3ed9a3411c3a#file_gistfile1.txt" style="float:right;margin-right:10px;color:#666">gistfile1.txt</a> <a
href="https://gist.github.com/d38d297e3ed9a3411c3a">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>In this example, the <code>Comment</code> model&#8217;s authorization rules come from <code>BasicAuthorizer</code>, but <code>Article</code> and <code>Edition</code> get theirs from <code>AdminAuthorizer</code>. You&#8217;d call <code>self.authorizer_name =</code> on each model to set this up.</p><p>The <code>AdminAuthorizer</code> might have a method like this:</p><div
class="gistem"><div
id="gist-2437756" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">AdminAuthorizer</span> <span class="o">&lt;</span> <span class="no">Authority</span><span class="o">::</span><span class="no">Authorizer</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">creatable_by?</span><span class="p">(</span><span class="n">user</span><span class="p">)</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">user</span><span class="o">.</span><span class="n">is_admin?</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC5'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/0f9d6cb90948117b47fe/2c2b23699b9d772d6c7b55dce97d53d782fea7ec/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/0f9d6cb90948117b47fe#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/0f9d6cb90948117b47fe">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Anytime a user tries to create an <code>Article</code> or an <code>Edition</code>, this method will be called. If the user isn&#8217;t an admin, it will return false and the action will be denied.</p><p>Any method that isn&#8217;t defined on an authorizer will be inherited from <code>Authority::Authorizer</code>, which will consult a configurable <code>default_strategy</code> proc. The built-in default strategy simply returns false. This is a whitelisting approach: any action you don&#8217;t explicitly allow will be forbidden. But you can supply your own default strategy to do something more nuanced.</p><p>So the full lookup chain looks like this:</p><div
class="gistem"><div
id="gist-2437762" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default_strategy          </div><div class='line' id='LC2'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+                  </div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+-------+          </div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+                +          </div><div class='line' id='LC5'>BasicAuthorizer   AdminAuthorizer  </div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+                +          </div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+-+       +------+          </div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+       +      +          </div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Comment Article Edition</div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/e577d3ef3b988aa2a1a0/73642a97aaf073a781c587e54c6b74179bc44622/gistfile1.txt" style="float:right;">view raw</a> <a
href="https://gist.github.com/e577d3ef3b988aa2a1a0#file_gistfile1.txt" style="float:right;margin-right:10px;color:#666">gistfile1.txt</a> <a
href="https://gist.github.com/e577d3ef3b988aa2a1a0">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h2>Standard Ruby Classes And Methods</h2><p>The nice thing about this structure is that it&#8217;s just regular object-oriented programming. Your authorizers are just classes, so you can modify them any way you like: include modules, change the parent class, metaprogram, etc.</p><p>In addition, the authorizer&#8217;s methods are just plain Ruby methods: there&#8217;s no new syntax to learn. Authority makes no assumptions about what logic you&#8217;ll need. You can consult a database or a file or a web service to make your decisions; if you use a database, you can use whatever ORM you like. All the logic is up to you; Authority just helps you keep it organized.</p><h2>Syntactic Sugar for Users</h2><p>So far we&#8217;ve seen our authorization methods in the passive voice: is this resource creatable by this user? But it&#8217;s nice to be able to say the same thing in an active voice: can this user create this resource?</p><p>Like several other popular authorization gems, Authority gives you this syntactic sugar. <code>current_user.can_edit?(@article)</code> is simply a pass-through to <code>@article.editable_by?(current_user)</code>, which in turn would ask the <code>AdminAuthorizer</code>.</p><p>Since <code>can_edit?</code> and similar methods are defined on the user object, you can use them anywhere that object is available. A common case would be to show links only to users who should see them:</p><div
class="gistem"><div
id="gist-2437773" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">link_to</span> <span class="n">new_article_path</span> <span class="k">if</span> <span class="n">current_user</span><span class="o">.</span><span class="n">can_create?</span><span class="p">(</span><span class="no">Article</span><span class="p">)</span></div><div class='line' id='LC2'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/322a41a42087a7c091d1/f1df9239b3a58ec9acf3b40edd3489de03764473/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/322a41a42087a7c091d1#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/322a41a42087a7c091d1">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h2>A Little Magic for Controllers</h2><p>If you&#8217;re using the method shown above to hide links from unauthorized users, most people won&#8217;t try anything they shouldn&#8217;t be doing. But what if someone manually types the URL to edit a resource that&#8217;s forbidden for them?</p><p>At that point, your controller must intervene. Authority gives you a couple of methods for this: <code>authorize_actions_for(ModelName)</code>, which checks class-level permissions and will stop the controller method from ever running, and <code>authorize_action_for(@model_instance)</code>, which checks instance-level permissions from inside a controller method.</p><p>In either case, forbidden actions are handled by a controller method that you specify; the default one logs the user&#8217;s action and displays a warning.</p><h2>Check it out</h2><p>You can get more detail on everything discussed here by looking at <a
href="http://github.com/nathanl/authority">the README on Github</a>. I&#8217;d also encourage you to read the source code; Authority isn&#8217;t a large gem, and the source is well-commented. You may learn something, and of course, reading the code is the first step towards contributing.</p><p>Happy hacking!</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/authorizing-your-rails-app-with-authority/feed/</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>Sinatra Up and Running: A Book Review</title><link>http://rubysource.com/sinatra-up-and-running-a-book-review/</link> <comments>http://rubysource.com/sinatra-up-and-running-a-book-review/#comments</comments> <pubDate>Wed, 25 Apr 2012 13:30:09 +0000</pubDate> <dc:creator>Darren Jones</dc:creator> <category><![CDATA[Misc]]></category> <category><![CDATA[Sinatra]]></category> <category><![CDATA[books]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3602</guid> <description><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/sinatraupandrunning-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="book cover" title="Sinatra Up and Running cover" />Sinatra: Up and Running was published at the end of last year by O&#8217;Reilly Press and is the first book to be written exclusively about Sinatra. It is written by Alan Harris and Konstantin Haase. Konstantin is the current maintainer of Sinatra&#8230;]]></description> <content:encoded><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/sinatraupandrunning-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="book cover" title="Sinatra Up and Running cover" /><p><a
href="http://shop.oreilly.com/product/0636920019664.do">Sinatra: Up and Running</a> was published at the end of last year by O&#8217;Reilly Press and is the first book to be written exclusively about <a
href="http://www.sinatrarb.com/">Sinatra</a>. It is written by <a
href="https://twitter.com/#!/anachronistic">Alan Harris</a> and <a
href="http://rkh.im/">Konstantin Haase</a>. Konstantin is the current maintainer of Sinatra and probably knows more about it than anybody else (You can find out more about him in <a
href="http://rubysource.com/interview-with-konstantin-haase/">my interview</a>). Alan has already written two books about Python, and is also an enthusiastic Rubyist and Sinatra user. I have been waiting for a book to be written about Sinatra for a long time and was really looking forward to this &#8230; and it didn&#8217;t disappoint.</p><p><a
href="http://rubysource.com/?attachment_id=3659" rel="attachment wp-att-3659"><img
class="aligncenter size-full wp-image-3659" src="http://cdn.rubysource.com/files/2012/04/sinatraupandrunning.jpg" alt="book cover" width="500" height="656" /></a></p><p>The book is very thin and weighs in at just over 100 pages. But, like Sinatra, it packs a big punch into a small, lean package. It covers the latest version of Sinatra at the time of writing (1.3.1) and gets started quickly: The first chapter introduces Sinatra along with its design goals and philosophy. There&#8217;s also an interesting section on how learning Sinatra will help to develop transferable skills that can be used in other development environments. The first chapter concludes with a quick tutorial that uses Sinatra to build a complete web app. It&#8217;s a very basic rendition of the classic kid&#8217;s game <a
href="http://en.wikipedia.org/wiki/Rock-paper-scissors">Rock, Paper Scissors</a> that perfectly emphasizes Sinatra&#8217;s credentials for rapid development. This chapter does dive straight in to some of the more advanced aspects of Sinatra, but it is more to whet the appetite for what is to follow, rather than a tutorial of how it works (and this is made clear in the text).</p><p>The second chapter is very referencey in nature as it goes through a large number of Sinatra&#8217;s methods and how they work. It begins by going through the basics of HTTP headers and verbs such as GET, POST, PUT, DELETE and the newly added PATCH, which helps develop an understanding of how Sinatra is essentially a wrapper on top of <a
href="http://rack.rubyforge.org/">Rack</a> for routing HTTP requests. In fact, this chapter illustrates the fact that Sinatra is really just a collection of helper methods that provide syntactic sugar for Rack&#8217;s methods. A Highlight in this chapter was the introduction of the new streaming API that allows you to keep the connection to the server open and do things like this:</p><div
class="gistem"><div
id="gist-2464149" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="n">stream</span> <span class="k">do</span> <span class="o">|</span><span class="n">out</span><span class="o">|</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">out</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;3</span></div><div class='line' id='LC4'><span class="s2">    sleep 1</span></div><div class='line' id='LC5'><span class="s2">    out &lt;&lt; &quot;</span><span class="mi">2</span> <span class="p"></span><span class="n">n</span><span class="s2">&quot;</span></div><div class='line' id='LC6'><span class="s2">    sleep 1</span></div><div class='line' id='LC7'><span class="s2">    out &lt;&lt; &quot;</span><span class="mi">1</span><span class="p"></span><span class="n">n</span><span class="s2">&quot;</span></div><div class='line' id='LC8'><span class="s2">    sleep 1</span></div><div class='line' id='LC9'><span class="s2">    out &lt;&lt; &quot;</span><span class="no">BLAST</span> <span class="no">OFF</span><span class="o">!!!</span><span class="s2">&quot;</span></div><div class='line' id='LC10'><span class="s2">  end</span></div><div class='line' id='LC11'><span class="s2">end</span></div><div class='line' id='LC12'><span class="s2"> </span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2464149/db59cdaeb17dbadbbc7ef71ef888791aa68d7abf/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2464149#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2464149">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>The chapter also covers useful stuff such as caching, error handling (particularly 404 and 500 Internal Server errors), before and after filters, and rendering views and sessions. A lot of this info can be found in the excellent <a
href="http://www.sinatrarb.com/intro">ReadMe section</a> of the Sinatra website, but it was nice to see it all written down in the same place.</p><p>Chapter 3 starts to get heavier and highlights the symbiotic relationship that Sinatra has with Rack &#8211; the foundation for its magic. For example, there is a nice explanation of how the &#8216;get&#8217; method works. It also covers how to go about extending the functionality of Sinatra using extension methods and helpers. Generally, extensions are used configuration or routing, whereas helpers are used in view blocks and templates. The chapter then goes quite deep into Rack and has a great example of a mini-implementation of Sinatra using Rack, which shows a little of how Sinatra and Rack work together. The chapter finishes by discussing how to use Sinatra to create Rack middleware applications.</p><p>Chapter 4 shows how you can subclass Sinatra in order to build modular applications. This builds the foundations of the good practice of creating your own structured framework. This is done by subclassing Sinatra itself like so:</p><div
class="gistem"><div
id="gist-2464157" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nb">require</span> <span class="s1">&#39;sinatra/base&#39;</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="k">class</span> <span class="nc">MyApp</span> <span class="o">&lt;</span> <span class="no">Sinatra</span><span class="o">::</span><span class="no">Base</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="n">code</span> <span class="k">for</span> <span class="n">your</span> <span class="no">App</span> <span class="n">goes</span> <span class="n">here</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC5'><span class="k">end</span></div><div class='line' id='LC6'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2464157/7759604f98563d3788bbcf3d9f8e2fec8066777e/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2464157#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2464157">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>It then goes on to explain how to use the <code>run!</code> command to get the server started. Settings are discussed next and, it turns out, they are actually just methods of the application class. It also covers subclassing these classes further and how the routes are inherited. The next section is really cool &#8211; it shows how you can very easily mimic Rails&#8217; controller architecture by subclassing Sinatra::Base with an ApplicationController class and then subclassing your controllers from this, like so:</p><div
class="gistem"><div
id="gist-2464167" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="nb">require</span> <span class="s1">&#39;sinatra/base&#39;</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">Sinatra</span><span class="o">::</span><span class="no">Base</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="n">general</span> <span class="n">controller</span> <span class="n">code</span> <span class="n">goes</span> <span class="n">here</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC5'><span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="k">class</span> <span class="n">myController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="n">specific</span> <span class="n">controller</span> <span class="n">code</span> <span class="n">goes</span> <span class="n">here</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC9'><span class="k">end</span></div><div class='line' id='LC10'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2464167/8119074da734e6523bc332cac0dab82fa86288b8/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2464167#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2464167">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Things get even cooler as the authors demonstrate how to dynamically create applications on the fly using <code>Sinatra.new</code>. The chapter finishes off by showing how these subclasses fit into the Rack ecosystem as middleware and also how to use Extensions and Helpers in modular applications.</p><p>The last chapter in the book takes a more tutorial-led approach, going through the steps required to build a blog application that uses Git for versioning. There are some great nuggets in this chapter, including using the ostruct library to store the blog posts, using a hook to reload the app whenever there are any updates, and parsing the data for each article on the pages. It was also nice to see how the project folder was structured and some examples of views using erb, markdown, JavaScript and CSS. As a tutorial, it moves along at a fair pace &#8211; a lot of ground is covered in only a few pages &#8211; but it is easy to follow.</p><p>I was really impressed with this book. There isn&#8217;t much there, but what is there is worth it&#8217;s weight in gold and you can fly through it in a weekend (a plus point in my view). A word of warning though &#8211; it&#8217;s quite theoretical in places. Although you don&#8217;t need to know anything about Sinatra as a prerequisite, you will need a reasonable level of Ruby knowledge in order to follow along. You also need to be fairly confident at using the command line and installing gems as well as firing up a local server.</p><p>This shouldn&#8217;t put anybody off though &#8211; nothing is particularly difficult and since everything in Sinatra is just Ruby there are lots of places you can go to find out more information. I also found that the book really helped to improve my Ruby skills &#8211; it&#8217;s choc full of well-written snippets of ruby. It also encourages you to fire up an irb session and experiment with Sinatra in order to help further your understanding of how it works.</p><p>As the first book about Sinatra, I feel that this book is a success. The authors have done a great job of introducing Sinatra as well as giving the reader a thorough understanding of its inner-workings. Although the first and last chapter both contain example apps, they don&#8217;t really cover many use-cases. I feel that there is still a space for a more practical book that goes through building apps in more detail and design patterns that can be used for different types of applications. For example, database-driven apps are not featured at all in the book. If you want more practical advice then I can recommend <a
href="http://www.packtpub.com/cloning-internet-applications-with-ruby/book">Cloning Internet Applications in Ruby</a> by Shau Seung.</p><p>The good news is that I hear there&#8217;s a follow up planned. Some things I&#8217;d like to see covered are:</p><ul><li>More examples of using the streaming API</li><li>Testing &#8230; it&#8217;s not covered at all in the book</li><li>How to structure a modular application (file structure etc)</li></ul><p>In the meantime, I would recommend anybody with an interest in improving their Sinatra or Ruby skills, or finding out what all the Sinatra fuss is about to buy this book.</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/sinatra-up-and-running-a-book-review/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Hitchhiker&#8217;s Guide to Metaprogramming: Class/Module Hooks</title><link>http://rubysource.com/hitchhikers-guide-to-metaprogramming-classmodule-hooks/</link> <comments>http://rubysource.com/hitchhikers-guide-to-metaprogramming-classmodule-hooks/#comments</comments> <pubDate>Mon, 23 Apr 2012 13:30:52 +0000</pubDate> <dc:creator>Jonathan Jackson</dc:creator> <category><![CDATA[Best Practices]]></category> <category><![CDATA[Ruby Deployment]]></category> <category><![CDATA[metaprogramming]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=3606</guid> <description><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/shutterstock_75615193-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="shutterstock_75615193" title="shutterstock_75615193" />Rule one to metaprogramming: Don&#8217;t Panic! Like many others, I have struggled with the term metaprogramming. For the purposes of this article I&#8217;ll be going broad with my working definition of metaprogramming to include: Any code that significantly raises the level of&#8230;]]></description> <content:encoded><![CDATA[<img
width="150" height="150" src="http://cdn.rubysource.com/files/2012/04/shutterstock_75615193-150x150.jpg" class="attachment-thumbnail wp-post-image" alt="shutterstock_75615193" title="shutterstock_75615193" /><p><a
href="http://rubysource.com/?attachment_id=3607" rel="attachment wp-att-3607"><img
class="alignleft size-medium wp-image-3607" src="http://cdn.rubysource.com/files/2012/04/shutterstock_75615193-300x300.jpg" alt="" width="300" height="300" /></a>Rule one to metaprogramming: <strong>Don&#8217;t Panic!</strong></p><p>Like many others, I have struggled with the term metaprogramming. For the purposes of this article I&#8217;ll be going broad with my working definition of metaprogramming to include:</p><blockquote><p>Any code that <strong>significantly</strong> raises the level of abstraction and/or any code that creates code. -Me</p></blockquote><p>This definition is wildly oversimplified. But in order to start learning to metaprogram you have to have something you can point to and say &#8220;That is metaprogramming.&#8221; I&#8217;m also intentionally side-stepping the &#8220;There is no metaprogramming, there is only programming&#8221; stance here. While I partially agree, this is not the place for such a philosophical debate. If you are interested in that debate, there is a great Ruby Rogues podcast about it where several prominent &#8220;Rubyists&#8221; discuss the definition of metaprogramming in much greater detail.<a
href="http://rubyrogues.com/metaprogramming-in-ruby/">[1]</a></p><p>To start with, metaprogramming is not magical. It will not solve all of your problems. Its usage will not make you a rock star. I&#8217;m also going to bypass the whole, &#8220;You really shouldn&#8217;t run with scissors,&#8221; warning and assume that you&#8217;ll apply sound logic in your application of metaprogramming. Caveats aside, it can be a wonderful addition to your toolbelt. Quite simply, it is a way to get your code to do a little bit more for you. If used properly, it will result in less repetitive, more easily tested, and cleaner code. It may be a little confusing to understand at first, but once you get the knack of it you can solve a host of problems that would be incredibly difficult otherwise. In Part One of the guide, I&#8217;m going to run through several class/module hooks. These hooks will allow you to improve the readability of your code. Sometimes it feels like magic, but it&#8217;s not.</p><p>If you get lost anywhere, just remember <strong>rule #1</strong>. Also, look to the end of the post for links to more resources/help.</p><h1>The hooks</h1><h2>inherited</h2><p>The first hook I&#8217;d like to discuss is <code>#inherited</code>. It is the hook that allows code like this to exist in Rails:</p><p><code><br
/><div
class="gistem"><div
id="gist-2470567" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="n">has_many</span> <span class="ss">:comments</span></div><div class='line' id='LC3'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470567/ab0a344b62b4c94fa8789980e7a8df2d1641aae6/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470567#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470567">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></code></p><p>The new developer sees this and either ignores it or infers a comment above <code>class Posts</code> that says something to the effect of <code>#there be dragons here</code>. Not true. The &#8220;magic&#8221; is in relying on the Ruby hook provided by <code>#inherited</code>. You won&#8217;t find <code>#inherited</code> on <a
href="http://ruby-doc.org/">ruby-doc.org</a> because it is a private method (which ruby-doc.org does not list). Instead of calling this method you simply define a method and name it &#8216;inherited.&#8217; You can see more detail at <a
href="http://apidock.com/ruby/Class/inherited">apidoc.com</a> (this is also true of some of the other methods listed below). If you head over there, you&#8217;ll see this short description:</p><blockquote><p>Callback invoked whenever a subclass of the current class is created.</p></blockquote><p>Warning: drastic oversimplification inbound!</p><p>Rails does some pretty slick stuff with regards to building the <code>has_many</code> method. But in the end ActiveRecord::Base defines <code>#inherited</code>, which includes has_many and other modules, like so<a
href="https://github.com/rails/rails/blob/master/activerecord/lib/active_record/core.rb#L89">[2]</a>:</p><p><code><br
/><div
class="gistem"><div
id="gist-2470568" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">module</span> <span class="nn">ClassMethods</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">inherited</span><span class="p">(</span><span class="n">child_class</span><span class="p">)</span> <span class="c1">#:nodoc:</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">child_class</span><span class="o">.</span><span class="n">initialize_generated_modules</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">super</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="c1">#etc</span></div><div class='line' id='LC7'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470568/542fbbfb6e00de33e7a7517898db95473782a0a7/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470568#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470568">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><br
/> </code><br
/> When you inherit from ActiveRecord::Base the above method is fired. <code>#initialize_generated_modules</code> sets up the methods/modules and throws them into your Model (allowing you to build your association). That is potentially a little confusing. In the end this is what is happening:</p><div
class="gistem"><div
id="gist-2470572" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Mammal</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">inherited</span><span class="p">(</span><span class="n">child_class</span><span class="p">)</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Hey I&#39;m going to be called when I&#39;m inherited&quot;</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">super</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="k">end</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'><span class="k">class</span> <span class="nc">Dog</span> <span class="o">&lt;</span> <span class="no">Mammal</span></div><div class='line' id='LC10'><span class="k">end</span></div><div class='line' id='LC11'><span class="c1">#</span></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'><span class="o">=&gt;</span> <span class="s2">&quot;Hey I&#39;m going to be called when I&#39;m inherited&quot;</span></div><div class='line' id='LC14'><br/></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470572/5695247ac21e452311b155e724dd83d832111c9c/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470572#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470572">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>Pretty dern cool, imho. It is important to note that <code>#inherited</code> is called before subclass is fully defined, so any conditional logic that involves the subclass will not work. Also, calling <code>super</code> after is reccomended after executing your custom code.</p><h2>included</h2><blockquote><p>Callback invoked whenever the receiver is included in another module or class.</p></blockquote><p>Every Ruby developer is familiar with <code>#include</code>, which allows you to inject a module&#8217;s instance methods into a class. Similarly, to include a module&#8217;s class methods you&#8217;d use <code>#extend</code>. So, if you had a module that has both instance and class methods, you&#8217;d have to do something like the following to get the entire module into your class:</p><p><code><br
/><div
class="gistem"><div
id="gist-2470578" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">MyClass</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">Mymodule</span> <span class="c1"># instance methods</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class="kp">extend</span> <span class="no">Mymodule</span> <span class="c1"># class methods</span></div><div class='line' id='LC4'><span class="k">end</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470578/9db2708b1d2b38e3ee13c1e19096f33503ef1811/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470578#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470578">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div></code></p><p>Which brings us to our next hook, which allows us to execute arbitrary code whenever a module is included into a class. It&#8217;s defined on <a
href="http://apidock.com/ruby/Module/included">Module</a>, and there are some idioms associated with its use. To accomplish the preceding example, you could simply define <code>#included</code>:</p><div
class="gistem"><div
id="gist-2470592" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">module</span> <span class="nn">MyModule</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">included</span><span class="p">(</span><span class="n">base</span><span class="p">)</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">base</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="no">ClassMethods</span><span class="p">)</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">hellofrominstance</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;hello from </span><span class="si">#{</span><span class="nb">self</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC8'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>&nbsp;&nbsp;<span class="c1"># Conventionally named ClassMethods</span></div><div class='line' id='LC11'>&nbsp;&nbsp;<span class="k">module</span> <span class="nn">ClassMethods</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">def</span> <span class="nf">hellofromclass</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Hello from </span><span class="si">#{</span><span class="nb">self</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC15'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC16'><span class="k">end</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'><span class="c1"># including MyModule will call the hook (#included) which</span></div><div class='line' id='LC19'><span class="c1"># extends the class methods</span></div><div class='line' id='LC20'><span class="k">class</span> <span class="nc">Blah</span></div><div class='line' id='LC21'>&nbsp;&nbsp;<span class="kp">include</span> <span class="no">MyModule</span></div><div class='line' id='LC22'><span class="k">end</span></div><div class='line' id='LC23'><br/></div><div class='line' id='LC24'><span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Blah</span><span class="o">.</span><span class="n">hellofromclass</span></div><div class='line' id='LC25'><span class="c1">#=&gt; &quot;Hello from Blah&quot;</span></div><div class='line' id='LC26'><span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Blah</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">hellofrominstance</span></div><div class='line' id='LC27'><span class="c1">#=&gt; &quot;hello from #&quot;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470592/7d48314f55e2b730807964076a52af49f4100537/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470592#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470592">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>This is just one use of <code>#included</code>. Any code could be executed in the included hook, but this is a common idiom that you <strong>will</strong> see. When we include <code>MyModule</code> into the <code>Blah</code> class, we saw the hook fire, which calls extend on &#8216;base&#8217;. In this case, the argument &#8216;base&#8217; represents the current <code>self</code> which ends up being <code>Blah</code> at the time included is called (yikes, we&#8217;ll talk more about <code>self</code> in another post). Pretty Batman, right?.</p><h2>extended</h2><p>I won&#8217;t go into this at length due to its similarity to <code>#included</code>. It will fire arbitrary code when the module is extended. It is my opinion that doing the inverse of the above (included) idiom is less acceptable. However, I think this is still an amazing tool. A completely contrived example could look like this:</p><div
class="gistem"><div
id="gist-2470596" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="c1"># execute this snippet</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="k">module</span> <span class="nn">MyModule</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">extended</span><span class="p">(</span><span class="n">base</span><span class="p">)</span></div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Howdy!&quot;</span></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC7'><span class="k">end</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'><span class="k">class</span> <span class="nc">Blah</span></div><div class='line' id='LC10'>&nbsp;&nbsp;<span class="kp">extend</span> <span class="no">MyModule</span></div><div class='line' id='LC11'><span class="k">end</span></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'><span class="o">=&gt;</span> <span class="no">Howdy</span><span class="o">!</span></div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'><span class="o">=&gt;</span> <span class="no">Blah</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470596/56b0324b36a612970332b3440e3f2e77e10aed41/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470596#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470596">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>If you execute the snippet above, you&#8217;ll see that <code>#extended</code> gets called when Blah extends it. Pretty neat. Movin&#8217; on.</p><h2>extend_object</h2><blockquote><p>Extends the specified object by adding this module’s constants and methods (which are added as singleton methods).</p></blockquote><p>When you call Object#extend the extend_object, callback is fired. This is especially useful when you would like to extend an object, but only if it has certain characteristics.</p><div
class="gistem"><div
id="gist-2470605" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">module</span> <span class="nn">MyModule</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">extend_object</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Hello from </span><span class="si">#{</span><span class="nb">self</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">super</span> <span class="c1"># important</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">hello</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">puts</span> <span class="s2">&quot;Hello from </span><span class="si">#{</span><span class="nb">self</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC9'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC10'><span class="k">end</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'><span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">(</span><span class="n">myextendedstring</span> <span class="o">=</span> <span class="s2">&quot;blah&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="no">MyModule</span><span class="p">)</span></div><div class='line' id='LC13'><span class="c1">#=&gt; Hello from MyModule</span></div><div class='line' id='LC14'><span class="c1">#=&gt; &quot;blah&quot;</span></div><div class='line' id='LC15'><span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">s</span><span class="o">.</span><span class="n">hello</span></div><div class='line' id='LC16'><span class="c1">#=&gt; &quot;Hello from blah&quot;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470605/2fc8b4fd72879230a07cc87e011828f5ab0afae2/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470605#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470605">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><p>This will actually extend the variable <code>my_extended_string</code>, which happens to be the string &#8220;blah&#8221;, with MyModule.</p><h2>const_missing</h2><p>This is very similar to method_missing except it is called when a constant is missing or undefined. The example the docs give is pretty hairy, so I&#8217;ll make my own very contrived example of what you might use this for.</p><div
class="gistem"><div
id="gist-2470610" class="gist"><div
class="gist-file"><div
class="gist-data gist-syntax"><div
class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">MyCustomError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="c1"># super terribad</span></div><div class='line' id='LC4'><span class="k">def</span> <span class="nc">Object</span><span class="o">.</span><span class="nf">const_missing</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class="k">raise</span> <span class="no">MyCustomError</span><span class="p">,</span> <span class="s2">&quot;You attempted to use a constant that wasn&#39;t defined: </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span></div><div class='line' id='LC6'><span class="k">end</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'><span class="n">pry</span><span class="p">(</span><span class="n">main</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">WTF</span></div><div class='line' id='LC9'><span class="c1">#=&gt; MyCustomError: You attempted to use a constant that wasn&#39;t defined: WTF from :3:in `const_missing&#39;</span></div></pre></div></div><div
class="gist-meta"> <a
href="https://gist.github.com/raw/2470610/fe457c2469a77c0d22ab81e0667310952d649e62/gistfile1.rb" style="float:right;">view raw</a> <a
href="https://gist.github.com/2470610#file_gistfile1.rb" style="float:right;margin-right:10px;color:#666">gistfile1.rb</a> <a
href="https://gist.github.com/2470610">This Gist</a> is brought to you using <a
href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.</div></div></div></div><h1>Conclusions</h1><p>These hooks are often used in some of the more clever pieces of Ruby code floating around. Utilizing them can help you write better code. Not only that, but using these methods will also help you solidify your understanding of how Ruby executes/loads/manages each program you write.</p><p>In a talk given back in 2007, <a
href="http://twitter.com/#!/pragdave">@pragdave</a> explained some of the reasons why extending Ruby through metaprogramming is one of the mainstays of the language. He spoke of how metaprogramming in Ruby raises the level of abstraction. Essentially, this means increasing the distance between machine/language and your code. This is good for a few reasons. For one thing, the code ends up looking much more readable to both coders and clients. Also, once the underlying code is laid, a new developer has to understand less to be able to contribute. This raised level of abstraction is an amazing part of Ruby, and is prominently featured in some of its largest projects (especially Rails). I hope you find yourself exploring the hooks mentioned above in your own code. I&#8217;m sure that if you do, you&#8217;ll begin to think of Ruby a little more fondly. Thanks for reading.</p><p>This is first edition of Hitchhiker&#8217;s Guide to Metaprogramming. The next edition will cover more of the fundamentals of metaprogramming in Ruby. Learn more about metaprogramming at my <a
href="http://jonathan-jackson.net/2012/04/23/hitchhikers-guide-to-metaprogramming-classmodule-hooks.html">blog</a> where I&#8217;ve posted several links I&#8217;ve found helpful while writing this post. ^_^</p> <span
id="pty_trigger"></span>]]></content:encoded> <wfw:commentRss>http://rubysource.com/hitchhikers-guide-to-metaprogramming-classmodule-hooks/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using memcached
Database Caching 16/18 queries in 0.025 seconds using memcached
Object Caching 1562/1572 objects using memcached
Content Delivery Network via cdn.rubysource.com

Served from: rubysource.com @ 2012-05-17 20:43:18 -->
