An Experiment in Remote Pairing
August 12th, 2008
Recently, my employer, Integrum Technologies, has allowed me to work from home one day a week. This is due to rising gasoline prices combined with my distance from work. It’s great for me as I’ll save quite a bit of money each week as well as reduce my overall emissions. However, this has presented us with a slight dilemma: how do we remote pair effectively?
Pair Programming
Integrum subscribes to a variety of Agile practices. Perhaps the most prominent is working in pairs, a.k.a pair programming. I was first introduced to the idea of pair programming around 1999 when I was at the University of Arizona learning how to completing my BS. At that time we simply learned the theory from a professor, but didn’t actually try it.
About 16 months ago I was working in a contract position with my friend and phoenix Ruby on Rails developer, Josh. Toward the end of our time at that contract we attempted pairing and found it worked really well. When we were hired as contractors at Integrum we paired on a project form start to finish and then later, when we went full-time, we paired with the rest of the team members. These positive experiences with pair programming have convinced me of it’s usefulness.
Now that I work a day from home, how will I pair with my various teammates?
Remote Pairing
I’m sure there are many, many ways to pair remotely, but here I will describe our first attempts at pairing remotely. It may help that we’re an all Mac shop, but we found it rather easy to get up and running quickly.
Systems
At home and at work we are using iMacs running OS X 10.5.4. The built in iSight and microphone are great for what we need right now.
Complaint: Noise. It’s everywhere, seriously. The mic picks up everything.

