Posts Tagged ‘known state’

Chores: A test driven website – Part 10

Monday, June 8th, 2009

Where was I?

By the time I finished up post nine and started working on post 10, I was completely lost. It was entirely my fault and it happens to the best of us, and me too. Coming back to the Chores app after time had passed, and looking around, I thought two things:

  • How did this get to be such a mess?
  • Where was I?

A number of factors had come into play that led to this point. The first of which was lack of discipline. As came across features I thought were interesting or necessary, I abandoned the test driven mantra of red, green, refactor. This ruined my focus, splintered my development efforts and generated a fair share of untested code.

Another issue I ran into is that over the time between my first posts and now, a number of new versions of libraries were released and Rails itself moved from version 2.2.2 to 2.3.2. Subtle variations in the APIs for these gems, generated a few errors and left me scratching my head more than once. I would like to say that change is good, it’s great that the Ruby community is so active in improving it’s products. However, I needed to be more proactive in managing the changes.

Time is another factor that worked it’s entropy in Chores. You hear people talking about the poor developer left to maintain sloppy code months or years after it’s release, and that they may be you. Well it was me, and I was feeling the hurt.

Not all is lost

Thus far, I’ve painted a pretty dire picture of the state of the code. I’ve probably left you thinking, “but this is Chores: A test driven website! How could this happen?” Well it does happen, but you are right. On the bright side, it is a test driven website. If it wasn’t I would probably be tempted to toss it out and start over. But, because it’s test driven and in source control, I’m fairly confident that I can get the codebase up to date and functional in a reasonable amount of time. This in addition to a few practices will have me back in business in no time

While these are good practices to follow in the future, what can I do to get this codebase back under control?

  • Git back to known state – Since I’ve done some unknown piecemeal development on chores I will use my post_9 tag.
  • Make sure existing tests pass – This is where our tests are worth their weight. We know what code is working, and what that code does.
  • Evaluate holes – Find the weak points in the tests and make a plan to fix them.

Git Happy

Note: You can find post_9 here, though if you happen to be following along, you would want to merge it into your own source.

My first step is to rewind to the tag I made for my last post. Right away I realise that I now have two source repositories, one on my shared host and one on github. One is really enough so part of the process will be to move to only github.

rubyyot@atlas:~/working/chores$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 4 commits.
#
nothing to commit (working directory clean)
rubyyot@atlas:~/working/chores$ git remote
github
origin
rubyyot@atlas:~/working/chores$ git remote rm origin
rubyyot@atlas:~/working/chores$ git remote
github
rubyyot@atlas:~/working/chores$ git remote add origin   git://github.com/rubyyot/chores.git

Above, I listed my remote repositories. I see that I have two, so I remove one and add another. A mistake I made here is that I set my remote to my public clone url. I should have, since I will be pushing to it, used my clone url, git@github.com:rubyyot/chores.git.

rubyyot@atlas:~/working/chores$ git status
rubyyot@atlas:~/working/chores$ git reset --hard post_9
HEAD is now at b0eecae added auto formatting to Identity.find_or_create_if_valid

Here I have done a hard (destructive) reset back to the tag post_9. I could have avoided doing such a dangerous procedure if I had been working on a branch.

rubyyot@atlas:~/working/chores$ git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       db/production.sqlite3
nothing added to commit but untracked files present (use "git add" to track)
rubyyot@atlas:~/working/chores$ vi .gitignore      #added db/*.sqlite3 to .gitignore
rubyyot@atlas:~/working/chores$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified:   .gitignore
#
no changes added to commit (use "git add" and/or "git commit -a")

Here I noticed that git sees my “production” sqlite3 database is not in sync with the index. I really don’t want or need to have a pseudo-production sqlite3 database in source control so I updated my .gitignore file to ignore it.

rubyyot@atlas:~/working/chores$ rake test
(in /home/rubyyot/working/chores)
/usr/bin/ruby1.8 -I"/home/rubyyot/working/chores/lib" -I"/home/rubyyot/working/chores/test" "/home/rubyyot/.gem/ruby/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb" "test/unit/identity_test.rb" "test/unit/chore_test.rb" "test/unit/child_test.rb"
Loaded suite /home/rubyyot/.gem/ruby/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
Started
.............................
Finished in 0.347031 seconds.
 
29 tests, 43 assertions, 0 failures, 0 errors
/usr/bin/ruby1.8 -I"/home/rubyyot/working/chores/lib" -I"/home/rubyyot/working/chores/test" "/home/rubyyot/.gem/ruby/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb" "test/functional/children_controller_test.rb" "test/functional/chores_controller_test.rb" "test/functional/home_controller_test.rb"
Loaded suite /home/rubyyot/.gem/ruby/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
Started
..........
Finished in 0.201701 seconds.
 
