2:08 PM EDT Friday, August 15 2008
This week I took a brief hiatus from my normal job at BrowserMedia to spend a week on a 3-2-1 project with Hashrocket with fellow guest stars Hampton Catlin and Matthew Bass. It was a great experience for many reasons.
One of the key factors that made this 3-2-1 possible is that not only do we all use good tools, but we all use the same good tools. All of us use Macs, Textmate, Git, Rails, RSpec, Restful Authentication and Haml. This allowed us to hit the ground running. All the basic infrastructure needed for building a web app was in place, so were able to get going and focus on the core functionality of the app.
Pair programming, at least to the degree that it is done at Hashrocket, was a new experience for me. Every developer works in a pair programming setup, with a laptop hooked up to a 30-inch display and two keyboards and two mice. Having a large monitor, two keyboards and two mice may seem like a luxury, but it really helps you get into the flow of pair programming.
Another core philosophy at the heart of the Hashrocket methodology is test-driven development (TDD). TDD is something that most agile development shops strive to do, but sometimes fall short. I believe that two of the main reasons many teams struggle getting TDD right are tight deadlines and lack of developer expertise and discipline. All developers who try to do TDD have experienced this at some point. You've got a feature that needs to be cranked out ASAP, you're not exactly sure how test it, so you just dive into the code and pound it out. My experience at Hashrocket has taught me that that all too common scenario can be avoided by practicing pair programming and TDD together.
One pair programming technique that makes TDD easier is ping pong pair programming. When doing ping pong pair programming, when you sit down to build a feature, the first person in the pair writes the test. Next, the second person in the pair writes the code to make the test pass. Then the second person writes a test for the next feature, and the first person in the pair implements it. By repeating this process throughout day, you have several benefits. First of all, it avoids the scenario where one developer does most of the work and the other developer just zones out and gets distracted by something else, because you are constantly switching back and forth. Also, like two people who go on a diet together to help each other stick to it, one developer doesn't let the other developer get lazy and skimp out on the tests for a specific feature. That doesn't apply just to the test. As long as you have a pair of two experienced programmers, one of them it's going to let the other get away with writing a nasty piece of code. It's like a real-time code review.
My favorite aspect of the 3-2-1 experience was having the time to spend with other good software developers and just discuss the process of software development. TDD was a topic of conversation all week. Some believe in a strict adherence to TDD, "Test all the fucking time", if you will. Others believe TDD can be a warm blanket. It's easy to fall into a mentality that the tests pass, therefore the code works. Human testing can be a more effective process for discovering bugs in software, but human testing gets much less emphasis than TDD. I believe there is a lot of value in functional testing with tools like Selenium and I would like to see more emphasis on functional testing in the community. For my next project, I'm considering just specing the models and testing the rest of it with Selenium. If you are following the best practice of SCFM, you controllers should be mostly glue code anyway, so you get more out of testing from the browser via an automated, repeatable test than unit testing your controllers. It was a healthy debate and great to have a chance to be part of it.
I suggest all developers, whether you are an independent developer or a full-time employee for an organization, find an opportunity to participate in a 3-2-1. The amount of knowledge sharing that goes on is invaluable, exceeding what you get out of attending any conference. You spend the entire day pair programming with other top-notch developers and the evenings discussing all aspects of software development. This is the type of environment Evan Light envisions for DCamp. Developers working together, one-on-one, sharing knowledge and trading best practices.
Posted in
Technology |
Tags
Rails, Ruby |
3 Comments
1:54 PM EDT Wednesday, June 25 2008
Today I was working on a Rails site where users have profiles that anyone can view, but if you are viewing your own profile, there are links on the page to edit various parts of the profile. I want the links to show up if you are viewing your own profile, but not be there if you are viewing someone else's profile. To implement this is trivial, as any Rails developer would tell you. I simply added a helper method:
def link_to_if_owner(title,url,options={})
if current_user == @user
link_to title, url, options
end
end
And changed the occurrences of <%= link_to "Edit", edit_something_path %> to <%= link_to_if_owner "Edit", edit_something_path %>. This probably took 15 seconds, and could not be more clear and succinct. When I compare this to doing Java/J2EE development, this task would probably have involved a custom JSP tag and would have been a complicated solution with much ceremony.
This is what is referred to as the means of abstraction in SICP. They assert that the power of a programming language or framework should be measured by its means of abstraction. The means of abstraction is how you take a set of operations and build a smaller operation that you can build on top of. This is just one example of many that shows why Ruby/Rails is a more powerful combination than Java/J2EE.
Posted in
Technology |
Tags
Rails, Ruby, Java, J2EE |
0 Comments
10:15 AM EDT Tuesday, May 6 2008
One thing that all web developers do at some is view the HTML source of the page your application has generated. The reason why is because what your web application does is dynamically generate HTML and you want to see what HTML it generated. You can think of Ruby, as a metaprogramming language, as a language that dynamically generates code. If you are doing Rails, you see this all of the time with association methods like has_and_belongs_to_many. What has_and_belongs_to_many does is generate methods on your class. For example, if you have an Product class that has_and_belongs_to_many categories, it will add a category_ids= method to your class. This all happens at runtime, so you can't see the method, but if you use Ruby2Ruby, you can. sudo gem install ruby2ruby and then run script/console at the root of your Rails app. Then just run these simple commands:
>> require 'ruby2ruby'
>> puts Ruby2Ruby.translate(Article)
The output will be the ruby code that your class now has. Somewhere in that code you will see:
def category_ids=(new_value)
ids = (new_value or []).reject { |nid| nid.blank? }
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
end
Which is the category_ids= method that was generated by has_and_belongs_to_many.
Posted in
General |
Tags
Ruby2Ruby, Rails, Ruby |
0 Comments
6:30 PM EDT Wednesday, April 23 2008
The suggested way of checking if an ActiveRecord model has errors with RSpec is:
@something.errors.should be_empty
When that fails, you get a report that says:
expected empty? to return true, got false
That's not very helpful because it doesn't tell you what the errors are. If you do this:
@something.errors.full_messages.should == []
You get a more informative failure message:
expected: [],
got: ["Whatever can't be blank"] (using ==)
Posted in
Technology |
Tags
RSpec, Rails, Ruby |
3 Comments
12:11 PM EDT Monday, April 21 2008
I just stumbled upon a neat little Rails feature and figured I'd share with the world. I'm working on a Facebook application and I'm going through some of the examples in Developing Facebook Platform Applications with Rails. In chapter 4, one of the tasks is:
Let's add a boolean hit column to our attack model. I'll wait while you create that migration.
It doesn't tell you how to do that, which I like. This is a simple enough task the reader should be able to do in their own, no need to clutter up the book with the code.
So I set out to do that, but for whatever reason my mind is in a bit of a fog today. I don't what it is, I've been making an inordinate number of typos. So when my fingers went to type the command to generate the migration, I just instinctively did this:
script/generate migration add_hit_to_attack hit:boolean
I accidentally added the hit:boolean column specification as if this were a script/generate model Whatever command. But it turns out Rails is able to read my mind. I opened up the migration file that was generated and there was this:
class AddHitToAttack < ActiveRecord::Migration
def self.up
add_column :attacks, :hit, :boolean
end
def self.down
remove_column :attacks, :hit
end
end
The code to add the column was already there. I checked the docs and sure enough there is support for this naming convention:
You can name your migration in either of these formats to generate add/remove
column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable
Example:
`./script/generate migration AddSslFlag`
With 4 existing migrations, this creates the AddSslFlag migration in
db/migrate/005_add_ssl_flag.rb
`./script/generate migration AddTitleBodyToPost title:string body:text published:boolean`
This will create the AddTitleBodyToPost in db/migrate/005_add_title_body_to_post.rb with
this in the Up migration:
add_column :posts, :title, :string
add_column :posts, :body, :text
add_column :posts, :published, :boolean
And this in the Down migration:
remove_column :posts, :published
remove_column :posts, :body
remove_column :posts, :title
Posted in
Technology |
Tags
Rails, Migrations, Ruby |
0 Comments