iChat
Yep, that’s right, iChat – video chat specifically. We simply fire up our iChat instances using our Integrum Jabber IDs and request a video session. Seriously, that’s all we do. The sound and video quality have been more than adequate for pair programming 7 hours a day.
Our first attempt at this was flawless, well… until it just stopped working that is. After a little research we found that I probably needed to open the right ports to maintain a consistent connection (this post on iChat Routing Problems helped). After I did that we haven’t had a problem.
Complaint: Video chat over iChat can sometimes lag or have artifacts, especially during peak times. It’s good enough, but would be nice if it were smoother and clearer.
Screen Sharing
While we’re pairing we share the screen on one of the computers. When we’re done we turn it off. This allows us to both have control of the keyboard/mouse if we need it and has helped immensely in making us feel like we’re both working. We take turns driving while the other gives advice or comments and we can smoothly transition from one driver to the other.
Complaint: From my end the connection latency can sometimes hinder progress. If I hit one of the Exposé areas of the screen and all the windows separate it can almost bring rendering on my side to a halt. There also seems to be a problem with select via the mouse. When I click and drag to select some text the screen jumps to the top or bottom immediately, selecting all text between. I’ve had to get better with the keyboard.
iSight + built-in microphone + iChat + Screen Sharing = Adequate Pairing
I do have some complaints about the current setup, but I’m willing to keep going until we find better solutions. Perhaps we should try using Xbox Live as I’ve heard some people suggest.
I think the next steps are to secure some good microphone headsets that will reduce the noise and allow us to hear one another more clearly. Then we can look at replacing iChat and Screen Sharing.
As tools for remote pairing progress I can see more developers turning to this as a solution to save money and time while still retaining their communication with their team, especially with their pair. Pairing remotely can reduce cost for the developer and the company, save time and reduce emissions because the developer no longer commutes. And if their whole company is remote then all the better!
Everything I Need to Know About Pair Programming Etiquette, I Learned From My 4 Year Old
August 5th, 2008
The folks at Integrum recently had a discussion about avoiding distractions while pair programming. We all know how many distractions there are in today’s world, everything from email to blogs to Twitter. It’s hard to not get distracted by all of these things, as they have become so prevalent in our lives. We want to be in the know and there are many tools out there that let us do just that. They constantly run in the background or popup, they email us, they text message us, they demand our attention…
Look at me, I’m a tweet!!
No, I’m working.
But I’m a direct message from iJustine!
Well… okay, just this once!
But it’s not once, it’s all the time… constantly interrupting (seriously Justine, stop, I’m not going to respond). When someone is paying you for your time, they don’t want to be competing with all these distractions.
So what’s appropriate etiquette for a person working in a pair programming environment? I asked my 4 year old over dinner to see what he thought.
The Interview
me. Do you know what it means to work in a pair?
ethan. When 2 people are working together.
me. Good. What about focus? Do you know what that means?
ethan. Stay on track. Concentrate. Keep an eye on what you’re doing.
me. How about respect, do you know what that means?
ethan. Be nice?
me. Okay good, let’s get started.
me. When you’re working with someone as a pair is it okay to check your email, instant messenger or twitter?
ethan. No.
me. Why is it not okay?
ethan. Cause we might lose focus.
me. If we were working together and I was having trouble solving a problem, how could you help me?
ethan. I could tell you how to do it.
me. What if you didn’t know how to do it?
ethan. We could think of an idea and do it.
me. Should we think by ourselves?
ethan. No we should talk and hear… and choose what we want to do.
me. If were working together and I get distracted, what should you do?
ethan. Daddy, what are you doing? Please pay attention. You made me distracted and I don’t know what I was working on.
Conclusion
This interview proves beyond a doubt that my 4 year old knows what it takes to work in a pair programming environment. He was polite, but forceful in his remonstration of my lack of focus. His answers were not overly complex, he kept it simple. Which is as it should be since it really isn’t all that complex – there’s a reason the acronym KISS exists.
So there you have it, think like a 4 year old and you’ll do just fine in any pair programming environment :)
TwitterBot Gem Released
June 11th, 2008
Tonight, at one of our (in)famous hack-a-mania nights at Integrum, we released a gem for accessing Twitter through XMPP. Read the full Integrum release article and then go get hacking!
I’m excited because not only did I have a hand in the creation of the twitter_bot gem, but I also helped in the development of Props & Drops, aka. TwitterProps, a Twitter application that uses this gem. So If you want to see it in action, give someone props (or drops) via twitter:
props @username for doing something cool
OR
drops @username for being a jerk
Go try it out!
DISCLAIMER: TwitterProps and twitter_bot only work when Twitter has their track feature enabled, which recently has been scarce…
RailsConf 2008
May 26th, 2008
I’ll be heading to RailsConf 2008 this week in beautiful Portland, OR. I can’t wait to be in one of my favorite cities again. Especially because I’ll not only be enjoying the city, but also learning from and interacting with the Rails community. If you’re going to be in Portland this weekend, then feel free to contact me.
This trip is brought to me by the awesomest rails development shop, Integrum! And if you see any of the other Integremlins there, say “Hi”, you never know what could happen. That’s what I did last year and now I get to work there building amazing Rails applications every day. Oh, and we have stickers.
GoRuCo 2008 Wrap-up
April 28th, 2008
As I stated in a previous post, I attended the Gotham Ruby Conference this past weekend in NYC. It was a great time with many interesting talks. The weather was amazing as well. I don’t want to spend a lot of time rehashing the conference because you can view the GoRuCo 2008 presentations for yourself, but I did want to mention a few things I got out of it.
Bryan Helmkamp gave an interesting introduction to story driven development using rspec and story runner. This is something we’ve been talking about at Integrum, but haven’t had a chance to use in a real setting yet. Derek and I got to spend some time at the WePlay offices (where Bryan works) and they showed us some of the stuff they’re doing, including using story runner. It was awesome to hear about their experiences and hopefully we’ll get a chance to use it soon.
Next up was “Archaeopteryx: A Ruby MIDI Generator.” I was skeptical about this one, but was blown away by Giles Bowkett’s presentation. From recounting his life adventures, including a “psycho throwing rocks at his dad until he threatened him with a piece of rebar” to calling venture capitalists “weasel-brained muppet fuckers… they’re not fools, they’re liars.” He kept the crowd well entertained, but threw in some really interesting and well-delivered points about startups and life. It is well worth watching.
Another one that really stood out was Ryan Davis’ presentation about “Hurting code for fun and profit.” He had a lot of really great things to say about being a developer, so go watch it already!
These speakers helped me to become aware of a few things:
- I don’t read enough. Ryan mentioned that the average developer reads 1 book a year. Is one book a month really so hard?
- I don’t hurt my code enough. I don’t use flog or heckle consistently and I often don’t take the time to correct code that I know should be improved.
I definitely have some things to work on… how about you?
Caching Locale-specific Dynamic JavaScript Files
April 23rd, 2008
I was recently inspired to create some cached, dynamic JavaScript files for a project I am working on after watching Ryan Bates Railscasts episodes 88 and 89.
The basic concept is to create a JavaScript controller that dynamically renders some JavaScript file(s). This allows you to take advantage of ERB in your JavaScript files. However, this can become slow as every request for that JavaScript file must be processed by Rails. In episode 89, Ryan showed how to cache the dynamic JavaScript file to improve performance. These episodes were great, except I had one problem… The dynamic portion of the JavaScript files I was working with were translated strings.
This meant that I couldn’t just cache the JavaScript file because the translation would need to be different based on the locale setting of the user. It turns out there’s a pretty easy solution to this. If you follow the lessons from the episodes then you will have a general caching mechanism for the dynamic JavaScript files. Assuming you are using a translation mechanism like Globalize then you have access to a locale for the current session. Simply use this locale when including the JavaScript file (e.g., dynamic_states):
Your layout file<%= javascript_include_tag "#{locale}/dynamic_states" -%>
This will try to include the dynamic_states.js file from the javascripts/locale directory where locale is something like en, es, zh, etc. The only other thing to do is define a custom route to handle this new pattern.
routes.rbmap.connect ':controller/:locale/:action.:format'
That’s it. You should now have locale-specific dynamic JavaScript files. For what I was working on, I also nested the locale directory within another directory (e.g., cache) just for ease of removal in case you need to wipe out the entire cache of dynamic JS.
Resources
GoRuCo 2008
April 15th, 2008
Next week I will be traveling to New York for the Gotham Ruby Conference courtesy of my favorite Ruby on Rails consulting company in Phoenix. I’ll be attending with Integrum’s resident Agile methodology expert. If you’re in NY or attending the conference, be sure to look us up – see my contact information.
Rails counter_cache problem
April 11th, 2008
I ran into a strange Ruby on Rails counter_cache problem today. Given the following example models:
class Poll < ActiveRecord::Base
has_many :poll_choices
has_many :poll_votes
end
class PollChoice < ActiveRecord::Base
belongs_to :poll
has_many :poll_votes
end
class PollVote < ActiveRecord::Base
belongs_to :poll, :counter_cache => :votes_count
belongs_to :poll_choice, :counter_cache => :votes_count
end
We want to ensure that the Poll maintains the total vote count. We also want the PollChoice to maintain the votes for that specific choice. In our controller we might be tempted to add a PollVote through either the Poll or PollChoice association with PollVote, but that’s where the problem appears.
It turns out that both of the following approaches will only update the votes_count for one or the other instance, but not both.
@poll.poll_votes.create(:poll_choice_id => @poll_choice.id)
OR
@poll_choice.poll_votes.create(:poll_id => @poll.id)
Instead, if we create the PollVote directly we will get the desired result of both the Poll and PollChoice having their votes_count updated appropriately.
PollVote.create(:poll_id => @poll.id, :poll_choice_id => @poll_choice.id)
Strange behavior or expected result?
Update: Another solution is to not assign using the ID, but instead assign using the object itself.
@poll.poll_votes.create(:poll_choice => @poll_choice)
OR
@poll_choice.poll_votes.create(:poll => @poll)
A quick test showed this worked as well. (Thanks Arya A)
Integrum Trip - SxSW day 1
March 8th, 2008
The first real day of the conference. I’m not gonna lie to you, the first day seemed pretty light. Registration was from 10:00am until 2:30pm with no panels, sessions, etc. scheduled during that time.
We got to the convention center in Austin at about 11:00am, registered with ease and proceeded to B. D. Riley’s Irish Pub for an Austin Refresh meetup. Not wanting to partake in the suds before noon, Gist and I headed for Starbucks for some early afternoon pick-me-up.
After the Refresh meeting we headed back to the convention center to prepare for the first panel of the day. Then a strange thing happened… While we were shooting the shit, Gist comes up with a great idea for a simple application for Twitter. We laugh about it, throw some ideas around and viola we have the beginnings of a new project. After taking a poll amongst the others, we decided to devote a triple to working on a first release – me, Jade, and Gist.
While Gist drew some sketches, Jade and I borrowed a laptop from Heidmo to begin development. Just a few short hours later we have our first version of an app we are calling “Props and Drops”. It is a way for people to give props (or drops) via Twitter. It’s pretty easy to get started, just visit http://props.integrumtech.com and click ‘About’ to get started. It also has a nifty iPhone interface, so if you’re lucky enough to have an iPhone check it out!
It was really awesome to see an idea progress so quickly. Here is a brief timeline of the day (totally estimated):
- 1:00pm – Mother jokes
- 1:07pm – Gist mentions way to vote people up or down via Twitter
- 1:08pm – Someone says props
- 1:12pm – More mother jokes
- 1:17pm – The idea turns serious and Jade, Gist and I volunteer to build it in the few hours before the 5:00pm panel
- 1:21pm – Another mom joke…
- 1:35pm – Gist sketches interface
- 1:30pm to 3:30pm – Jade controls laptop while I pair and we write the app in under 2 hours
- 3:30pm to 4:52pm – Deployment takes about another hour, including minor tweaks discovered on deployment
- 5:00pm – People begin to use ‘Props and Drops’ during the 5:00pm panel
- 5:45pm – Strebel gains a substantial props lead over all others
- 6:10pm – High fives are exchanged amongst Integremlins
- 6:11pm – Mother jokes resume
- 6:40pm to 2:00am – Alcohol is consumed
What a completely awesome day. Thanks to Jade and Gist for kicking ass to get this app out there and ready for use. Thanks to everyone already partaking of the Twitter Props and Drops goodness. Send us feature requests!!
Integrum trip - SxSW Day 0
March 6th, 2008
What a long day! We traveled by RV from Phoenix, AZ to Austin, TX in just about 19 hours. Not record time by any means, but we had a blast. Josh provided the RV and both he and Derek drove.
Highlights of the trip include stopping to discover the mysteries of ‘The Thing’, talking politics and religion with Jade and Erica, Curtis hacking on a new side project and me hacking on a side project (details soon).
It was really nice to spend some quality time with some of my co-workers outside of work and the pressures and demands of customers. Looking forward to participating in SxSW!
Integrum and me
February 9th, 2008
In August 2006, I left my employer to embark on a journey to change my skill set and mind set. My previous employer was a very large defense contractor who offered a decent workplace, average salary, excellent benefits, and, of course, job stability. I announced to my friends and family that I would be leaving this employer, returning to Arizona and leaving the ranks of the employed for an undetermined amount of time. It seemed ludicrous to most people at the time, myself included. I was sure of only one thing at that time: I intended to refocus my career goals in an entirely different direction.
Unemployment
I had attempted to take my career in a different direction while still working, but I found that after 60-80 hour work weeks, a Master’s program in Computer Science and a wife and two young children, I didn’t have the energy. Voluntary unemployment was a calculated risk that I could learn what I needed to enter my newly chosen career path before I was forced to declare bankruptcy.
Unemployment is not really as bad as I thought it would be. I was able to spend time learning the things I was truly interested in, including Ruby on Rails. I spent a lot of time with my family and generally enjoyed myself. I found that I could actually work longer with more focus that I ever did at a previous employer without feeling tired or burnt out.
Eventually, our finances drained to the point of alarm and I began to look for a professional position working with Ruby on Rails.
Contracting
Since early 2007, I have been contracting my Ruby on Rails services to various employers. While this was lucrative it was also very frustrating. During that time I learned a little about myself and the environment in which I work best. I invest myself in what I do and as a contractor I found it hard to not get involved beyond what was required of me.
My first contract position was a fixed bid contract that was referred to me by my brother. It allowed me to choose the technology and create a simple student tracking system for a wellness center. It was deployed internally in January of 2007 and as far as I know it is still being used. My first real, deployed and used application! With that experience under my belt, I placed my resume on various websites advertising my interest in Ruby on Rails development positions. In a few short days I was contacted.
My second contracting position was an 18 month contract to create an EMR system for a local, privately held behavioral health organization. This was the big test for me to gauge if my calculated risk would pay off. Unlike the previous contract I was interviewed about my knowledge of Ruby on Rails, databases and Agile development. Two weeks after advertising my resume, I was hired to work professionally with Ruby on Rails. I learned a lot during my time contracting for that company, including how much I didn’t know, and met a great Phoenix Ruby on Rails developer, Josh Huckabee. I also attended my first RailsConf in Portland, OR in May with Josh and a few of the full-time employees.
Attending RailsConf was a real turning point for me, in many ways. Part of what I was missing as a lone wolf developer was the sense of community and interaction with other Rubyists – I met some great people at RailsConf! Upon returning, I began attending the Ruby User’s Group in Phoenix, Phoenix Rails Group and Refresh Phoenix to connect at a local level with other enthusiastic Rubyists, developers and designers.
This was the beginning of the end of my time at my current contract. As I discovered more about agile Ruby on Rails projects I realized that it would be highly improbable to influence the current direction of my contract employer. For three more months Josh and I attempted to influence the direction of our project and rescue it from impending failure. In August, we made our exit and, because of RailsConf and local networking, we began contracting for a Phoenix Ruby on Rails consulting company, Integrum Technologies.
Integrum and Me
In January 2008, Josh and I became full-time Integrum code monkeys. Integrum is an amazing place to work and completely unlike any company I’ve worked for. There is a level of openness and transparency that I have only read and dreamed about. Of course, Integrum is not perfect, but we are working hard to constantly improve ourselves while still having a blast. My wife constantly reminds me that Integrum is fueled by fun :) I believe that this year will be an important one for our company. You can keep up on what’s going on with us by reading, or subscribing to, the Integrum blog.
A year and a half has passed since I resigned my comfortable, full-time position in California. In that time I have accomplished more that I could have hoped for and am happier than I’ve been in some time – my calculated risk paid off.
Minor TextMate Annoyance
November 13th, 2007
A recent update to Textmate added a variable to the do snippet. So when you type ‘do’ followed by a tab, you get
do |variable|
end
Most of the time I don’t need a variable, so I want the default ‘do’ snippet to not have a variable.
Open TextMate and use the keyboard shortcut to open the Snippet editor (Ctrl+Alt+⌘) Click Ruby in the Snippets area, click the plus sign (+) in the bottom left corner and click ‘New Snippet’.