10 tests, 17 assertions, 0 failures, 0 errors
/usr/bin/ruby1.8 -I"/home/rubyyot/working/chores/lib" -I"/home/rubyyot/working/chores/test" "/home/rubyyot/.gem/ruby/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb"
rubyyot@atlas:~/working/chores$ rake features
(in /home/rubyyot/working/chores)
/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/lib:lib" "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/bin/cucumber" --format pretty features/define_children.feature features/define_chores.feature
Story:  Define child
As a parent
I want to define my child(ren)
So that I can assign them chores
 
  Scenario: Defining a child                                        # features/define_children.feature:6
    Given I am logged in as "test.example.com"                      # features/step_definitions/chores_steps.rb:9
    And I am on the homepage                                        # features/step_definitions/webrat_steps.rb:6
    When I follow "Add Child"                                       # features/step_definitions/webrat_steps.rb:18
    And I fill in "child[nickname]" with "Bobby"                    # features/step_definitions/webrat_steps.rb:22
    And I fill in "child[open_identifier]" with "bobby.example.com" # features/step_definitions/webrat_steps.rb:22
    And I press "Add"                                               # features/step_definitions/webrat_steps.rb:14
    Then I should see "Child added."                                # features/step_definitions/webrat_steps.rb:93
    And I should see "Bobby"                                        # features/step_definitions/webrat_steps.rb:93
 
  Scenario: Defining a second child                                 # features/define_children.feature:16
    Given I am logged in as "test.example.com"                      # features/step_definitions/chores_steps.rb:9
    And I am on the homepage                                        # features/step_definitions/webrat_steps.rb:6
    When I follow "Add Child"                                       # features/step_definitions/webrat_steps.rb:18
    And I fill in "child[nickname]" with "Bobby"                    # features/step_definitions/webrat_steps.rb:22
    And I fill in "child[open_identifier]" with "bobby.example.com" # features/step_definitions/webrat_steps.rb:22
    And I press "Add"                                               # features/step_definitions/webrat_steps.rb:14
    And I follow "Add another Child"                                # features/step_definitions/webrat_steps.rb:18
    And I fill in "child[nickname]" with "Suzy"                     # features/step_definitions/webrat_steps.rb:22
    And I fill in "child[open_identifier]" with "suzy.example.com"  # features/step_definitions/webrat_steps.rb:22
    And I press "Add"                                               # features/step_definitions/webrat_steps.rb:14
    Then I should see "Child added."                                # features/step_definitions/webrat_steps.rb:93
    And I should see "Bobby"                                        # features/step_definitions/webrat_steps.rb:93
    And I should see "Suzy"                                         # features/step_definitions/webrat_steps.rb:93
 
Story:  Define chores
As a parent
I want to define chores
So that I can assign them to my children
 
  Scenario: Creating a chore                      # features/define_chores.feature:6
    Given I am logged in as "test.example.com"    # features/step_definitions/chores_steps.rb:9
    And I am on the homepage                      # features/step_definitions/webrat_steps.rb:6
    When I follow "Add Chore"                     # features/step_definitions/webrat_steps.rb:18
    And I fill in the chore with "My first chore" # features/step_definitions/chores_steps.rb:5
    And I press "Add"                             # features/step_definitions/webrat_steps.rb:14
    Then I should see "Chore added."              # features/step_definitions/webrat_steps.rb:93
    And I should see "My first chore"             # features/step_definitions/webrat_steps.rb:93
 
3 scenarios (3 passed)
28 steps (28 passed)
0m0.266s

Now I’ve run my tests and features and am pleasantly surprised to see that they are all passing. I didn’t expect this, but it’s a pleasant surprise. Still there could be some hidden surprises lurking in the shadows.

rubyyot@atlas:~/working/chores$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified:   .gitignore
#       modified:   db/test.sqlite3
#
no changes added to commit (use "git add" and/or "git commit -a")
rubyyot@atlas:~/working/chores$ git rm -f db/test.sqlite3
rm 'db/test.sqlite3'
rubyyot@atlas:~/working/chores$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       deleted:    db/test.sqlite3
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified:   .gitignore
#
rubyyot@atlas:~/working/chores$ git add .gitignore
rubyyot@atlas:~/working/chores$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   .gitignore
#       deleted:    db/test.sqlite3
#

After running my tests my sqlite3 test db shows as modified. Since my modification to .gitignore should be excluding all sqlite3 databases, it must already be in the git index. So I removed it and added the updated .gitignore to the index. I’m all ready for a commit.

rubyyot@atlas:~/working/chores$ git commit -m "resetting to post_9"
Created commit 79d6f46: resetting to post_9
 2 files changed, 1 insertions(+), 0 deletions(-)
 delete mode 100644 db/test.sqlite3
rubyyot@atlas:~/working/chores$ git push github
git push github
Enter passphrase for key '/home/rubyyot/.ssh/id_rsa':
To git@github.com:rubyyot/chores.git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to 'git@github.com:rubyyot/chores.git'

I make my commit and try to push to github. The attempt is rejected. Looks like I need to merge into it.

rubyyot@atlas:~/working/chores$ git pull github master
Enter passphrase for key '/home/rubyyot/.ssh/id_rsa':
From git@github.com:rubyyot/chores
 * branch            master     -> FETCH_HEAD
Merge made by recursive.
 README |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)
