<?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/"
	>

<channel>
	<title>The Carbon Emitter &#187; rapid development</title>
	<atom:link href="http://blog.carbonfive.com/tag/rapid-development/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.carbonfive.com</link>
	<description>The blog of Carbon Five</description>
	<lastBuildDate>Mon, 23 Jan 2012 18:38:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Deploying to Heroku from TeamCity</title>
		<link>http://blog.carbonfive.com/2010/08/06/deploying-to-heroku-from-teamcity/</link>
		<comments>http://blog.carbonfive.com/2010/08/06/deploying-to-heroku-from-teamcity/#comments</comments>
		<pubDate>Fri, 06 Aug 2010 07:09:32 +0000</pubDate>
		<dc:creator>Jonah Williams</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[continuous integration]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rapid development]]></category>
		<category><![CDATA[Testing]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=1098</guid>
		<description><![CDATA[Previously I discussed our TeamCity configuration using RVM and mentioned that we often use git to deploy projects. Today I&#8217;ll share an example of how a TeamCity build agent can trigger deployments of a application hosted on Heroku and some &#8230; <a href="http://blog.carbonfive.com/2010/08/06/deploying-to-heroku-from-teamcity/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Previously I discussed our <a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_heroku_build_runner21.png2010/08/ruby-on-rails/using-rvm-on-teamcity-build-agents">TeamCity configuration</a> using RVM and mentioned that we often use git to deploy projects. Today I&#8217;ll share an example of how a TeamCity build agent can trigger deployments of a application hosted on <a href="http://heroku.com/">Heroku</a> and some of the challenges I found.<br />
<span id="more-1098"></span><br />
<strong>Add an ssh config entry for the project&#8217;s access to Heroku</strong></p>
<p>Much like shared github access I need to be able to specify which ssh key a particular project&#8217;s connections to heroku should use. Since I want to use client side checkouts I can&#8217;t specify a private key within TeamCity but I do know that all my compatible build agents are local so I can add a new entry to `~/.ssh/config` and define a host which will allow me to connect to Heroku with a particular key.</p>
<p><code># Heroku for #{new_project}<br />
Host heroku-#{new_project}<br />
  HostName heroku.com<br />
  User git<br />
  ForwardAgent yes<br />
  IdentityFile /home/teamcity/.ssh/teamcity_#{new_project}_rsa<br />
</code></p>
<p><strong>Add a ssh key to the Heroku account</strong><br />
Now I need to add this new key to the Heroku account for this project by installing the heroku gem in my project&#8217;s gemset and using the `heroku` command line to add the key. Heroku stores the account credentials used in `~/.heroku` and the user account running my build agents might need to connect to many different heroku accounts. I could switch my heroku credentials, upload a key, and have different projects deploy using different accounts. However since I find it useful to be able to run the heroku command line (see below) I found it preferable to have a single `teamcity` heroku account added to every project as a collaborator.<br />
<em>If you find it necessary to switch between Heroku accounts frequently this tool from Aeonscope might be useful:</em> <a href="http://www.aeonscope.net/2010/02/22/managing-multiple-heroku-accounts/">managing multiple heroku accounts</a></p>
<p>Now I can add a key to the current project&#8217;s Heroku account.<br />
<code>heroku keys:add ~/.ssh/teamcity_#{new_project}_rsa</code></p>
<p><strong>Deploying to Heroku</strong><br />
Now I can add a simple TeamCity build configuration which pushes code to Heroku once a particular revision passes the tests.</p>
<table>
<tbody>
<tr>
<td>Command executable</td>
<td>git</td>
</tr>
<tr>
<td>Command parameters</td>
<td>push git@heroku-#{new_project}:#{new heroku project&#8217;s name}.git master</td>
</tr>
</tbody>
</table>
<p><div id="attachment_1132" class="wp-caption alignnone" style="width: 585px"><a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_heroku_build_runner21.png"><img src="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_heroku_build_runner21.png" alt="" title="tc_heroku_build_runner" width="575" height="319" class="size-full wp-image-1132" /></a><p class="wp-caption-text">A project's deployment build configuration</p></div><br />
<strong>Deploying and running database migrations</strong><br />
I don&#8217;t want my app trying to respond to requests while waiting for a database migration to finish so now it becomes necessary to put the app in maintenance mode before deploying and migrating.</p>
<table>
<tbody>
<tr>
<td>Command executable</td>
<td>sh</td>
</tr>
<tr>
<td>Command parameters</td>
<td>-c heroku maintenance:on &#8211;app #{new_project} &amp;&amp; sleep 60 &amp;&amp; git push git@heroku.com:#{new_project}.git master &amp;&amp; heroku rake db:migrate &#8211;app #{new_project} &amp;&amp; heroku maintenance:off &#8211;app #{new_project}</td>
</tr>
</tbody>
</table>
<p>I found that I need to add a delay between turning on maintenance mode for the app and pushing to heroku. Attempting to push immediately fails with the error `Heroku push rejected, your slug is currently being compiled. Please try again later.` Even with a significant delay I still see this error occasionally and have to rerun the deploy build but at this point I don&#8217;t know of a way to make sure heroku can handle a new revision before attempting to push changes.</p>
<p>Now I can use TeamCity to automatically deploy changes to Heroku as soon as my project&#8217;s tests pass.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2010/08/06/deploying-to-heroku-from-teamcity/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using RVM on TeamCity build agents</title>
		<link>http://blog.carbonfive.com/2010/08/05/using-rvm-on-teamcity-build-agents/</link>
		<comments>http://blog.carbonfive.com/2010/08/05/using-rvm-on-teamcity-build-agents/#comments</comments>
		<pubDate>Fri, 06 Aug 2010 04:52:16 +0000</pubDate>
		<dc:creator>Jonah Williams</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[continuous integration]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[rapid development]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[Testing]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=1106</guid>
		<description><![CDATA[We have been using TeamCity to manage the continuous integration, testing, and deployment of many of our recent projects. We have also been using RVM on all of our recent Rails projects to allow us to install multiple ruby versions &#8230; <a href="http://blog.carbonfive.com/2010/08/05/using-rvm-on-teamcity-build-agents/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>We have been using <a href="http://www.jetbrains.com/teamcity/">TeamCity</a> to manage the continuous integration, testing, and deployment of many of our recent projects. We have also been using <a href="http://rvm.beginrescueend.com">RVM</a> on all of our recent Rails projects to allow us to install multiple ruby versions and create isolated gemsets for each project. RVM proved to be particularly useful on our TeamCity build agents where it allows a single agent to build many projects without the fear that we will see gem or ruby version conflicts between projects or introduce dependencies on gems installed on the build server but not enumerated in the project. Here&#8217;s the configuration I have used to get our build agents to use each project&#8217;s RVM settings.<br />
<span id="more-1106"></span><br />
As much as I would like to see each project able to configure all of it&#8217;s dependencies on its environment we still have some dependencies which are not captured in the project configuration. For example we have projects which need access to a database servers or need private keys to use when deploying. As a result I have favored running as many build agents as possible as local build agents so that they share this configuration. The configuration below is therefore usually only applied once but in an environment using remote build agents it would need to be repeated or shared between every compatible remote agent&#8217;s system. Perhaps one day we&#8217;ll see each project include chef recipes to configure it&#8217;s own build environment and I would be interested to hear from anyone who has automated their CI configuration to that extent.</p>
<p><strong>1. Create a key for github access</strong><br />
   <code>&gt; ssh-keygen -t rsa -C "#{project_name}"</code><br />
   Upload the public key to github as a deploy key for the project so that the build agent can pull from the github repository.</p>
<p><strong>2. Setup rvm and a gemset for the project</strong><br />
<code>&gt; rvm install ruby-#{version}-r#{patch level}<br />
&gt; rvm use ruby-#{version}-r#{patch level}<br />
&gt; rvm gemset create #{project name}<br />
&gt; rvm gemset use #{project name}<br />
&gt; gem install bundler</code></p>
<p><em>This could probably all be automated as part of the project&#8217;s .rvmrc file.</em></p>
<p><strong>3. Setup a ssh config for the project</strong><br />
The keys used by many of the services we interact with are unique and tied to an account so we need to have TeamCity use different ssh keys when connecting to projects under different accounts. Add a new entry to ~/.ssh/config to alias a host to use a specific ssh key</p>
<p><code># GitHub for #{new_project}<br />
Host github-#{new_project}<br />
    HostName github.com<br />
    User git<br />
    ForwardAgent yes<br />
    IdentityFile /home/teamcity/.ssh/teamcity_#{new_project}_rsa<br />
</code></p>
<p>Connections to `github-#{new_project}` will now default to use `teamcity_#{new_project}_rsa` rather than the default `id_rsa`. By specifying the key in the ssh config file we&#8217;re able to use client side checkouts which will be useful when deploying using git. For projects able to use server side checkouts we can specify a private key to use in TeamCity&#8217;s VCS settings.</p>
<p><strong>4. Create a new TeamCity project</strong><br />
On projects with multiple build steps using the same build runner we could create a template to avoid repeating these settings in each build phase. Unfortunately TeamCity 5.1 templates do not support running multiple types of build runners so projects with both command line (`bundle install`) and rake runners (`rake spec`) require some repetition.</p>
<table>
<thead>
<tr>
<th>Setting the VCS root:</th>
</tr>
</thead>
<tbody>
<tr>
<td>Type of VCS</td>
<td>Git(JetBrains)</td>
</tr>
<tr>
<td>Fetch URL</td>
<td>git@github-#{project name}:#{account name}/#{project name}.git</td>
</tr>
<tr>
<td>Authentication method</td>
<td>default private key</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Version Control Settings:</th>
</thead>
<tbody>
<tr>
<td>VCS Checkout Mode</td>
<td>Automatically on client</td>
</tr>
</tbody>
</table>
<p>Client checkouts have a full git repository while a server side checkout performs a git pull and then copies the resulting files but not the full git work-tree to the build agents leaving the agents unable to easily run git commands. A client side checkout is therefore preferable when using `git push` to deploy a project.</p>
<table>
<thead>
<tr>
<th>Command line tasks (bundle install):</th>
</tr>
</thead>
<tbody>
<tr>
<td>Runner</td>
<td>command line</td>
</tr>
<tr>
<td>Executable</td>
<td>/home/teamcity/.rvm/gems/%rvm.ruby%@%rvm.gemset%/bin/bundle</td>
</tr>
<tr>
<td>Arguments</td>
<td>install</td>
</tr>
</tbody>
</table>
<p><a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_cl_build_runner11.png"><img src="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_cl_build_runner11.png" alt="Command line build runner settings" title="tc_rvm_cl_build_runner" width="861" height="393" class="alignnone size-full wp-image-1114" /></a></p>
<table>
<thead>
<tr>
<th>Rake tasks (test):</th>
</tr>
</thead>
<tbody>
<tr>
<td>Runner</td>
<td>rake</td>
</tr>
<tr>
<td>Rake task</td>
<td>%rake.tasks%</td>
</tr>
<tr>
<td>Additional rake command line parameters</td>
<td>%rake.opts%</td>
</tr>
<tr>
<td>Ruby interpreter path</td>
<td>/home/teamcity/.rvm/rubies/%rvm.ruby%/bin/ruby</td>
</tr>
<tr>
<td>RVM Gemset Name</td>
<td>#{project name}</td>
</tr>
</tbody>
</table>
<p><a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_rake_build_runner21.png"><img src="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_rake_build_runner21.png" alt="Rake task build runner settings" title="tc_rvm_rake_build_runner" width="852" height="548" class="alignnone size-full wp-image-1117" /></a></p>
<table>
<thead>
<tr>
<th>Configuration parameters</th>
</tr>
</thead>
<tbody>
<tr>
<td>rvm.ruby</td>
<td>ruby-#{version}-r#{patch level}</td>
</tr>
<tr>
<td>rvm.gemset</td>
<td>#{new_project}</td>
</tr>
</tbody>
</table>
<p><a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_config_params21.png"><img src="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_config_params21.png" alt="Configuration parameters" title="tc_rvm_config_params" width="852" height="220" class="alignnone size-full wp-image-1115" /></a></p>
<p>If using a template set the `rake.tasks`, `rake.opts`, and any other configuration parameters the template uses. Each build configuration based on the template can specify its own configuration parameters but cannot override the rake task and command line parameters directly.</p>
<p>To use the project&#8217;s RVM ruby and gemset add the following environment variables with appropriate paths:</p>
<table>
<tbody>
<tr>
<td>BUNDLE_PATH 	/home/teamcity/.rvm/gems/%rvm.ruby%@%rvm.gemset%</td>
</tr>
<tr>
<td>GEM_HOME 	/home/teamcity/.rvm/gems/%rvm.ruby%@%rvm.gemset%</td>
</tr>
<tr>
<td>GEM_PATH 	/home/teamcity/.rvm/gems/%rvm.ruby%@%rvm.gemset%:/home/teamcity/.rvm/gems/%rvm.ruby%@global</td>
</tr>
<tr>
<td>PATH 	/home/teamcity/.rvm/bin:/home/teamcity/.rvm/rubies/%rvm.ruby%/bin:/home/teamcity/.rvm/gems/%rvm.ruby%@%rvm.gemset%/bin:/home/teamcity/.rvm/gems/%rvm.ruby%@global/bin:%env.PATH%</td>
</tr>
</tbody>
</table>
<p><a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_environment_variables21.png"><img src="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_rvm_environment_variables21.png" alt="Environment variables" title="tc_rvm_environment_variables" width="854" height="355" class="alignnone size-full wp-image-1116" /></a></p>
<p>Now I can finally build a set of build configurations for a project which will all use the project&#8217;s RVM settings to isolate it from other projects on the TeamCity server.</p>
<p><a href="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_example_project21.png"><img src="http://blog.carbonfive.com/wp-content/uploads/2010/12/tc_example_project21.png" alt="An example set of build configurations" title="tc_example_project" width="1207" height="549" class="alignnone size-full wp-image-1122" /></a></p>
<p> JetBrains appears committed to adding RVM support to TeamCity and added the `RVM Gemset` property to rake tasks in TeamCity 5.1.3 so hopefully we&#8217;ll see additional RVM support in future releases.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2010/08/05/using-rvm-on-teamcity-build-agents/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Recipe for 5 Whys with an Agile Software Team</title>
		<link>http://blog.carbonfive.com/2010/01/26/recipe-for-5-whys-with-an-agile-software-team/</link>
		<comments>http://blog.carbonfive.com/2010/01/26/recipe-for-5-whys-with-an-agile-software-team/#comments</comments>
		<pubDate>Wed, 27 Jan 2010 04:53:33 +0000</pubDate>
		<dc:creator>Andy Peterson</dc:creator>
				<category><![CDATA[Process]]></category>
		<category><![CDATA[rapid development]]></category>
		<category><![CDATA[xp]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=818</guid>
		<description><![CDATA[5 Whys is a great way to get at the root of quality problems. On my last three projects, when I felt like code quality was dropping, I ran a &#8220;5 Whys&#8221; session. I have found it adds variety, solves &#8230; <a href="http://blog.carbonfive.com/2010/01/26/recipe-for-5-whys-with-an-agile-software-team/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>5 Whys is a great way to get at the root of quality problems. On my last three projects, when I felt like code quality was dropping, I ran a &#8220;5 Whys&#8221; session. I have found it adds variety, solves a very specific problem, and plugs right in as an alternative to an <a href="http://blog.carbonfive.com/2009/12/agile/recipe-for-simple-agile-retrospectives">agile reflection</a>.</p>
<p>It&#8217;s not in every agile software team&#8217;s bag of tricks. Asking around our fairy savvy office, I discovered it&#8217;s far from universal. In the <a href="http://www.versionone.com/whitepapers.asp">&#8220;State of Agile&#8221; report from Version One</a>, which includes survey results from 2500 software developers, it wasn&#8217;t mentioned. Since I haven&#8217;t seen it show up that much in other agile writings, I thought I&#8217;d share my experiences here.<span id="more-818"></span></p>
<p><strong>What is &#8220;5 Whys&#8221;?</strong> I picked up &#8220;5 Whys&#8221; from the lean software movement, which sprang from Toyota manufacturing.  You can read about its history on <a href="http://en.wikipedia.org/wiki/5_Whys">wikipedia</a>, but it&#8217;s pretty simple: at the end of the assembly line, when a widget comes out with a problem, you stop the line and ask &#8220;Why?&#8221; Whatever the reason, you ask the &#8220;Why?&#8221; again. Repeat at least 5 times. The goal is to discover the &#8220;root cause&#8221; of the defect, and fix the root cause, not just some symptom. Wikipedia has a good example around car repair. Here&#8217;s a software example:</p>
<blockquote><p>1. Why did Sheryl say the sign-up flow was broken?<br />
<em>A: Because she was trying to sign up a second time. Duh.</em><br />
2. Yeah, but why did the flow break if the user is already signed up?<br />
<em>A: It&#8217;s a bug. We didn&#8217;t have a test case for that.</em><br />
3. Why didn&#8217;t we have a test case for it?<br />
<em>A: I just didn&#8217;t think of it.</em><br />
4. Why didn&#8217;t you think about it?<br />
<em>A: The story seemed easy&#8211; I guess I didn&#8217;t think it through.</em><br />
5. Why do you think you didn&#8217;t think it through?&#8221;<br />
<em>A: I guess I was working alone and was in a hurry.</em><br />
&#8230;</p></blockquote>
<p>Obviously you don&#8217;t have ask &#8220;Why?&#8221; exactly five times, but that does seem to be a pretty good number. The point is you start articulating the behaviors and procedures that cause the problems.</p>
<p><strong>Taking It for a Spin. </strong> I&#8217;ve come up with exercise for agile software teams.  Like I mentioned, when quality drops below your comfort level, give it a try:</p>
<ol>
<li>(2 minutes) Make a list of the most recent bugs you&#8217;ve uncovered. I&#8217;ve found that the last 10 is usually enough&#8211; if not too much. You can put these on a white board, index cards or a wiki page&#8211; all will work.</li>
<li>(2 minutes) Prioritize them based on the ones you want to talk about. Which caused the most embarrassment or cost? You usually only have energy to talk about 5 or 6 of them.</li>
<li>(15 minutes) For each bug, ask &#8220;Why did this happen?&#8221; Then, probe deeper until you can&#8217;t get much further. Write the answers down where everyone can see.</li>
<li>(5 minutes) When you&#8217;re done with all the bugs (or out of time), circle common root causes.</li>
<li>(10 minutes) Brainstorm ways to mitigate or eliminate the root causes. Come of with one or two SMART goals for the team.</li>
</ol>
<p>Some of the root causes we&#8217;ve gotten to recently:</p>
<ul>
<li> we didn&#8217;t pair on hard features</li>
<li> we didn&#8217;t ask for clarifications on the requirements</li>
<li> one engineer didn&#8217;t understand the intent behind the code</li>
<li> nobody looked at the app on Internet Explorer 6</li>
</ul>
<p>Just like in agile reflections, the changes made coming out of these meetings stick with the team. During one session, the developers decided that there were just too many mistakes that someone else would have easily caught&#8211; so they always needed two sets of eyes on every checkin. I&#8217;d advocated this before, but it was never applied consistently. To my surprise though, after the reflection, everyone was  disciplined about this policy&#8211; and our quality was much better.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2010/01/26/recipe-for-5-whys-with-an-agile-software-team/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Convenient CSS and Javascript in Ruby on Rails</title>
		<link>http://blog.carbonfive.com/2009/03/08/convenient-css-and-javascript-in-ruby-on-rails/</link>
		<comments>http://blog.carbonfive.com/2009/03/08/convenient-css-and-javascript-in-ruby-on-rails/#comments</comments>
		<pubDate>Sun, 08 Mar 2009 17:41:11 +0000</pubDate>
		<dc:creator>Andy Peterson</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript / AJAX]]></category>
		<category><![CDATA[prototyping]]></category>
		<category><![CDATA[rapid development]]></category>
		<category><![CDATA[Software Design]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=480</guid>
		<description><![CDATA[I get tired of hunting through a hierarchy of folders and files, from the views to the public folder, to locate a certain CSS or Javascript file. It&#8217;d be convenient to have them right with the markup, but embedding these &#8230; <a href="http://blog.carbonfive.com/2009/03/08/convenient-css-and-javascript-in-ruby-on-rails/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I get tired of hunting through a hierarchy of folders and files, from the views to the public folder, to locate a certain CSS or Javascript file. It&#8217;d be convenient to have them right with the markup, but embedding these definitions within your HTML markup is a bad idea for several reasons. For our current project, I proposed we put everything in the view directory so they are easy to find:</p>
<pre class="brush: java; light: true; title: ; wrap-lines: false; notranslate">app/
  views/
    home/
      index.html.rb
      index.css
      index.js</pre>
<p>The convention is clear: to add page-specific CSS code, just create a new file with the same name as the view, in the same folder. Easy to add, edit and remove. The alternative (using the public folder), usually leads to a parallel hierarchy and the inconvenience of that.</p>
<p>I also thought, that this being Rails and all, the files should be included automatically. It turned out to be pretty easy.<span id="more-480"></span></p>
<h2>The Implementation</h2>
<h3>Serving the Assets</h3>
<p>To serve this file, we need a route and a controller.</p>
<p>First the route:</p>
<pre class="brush: ruby; light: true; title: ; wrap-lines: false; notranslate">map.connect '/asset/:path',
    :controller =&amp;gt; 'asset', :action =&amp;gt; 'serve_asset', :path =&amp;gt; /.*.(js|css)/</pre>
<p>We need to be careful here, since we&#8217;re going to be serving files directly out of our view hierarchy&#8211; make sure the route only picks up the javascript and CSS files.</p>
<p>The corresponding controller:</p>
<pre class="brush: ruby; light: true; title: ; wrap-lines: false; notranslate">class AssetController &amp;lt; ApplicationController
  def serve_asset
    path = params[:path]
    format = path.sub(/.*.(w+$)/, '1')
    respond_to do |format|
      format.js { render :file=&amp;gt;&quot;#{RAILS_ROOT}/app/views/#{path}&quot;}
      format.css { render(:file=&amp;gt;&quot;#{RAILS_ROOT}/app/views/#{path}&quot;)}
    end
  end</pre>
<p>Now the assets mentioned above are served as /assets/index.css and /assets/index.js.</p>
<h3>Including the Assets</h3>
<p>With this convention, we added code to our layout that includes these files automatically, but only if they exist. First, the additional controller code:</p>
<pre class="brush: ruby; light: true; title: ; wrap-lines: false; notranslate">class AssetController &amp;lt; ApplicationController
  ...
  def self.include_asset(rel_path)
    return '' unless AssetController.asset_exists?(rel_path)
    case rel_path
      when /.js$/
        &quot;&lt;!--mce:0--&gt;&quot;
      when /.css$/
        &quot;
&quot;
    end
  end

  def self.asset_exists?(rel_path)
    FileTest.exists?(RAILS_ROOT + &quot;/app/views/#{rel_path}&quot;)
  end</pre>
<p>And the code needed in the (Erector) layout:</p>
<pre class="brush: ruby; light: true; title: ; wrap-lines: false; notranslate">head do
  c = self.class
  rawtext AssetController.include_asset(&quot;#{c.name.underscore}.js&quot;)
  rawtext AssetController.include_asset(&quot;#{c.name.underscore}.css&quot;)
end</pre>
<p>Actually, since it&#8217;s Erector, the views are Ruby classes, so there is a hierarchy. These assets may be specific to any one of these:</p>
<pre class="brush: ruby; light: true; title: ; wrap-lines: false; notranslate">c = self.class
while c != Object
  rawtext AssetController.include_asset(&quot;#{c.name.underscore}.js&quot;)
  rawtext AssetController.include_asset(&quot;#{c.name.underscore}.css&quot;)
  c = c.superclass
end</pre>
<h2>Discussion</h2>
<p>Although some people will bristle at the proliferation of files, I have used this technique repeatedly over the last five years (in Java and PHP projects). I find it leads to sound, modular development. With Ruby, it&#8217;ll be pretty straightforward to concat the assets together later on, to reduce requests, or even embed the code directly in the served HTML files, if it&#8217;s small enough. It&#8217;s also convenient to add page caching in the controller, when the time comes. Adding</p>
<pre class="brush: ruby; light: true; title: ; wrap-lines: false; notranslate">  caches_page(:serve_asset)</pre>
<p>copies all these files into public/assets (as they are accessed), which conveniently takes your mongrel (or whatever) out of the picture.</p>
<p>You might imagine this keeps you from organizing and structuring your CSS and Javascript; it doesn&#8217;t do that. It lowers the barier to using a &#8220;local&#8221; scope for the CSS and Javascript. It allows you to write page-specific CSS and Javascript, which if you&#8217;re developing code quickly, is a lifesaver. We have repeatedly opened a view-specific Javascript file, seen that it no longer applies to the code, and removed it&#8211; safely. It&#8217;s easy to edit scripts when you know they only effect one page. Without this, I tend to lump things together that don&#8217;t really belong, and it becomes hard to tease them apart later.</p>
<p>Thoughts?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2009/03/08/convenient-css-and-javascript-in-ruby-on-rails/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