Name the snippet ‘Insert do … end’, assign Activation (Tab Trigger) the value ‘do’ and Scope Selector the value ‘source.ruby’. The snippet text should be
do
$0
end

Finally, change the existing Snippet named ‘Insert do |variable| ... end’ Activation (Tab Trigger) the value ‘dov’. Now when you type ‘do[tab]’ you will get the ‘do … end’ block without a variable. When you type ‘dov[tab]’ you will get the ‘do |variable| ... end’ block.
The Tip of the Ruby #1
November 4th, 2007
I was recently asked about the difference between using or versus || in Ruby and thought it might make a good tip. They essentially do the same thing, but with one difference: operator precedence. You see || is evaluated before an assignment whereas or is evaluated after an assignment. This is why you can create conditional assignments like so:
user = User.find_by_id(1) || User.new
In the above code, if a user is found, then the variable is assigned that user. If a user is not found, then find_by_id returns nil (evaluates to false) and a new User is instantiated and assigned to the variable. If you were to use or in the code above, then when find_by_id returned nil the variable would be assigned nil as the assignment operator takes precedence over the or operator.
Example
Here’s a contrived example to show the difference between the two. Given the following class:
class Tester
def self.a
p "In Tester.a"
return true
end
def self.b
p "In Tester.b"
return false
end
end
Here are some expressions and their results:
>> x = Tester.a || Tester.b
"In Tester.a"
=> true
>> x
true
x = Tester.a or Tester.b
"In Tester.a"
=> true
>> x
=> true
So far they look identical… But, let’s change the order things are invoked.
>> x = Tester.b || Tester.a
"In Tester.b"
"In Tester.a"
=> true
>> x
=> true
>> x = Tester.b or Tester.a
"In Tester.b"
"In Tester.a"
=> true
>> x
=> false
Now we see the difference! When the invocation of Tester.b finishes, then the assignment operator is invoked, thus assigning false to the variable. Notice that Tester.a is still invoked, but it is ORed with the result of the assignment.
Lazy (Shortcircuit) Evaluation
I was also asked if the conversational operators are always evaluated. The answer is: No. The are evaluated just as the non-conversational operators. From Programming Ruby, 2nd Edition:Both and and && evaluate to true only if both operands are true. They evaluate the second operand only if the first is true (this is sometimes known as shortcircuit evaluation). The only difference in the two forms is precedence (and binds lower than &&).Similarly, both or and || evaluate to true if either operand is true. They evaluate their second operand only if the first is false. As with and, the only difference between or and || is their precedence.
Precedence of and and or
There is one other minor difference. The && operator takes precedence over the || operator so if you had an expression containing both, the && should evaluate first. With and and or they should be evaluated at an equal precedence, probably in the order they’re encountered.
Conclusion
I use the conversationalnot/and/or because I feel they make the code easier to read and force you to be explicit to account for the low operator precedence. I tend to use !/&&/|| sparingly (like using || for conditional assignment). Also, with compound expressions, I like to use parenthesis for clarity. Am I missing something? Do you know of a reason to prefer one or the other?
Update: Jay Fields has a post about this same topic. Marcel Molina, Jr. said in the comments that he prefers not to use either form as it is easy to introduce subtle errors.
Search Engine Optimization for Coders
September 15th, 2007
I attended Desert Code Camp this weekend. It is a free technology event in Phoenix, AZ with speakers presenting on a variety of topics. One of the interesting ones I attended today was called SEO for Coders and was presented by Jerry Ferguson. I don’t know much about SEO, just the basics and this was a good introduction into what I can do as a programmer to assist in optimizing a site for search engines.
Here are some highlights:
- Domain/sub-domain names – Pick a domain name that contains a keyword that you would like to target. If you don’t have control over your domain name, then it’s still possible to create subdomains with a descriptive keyword (e.g., finance.yahoo.com) Domain names should be keyword rich, short, and should be a dotcom.
- Relevant & useful content – Your site should contain relevant and useful content for what you’re trying to optimize for. Two measures of how well your site performs in this regard are keyword density and keyword prominence. See ranks.nl for a tool to help analyze for these measures.
- Page title – Every page should have a title. The title should be reflective of the content contained on that page. Don’t use a generic title for all pages. If you put the domain name in the page title, then it should be at the end to increase the prominence of the title itself (unless you are specifically targeting your domain name as a keyword).
- Page description – Every page should have a description. The description is taken from the description meta tag and is used in the snippet shown by search engines in search results. If the description contains the keywords that a person searched on, then it is shown, otherwise snippets from the page are shown containing the keywords.
- URLs – URLs should be keyword rich as well. Instead of URLs like foobar.com/articles/1 create URLs with keywords like foobar.com/articles/search-engine-optimization-for-coders. Additionally, URL parameters are generally not very useful, such as foobar.com?a=12345&b=woohoo&c=cookie.
- Redirects – Use 301 redirects to maintain a consistent, canonical representation of your domain name. Do not have both a www.foobar.com and a foobar.com. Choose one and go with it. The other should redirect to your choice. If you have both then they’re treated as separate sites and are both indexed by search engines. This will result in duplicate indexed content and search engines don’t like duplicate content. Duplicates are usually shoved into a supplemental index of search results. Additionally, URLs with and without trailing slashes are treated separately. So foobar.com and foobar.com/ are two separate sites in the eyes of a webcrawler. Pick one and go with it.
- Sitemaps – Create a sitemap and submit it to search engines. Presenter referenced GSiteCrawler but it looks like Windows only. Anyone know any free alternatives?
- Valid markup – Create standards compliant sites and everyone wins. If you’re using Firefox, then the Web Developer extension can help you with validation.
- Speed – Pay attention to the speed of your site as it affects not only your user’s experience, but may affect your indexing as well. Again, if you’re using Firefox then try ySlow to help with performance optimization.
Jerry also mentioned the Overture tool for keywords and Matt Cutts blog for how Google views spam.
There was also a neat Google trick he showed that I didn’t know about. If you search in Google using allintitle it will show you search results that contain those keyword in the page title. Very useful. Try it here to see all the websites out there who forgot to put page titles on their pages ;)
Update: Here’s a link to the presentation.
Update: Josh pointed out Stefan Vervoort’s article on Off-Site SEO. He also has an article on On-Site SEO. Go check them out!
Discover your missing specs
September 4th, 2007
Have you ever had the feeling that something was missing, but you weren’t quite sure what it was? I was looking at my code coverage the other day and thought to myself that it seemed like I was missing something. Rather than go through each file manually to see if it had an associated spec, I created a small rake task that does this for you.
The rake task looks at all the files in your app directory and tries to find an associated file in the spec directory. It follows the current Rspec generator naming conventions and if it doesn’t find the associated file it runs the generator to create it. It assumes that your app directory and spec directory follow the same directory structure and that you should at least have one test file for every application file.
You may find this as part of the Rake Tasks plugin over at Integrum.
To install:
script/plugin install http://svn.integrumtech.com/public/plugins/rake_tasks/
OR
piston import http://svn.integrumtech.com/public/plugins/rake_tasks/ vendro/plugins/rake_tasks
(Piston is one of my new favorite tools)
My additions are two rake tasks:
rake spec:check # Check files in the app directory for corresponding test files in the spec directory.
rake spec:sync # Check for missing test files in the spec directory and create them if they don't exist.
If you’re using Subversion, then you can also use the Subversion tasks that come with the Rake Tasks plugin to add the new spec files to your repository:
rake svn:add # Adds new files to subversion
rake svn:remove # Removes missing files from subversion
rake svn:ignore # Configures svn:ignore properties on log, tmp, db/schema.rb and config/database.yml
rake svn:conflicts # Resolves all svn conflicts by keeping the working file
This plugin was also the original home of some tasks that you may already be familiar with (I use them all the time):
rake db:create # Creates the databases defined in your config/database.yml (unless they already exist)
rake db:drop # Drops the database for your currenet RAILS_ENV as defined in config/database.yml
rake db:reset # Drops, creates and then migrates the database for your current RAILS_ENV
Those three were merged into Rails edge in May of this year.
Thanks to Josh Knowles, Derek Neighbors, and Josh Huckabee for their feedback on the addition. If you have any comments, bugs, patches, questions, etc. please send them my way.