rubyyot@atlas:~/working/chores$ git merge ac60baa287c7ee73851d1b9895c7e6c826abddac
Already up-to-date.

I pull from github which appears to auto-merge. My manual attempt to merge tells me that I’m up to date.

rubyyot@atlas:~/working/chores$ vi README   #made a small change to README
rubyyot@atlas:~/working/chores$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 3 commits.
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")
rubyyot@atlas:~/working/chores$ git add .
rubyyot@atlas:~/working/chores$ git commit -m "small change to try commit to github"
Created commit 9991b45: small change to try commit to github
 1 files changed, 1 insertions(+), 1 deletions(-)
rubyyot@atlas:~/working/chores$ git push github
Enter passphrase for key '/home/rubyyot/.ssh/id_rsa':
Counting objects: 15, done.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 955 bytes, done.
Total 9 (delta 5), reused 0 (delta 0)
To git@github.com:rubyyot/chores.git
   ac60baa..9991b45  master -> master
rubyyot@atlas:~/working/chores$ git status
# On branch master
nothing to commit (working directory clean)

I made a small change and attempted to commit to github. It worked, yay!

rubyyot@atlas:~/working/chores$ vi README  #another small change to README
rubyyot@atlas:~/working/chores$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#       modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")
rubyyot@atlas:~/working/chores$ git add .
rubyyot@atlas:~/working/chores$ git commit -m "another small commit to test new remote to github"
Created commit a5b8a72: another small commit to test new remote to github
 1 files changed, 1 insertions(+), 1 deletions(-)
rubyyot@atlas:~/working/chores$ git push origin
fatal: protocol error: expected sha/ref, got '
*********'
 
You can't push to git://github.com/user/repo.git
Use git@github.com:user/repo.git
 
*********'

You will recall that earlier I removed my remote to my shared host and replaced it with github. Here I tried to push another small commit to it and it failed. I used my public clone url rather than my private ssh protected one, as I mentioned earlier. Time to fix that.

rubyyot@atlas:~/working/chores$ git remote rm origin
rubyyot@atlas:~/working/chores$ git remote add origin git@github.com:rubyyot/chores.git
rubyyot@atlas:~/working/chores$ git status
# On branch master
nothing to commit (working directory clean)
rubyyot@atlas:~/working/chores$ git push origin
Enter passphrase for key '/home/rubyyot/.ssh/id_rsa':
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 330 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To git@github.com:rubyyot/chores.git
   9991b45..a5b8a72  master -> master
rubyyot@atlas:~/working/chores$ git gc
Counting objects: 352, done.
Compressing objects: 100% (244/244), done.
Writing objects: 100% (352/352), done.
Total 352 (delta 105), reused 275 (delta 71)
rubyyot@atlas:~/working/chores$

All fixed up and I’m back in sync. I am still new to git and am only familiar with the handful of commands I use on a daily basis. There was probably a better or easier way to do what I did. If so, feel free to comment. If you are new to git too here are a couple resources.

How do I?

Below I included a small experiment I did where I cloned the chores source to a new local repository, checked out the post_9 tag to a new branch, modified origin to point to another repository, and checked the branch into master. Hopefully it will give you something to work with if you want to do anything similar. Alternatively, you could fork my github source into your own github repo and have fun.

 
rubyyot@atlas:~/working$ git clone   git://github.com/rubyyot/chores.git chores-github
Initialized empty Git repository in /home/rubyyot/working/chores-github/.git/
remote: Counting objects: 352, done.
remote: Compressing objects: 100% (227/227), done.
remote: Total 352 (delta 105), reused 320 (delta 88)
Receiving objects: 100% (352/352), 139.50 KiB, done.
Resolving deltas: 100% (105/105), done.
rubyyot@atlas:~/working$ cd chores-github/
rubyyot@atlas:~/working/chores-github$ git branch post_9_branch post_9
rubyyot@atlas:~/working/chores-github$ git checkout post_9_branch
Switched to branch "post_9_branch"
rubyyot@atlas:~/working/chores-github$
rubyyot@atlas:~/working/chores-github$ git remote rm origin
rubyyot@atlas:~/working/chores-github$ git remote add origin ssh://USERNAME@example.com/path/to/repo
rubyyot@atlas:~/working/chores-github$ git pull origin master
USERNAME@example.coms password:
From ssh://USERNAME@example.com/path/to/repo
 * branch            master     -> FETCH_HEAD
Already up-to-date.
rubyyot@atlas:~/working/chores-github$ git status
# On branch post_9_branch
nothing to commit (working directory clean)
rubyyot@atlas:~/working/chores-github$ git checkout master
Switched to branch "master"
rubyyot@atlas:~/working/chores-github$ git merge post_9_branch
Already up-to-date.
rubyyot@atlas:~/working/chores-github$ git status
# On branch master
nothing to commit (working directory clean)
rubyyot@atlas:~/working/chores-github$

Conclusion

In all it seems that things were not as bad as I had originally thought. Still it reinforces the need for discipline in doing TDD and coding in general. In the next post I will look over the health of the app and (hopefully) take appropriate steps to correct any problems.