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
- Configuring gems for Rails – Manage dependencies by documenting the gems you are using and requiring certain versions. You can also freeze a version of each to your app.
- Git branches for new features – create a new branch for a new feature to help stay focused.
- When inspiration strikes, annotate! – Rather than getting sidetracked by things outside of the scope of the current feature, leave a note and come back to it.
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.