spacer Jamie van Dyke

Toward my dreams I persist,
determined, relentless.
Destroy them, you cannot.
For I shall continue,
I shall prevail.
The sands of time,
have expunged my writings.
So let us again set in motion
the teachings,
and share the bollocks.

Jamie van Dyke is proficient in Ruby (and Rails). He teaches, he codes and is working for boxedup.

Web Development with Source Code Management Basics (A Basic Guide)
inscribed on 30 Aug 2009
by Jamie van Dyke

Pre-Amble

I remember when I first started development. Even more fun was doing the same but over a remote ftp connection through Transmit, or just directly on the server. I got used to pretty GUI’s doing things for me and visually feedback for whether I’d got it right. Oh, how things have changed. My work flow was:

  • Make changes to my code
  • Flip to my browser and refresh
  • See what I’ve done has changed
  • Go back to my code and make more changes
  • Go back to my browser and refresh
  • See I broke stuff
  • Go back and try fix it
  • etc.

I want to take you through how a basic version of what I do now, and the benefits of what I do. I have confidence in my changes and a general feeling of happiness that I’d like to pass on to you. Whether or not i work in a team, I use distributed version managed code to do so. Any changes I make locally are pushed to a remote computer, why? What if my hard drive failed or I’m working on another machine for the day, you never know. Either way, I have access to my code, including all the changes I’ve made.

Why was my initial plan inefficient? Well, it was brittle and to test everything meant looking at all the pages sequentially. Making changes in one place might affect another and I had to double check it manually. Also, the changes I made weren’t reversible. I worked around that by saving multiple copies of my files or folders, keeping old version of sites hanging around so I could always revert back if I wanted. This just doesn’t scale.

What’s the alternative? Source Code Management. There are many systems around and I’ve certainly used a myriad of them, but I’ve settled on git which makes me warm and fuzzy inside. Git was developed initially by Linus Torvalds to manage the complicated process of building a Linux kernel in a distributed manner (multiple people, multiple places editing one set of files). Quick list of benefits we’re interested in:

  • Revert changes without saving different versions manually
  • Develop multiple version of your site concurrently
  • Log changes you’ve made without a separately managed log file

Enough talk, let’s just run through what I do. I use Ruby on Rails and Sinatra for web development, but this will work with most languages too, if not all.

Getting Setup

Create a new project in your tool of choice, I’ll create one called ‘scm_project’ with Ruby on Rails:

    $ rails scm_project
    create  
    create  app/controllers
    ...
    create  log/test.log

This is just a plain old Ruby on Rails project, but it could be anything for you, even just a folder with some php files. We need to initialise the folder with our source code manager ‘git’.

    $ cd scm_project
    scm_project $ git init
    Initialized empty Git repository in /a/demos/scm_project/.git/

So git has set up our folder as a version managed folder. We can now, for example, ask git for the status of this folder:

    scm_project $ git status
    # On branch master
    #
    # Initial commit
    #
    # Untracked files:
    #   (use "git add <file>..." to include in what will be committed)
    #
    #	README
    #	Rakefile
    #	app/
    #	config/
    #	doc/
    #	log/
    #	public/
    #	script/
    #	test/
    nothing added to commit but untracked files present (use "git add" to track)

Don’t worry, this is nothing scary. It’s just telling me that this folder has some unmanaged (‘untracked’) files. Source code managers need to told which files they’re managing. In this case we just need to tell git what to add and what to ignore. Some things shouldn’t be added, for example a database credentials file. If you have multiple people on a project and you all have your own local database server, then there’s no guarantee that the access details are the same, so by leaving it out (telling git to ignore it) it will never be added and everyone can have their own version without it affecting others.

Let’s set up our file ignore list. Create a file in the root of that folder called .gitignore and put in contents like this:

    log/*.log
    tmp/
    .DS_Store
    doc/api
    doc/app
    doc/plugins
    db/*.sqlite3
    config/database.yml

This is my base .gitignore file, you can add to this later as you go. The idea is that anything you don’t want version managed, you add to this file.

Next up, we need to tell the version management to add all files.

    scm_project $ 
    scm_project $ git add .
    scm_project $ git status
    # On branch master
    #
    # Initial commit
    #
    # Changes to be committed:
    #   (use "git rm --cached <file>..." to unstage)
    #
    #	new file:   .gitignore
    #	new file:   README
    ...
    #	new file:   test/performance/browsing_test.rb
    #	new file:   test/test_helper.rb
    #

So by running ‘git add .’ we’re telling git to add everything within this directory. ‘.’ means ‘this directory’ in linux talk. That’s easy enough. Adding files to git means you’re telling it what you want to store in the system, but you need to ‘commit’ them too. Committing the changes that you’ve racked up tells the version system to store them.

The pattern is generally to rack up related changes, and then commit them with a message that logs those changes. In the future you can look at those logs and see what changes were made, even over multiple files, because of the way you add them.

The changes I’ve added here are simply adding a bare project, so I’m going to commit them now with a relevant message.

    scm_project $ git commit -m 'initial bare rails project'
    [master (root-commit) a085392] initial bare rails project
     41 files changed, 8438 insertions(+), 0 deletions(-)
     create mode 100644 .gitignore
     create mode 100644 README
     ...
     create mode 100644 test/performance/browsing_test.rb
     create mode 100644 test/test_helper.rb

After running the command, notice that git has told me how many insertions and deletions it’s accomplished. These are line counts. Where are we now? We’ve added the files we want the version management system to track, we can add more later, and we’ve committed the first set of changes to those files. Let’s hope through a few more changes and see what happens.

Making Changes

I’m going to change my application’s README file.

    scm_project $ echo 'My New README' > README
    scm_project $ git status
    # On branch master
    # Changed but not updated:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #	modified:   README
    #
    no changes added to commit (use "git add" and/or "git commit -a")

Our status shows me that I’ve got changes in the README but I’ve not added them to a commit. So this highlights an important fact, adding files to git doesn’t mean ‘add the file’ it means ‘add the changes’. Let’s prove this fact by adding our changed README.

    scm_project $ git add README
    scm_project $ git status
    # On branch master
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #	modified:   README
    #
    scm_project $ git commit -m 'showing what modifying files that are tracked does'
    [master b9b57e5] showing what modifying files that are tracked does
     1 files changed, 1 insertions(+), 243 deletions(-)
     rewrite README (100%)

Our status changed from ‘Changed but not updated’ to ‘Changes to be committed’. Changes can be over multiple files and be added in groups, what’s important is that when you commit those changes they must be relevant to each other. Notice also that git has counted the new lines I’ve added as well as the ones I deleted. 1 files changed, 1 insertions(+), 243 deletions(-).

The pattern I generally follow is:

  • Make changes for a feature or bugfix
  • Ensure my tests are passing
  • Add the changes for that commit
  • Commit them as a group describing what I changed overall

Some people like to describe it on a per file basis, but for me it comes down to a feature or bugfix. ‘why did I change it’, not ‘what did I change’. This is an important fact to note because the version manager is already logging ‘what’ changed, so all that remains is ‘why’.

Looking at Changes

What is it logging? Let’s take a look:

  scm_project $ git log
  2009-08-30 21:15:10 fearoffish ttys001
  commit b9b57e53fa0bdcd29b14fb0ded6ab4355ef3cb92
  Author: Jamie van Dyke <fearoffish@fearofpro.local>
  Date:   Fri Aug 28 23:03:31 2009 +0100

      showing what modifying files that are tracked does

  commit a08539202e8bf908106e1736dabf588c3b367151
  Author: Jamie van Dyke <fearoffish@fearofpro.local>
  Date:   Fri Aug 28 21:58:47 2009 +0100

      initial bare rails project

What we see is who committed changes, when, and what they logged as the message. You also get a reference id to the commit, which is a 40 character SHA-1 value of the commit contents. When you reference a commit in the future you use this id, like others I mostly just use 6 characters as that’s more than likely the amount of characters you need to uniquely identify it. The beauty of the git log is that if you keep good logs of the changes you’ve made and you group your commits correctly, then you can revert blocks of changes as required.

Reverting Changes

Let’s revert the last commit and see how easy it is to go back to previous version of your app.

  scm_project $ git revert b9b57e
  2009-08-30 21:20:59 fearoffish ttys001
  Finished one revert.
  [master 47d2c28] Revert "showing what modifying files that are tracked does"
   1 files changed, 243 insertions(+), 1 deletions(-)
   rewrite README (100%)
  scm_project $ git log
  2009-08-30 21:33:04 fearoffish ttys001
  commit 47d2c28a1c90ed95a32c5e76fa6e669b89ca5f09
  Author: Jamie van Dyke <fearoffish@fearofpro.local>
  Date:   Sun Aug 30 21:33:03 2009 +0100

      Revert "showing what modifying files that are tracked does"

      This reverts commit b9b57e53fa0bdcd29b14fb0ded6ab4355ef3cb92.

  commit b9b57e53fa0bdcd29b14fb0ded6ab4355ef3cb92
  Author: Jamie van Dyke <fearoffish@fearofpro.local>
  Date:   Fri Aug 28 23:03:31 2009 +0100

      showing what modifying files that are tracked does

  commit a08539202e8bf908106e1736dabf588c3b367151
  Author: Jamie van Dyke <fearoffish@fearofpro.local>
  Date:   Fri Aug 28 21:58:47 2009 +0100

      initial bare rails project

When you run the git revert command, you give it the SHA-1 reference — or six characters, like I did — and git will remove any changes you made for that commit. Cool, huh? So you are keeping multiple versions of your application without keeping multiple files.

Okay, that’s enough for this episode. I’ll be creating more episodes on subjects like local and remote repositories, hosting your Rails applications and more, soon.

Tags: ruby, rails, git, guide

Twitter + World of Warcraft Announcements
inscribed on 03 May 2009
by Jamie van Dyke

It’s been a while since I posted anything, and I intend to fix that with smaller posts that describe things I’m doing. So let’s start off this new regime with a little post about a tiny script I wrote the other day for my WoW guild. We are now using a twitter account to post scheduled raids and other announcements, and I knocked together a little script that parses our guilds (Enigma on Hellfire) guildomatic site for raids we’ve rostered for later that day, and it posts them to twitter.

I needed to parse some html and for that I use the venerable HPricot gem, and to communicate with Twitter I use the Twitter gem. Here’s the code:

  require 'rubygems'
  require 'twitter'
  require 'hpricot'  
  require 'open-uri'  
  require 'parsedate'

  TWITTER_USERNAME = 'replaceme'
  TWITTER_PASSWORD = 'replaceme'
  # enigma-hellfire.guildomatic.com/ is ours
  GUILD_SITE = 'replaceme'

  def get_raids(doc)  
    message = "[RAID REMINDER] "
    curr_date = Time.new
    raids_today = false
    (doc/"tr.today").each do |post|
      raids_today = true
      (post/"td.clickable/a").each do |name|
        unless name.inner_html.include?( 'Roster' ) or name.inner_html.include?( 'roster' )
          message << name.inner_html 
          (post/"td.inviteAtTime").each do |start_time|
            message << " (" << start_time.inner_html << ") | "
          end
        end
      end
    end
    raids_today ? message[0..-3] : nil
  end  
  
  doc = Hpricot(open(GUILD_SITE))  

  httpauth = Twitter::HTTPAuth.new(TWITTER_USERNAME, TWITTER_PASSWORD)
  base = Twitter::Base.new(httpauth)
  message = get_raids(doc)

  if message
    base.update(message)
  else
    base.update("[RAID REMINDER] There are no raids today, take a break, slacker!")
  end

Guildomatic has the raids for today posted in a table cell with a class of ‘today’, which made this much easier. You get the idea from the code.

I’m also cheered up immensely on what 10 minutes of key bashing can output. I run this on my server (hosted by Linode) in a cron job at 12pm every day.

Tags: ruby, twitter, wow

Building a Gem Using BDD
inscribed on 09 Jan 2009
by Jamie van Dyke

I wrote this article for the first edition of “The Rubyist”, and having left it a good set of months, I’m posting it here for your perusal.

The internet is full of tutorials and blogs on Rails, but lacking in the Ruby Gem building department, and as part of my work for Engine Yard over the last year I’ve been building internal tools packaged as Ruby Gem’s. I’d like to share my method’s with you now, and I hope it helps you building your own Gem’s.

We’re going to build a Gem that translates from American to British, it’s going to be simple, but it will demonstrate how to think about what you’re making and how to go about it. We’ll be testing with RSpec, for more details on the syntax of RSpec, please see their site.

If you’d like to skip ahead and see the entire application, I’ve put it on my github account.

Skeleton Structure

I use a gem called Mr Bones to generate the structure for my Gem’s, you can get it yourself using the gem command. On windows, omit the sudo command.

  $ sudo gem install bones

I use bones to generate a skeleton structure for our gem. It creates Rake files that do lots of funky bits and pieces for us, as well as a good folder structure and a ready to roll ‘Britify’ module under the lib folder. Let’s create that now.

  $ bones create britify

Mr Bones has now built us a structure, and all you have to do is edit the files Mr Bones told you to. The next step is to start building up the specs for our Gem, so let’s get cracking.

Start the Build Cycle

There is a specs/britify_spec.rb file already in place, but I prefer to structure my spec folder so that it automatically works with the autotest application. We won’t be using it in this tutorial, but bear in mind that I usually have it running so that I don’t need to re-run my tests constantly. I suggest you look into it on the ZenTest web site.

If we think about how we’re going to deal with the translations a little bit, it makes sense that we’ll need a Translation class to do the actual work. Now you’ll see exactly how BDD really works, as we build our first spec. Go ahead and create the following file, I’ve annotated it with comments to describe each piece. You’ll need to duplicate the exact folder structure for any file I describe in a code block, like below, use the first comment which tells you where to create it.

  # spec/britify/translate/translate_spec.rb
  # $Id$
  
  # Require the spec helper relative to this file
  require File.join(File.dirname(__FILE__), %w[ .. .. spec_helper])
  
  # No need to type Britify:: before each call
  include Britify
  
  describe Translate do
    # All of our specs for Translate will go in here
    
  end

  # EOF

It’s pretty empty right now, but the basics are in place. In fact, if we run this spec it will fail which is exactly what we’d expect. Let’s get something in it first though.

A Translate class, in my opinion, would be instantiated without the need for any arguments. This seems like a good place to start for our specs. In between the describe block above, let’s elaborate on that thought.

  # spec/britify/translate/translate_spec.rb
  it "should be instantiated without any arguments" do
    lambda { Translate.new(  ) }.should_not raise_error
    lambda { Translate.new( "moo" ) }.should raise_error(ArgumentError)
  end

This seems reasonable enough. Running rake on the command line (to run our tests) tells me that I have an uninitialized constant Translate. Well of course I have that error, I haven’t created that class yet! I use this as my sanity check, now it’s time to make that spec pass.

  # lib/britify/translate/translate.rb
  module Britify
    class Translate
    end
  end

Run your specs and you’ll get a successful pass. Brilliant. Further into the rabbit hole we go! I would expect our class to have a method that accepts an American sentence or word and returns the British version, I’d probably call it translate, but that’s too big a leap for my liking, I’ll tone it down to go in little steps.

  # spec/britify/translate/translate_spec.rb
  it "should accept a string on a translate method, and return a string" do
    t = Translate.new
    t.translate("wonky").should be_instance_of(String)
  end

It seems like a strange test, because we’re merely saying that if I pass in a string that i should get one returned. We’re not saying that it should be a correct translation, but that’s the whole point. We want to go in small steps so the specs cover as much of our thinking as possible and basically describe our application logic. The pattern from here on is pretty similar, so apart from the explanation of my thought pattern I’ll whizz you through each file and change that we make as we go.

  # lib/britify/translate/translate.rb
  def translate( string )
    "wonky?"
  end

Yup, tests are passing again. Now, before I can implement a translation method I’ll be wanting somewhere to store my translations for looking up. I like the idea of YAML for this simple app, I’ll store it in data/translations.yml, and I’m thinking a simple hash of key value pairs like "American" => "British" will suffice for now.

It makes sense that we’ll want the Translate class to load this in straight away so they’re immediately available for translations.

  # spec/britify/translate/translate_spec.rb
  it "should load the translations in from the data file on instantiation" do
    IO.should_receive(:read).and_return(
      "--- \nshut your gob: shut your mouth\nsnog: make out"
    )
    t = Translate.new
    t.translations.should be_a_kind_of(Hash)
    t.translations.keys.size.should == 2
  end

Notice the should_receive method there? This basically overrides the real functionality and fakes a response. The usual reason for this is that we don’t want to test the functionality of others classes and libraries, only our own. However, the real reason here is that as our translations grow the test will fail. So I fake a response in this spec and I’ll test the functionality more in other tests to ensure as we grow everything stays sane.

We need to make two changes to our Translate class for this to pass, the first is we need to require the YAML library at the top of the Translate class (above everything).

  require 'yaml'

Secondly we need to implement the YAML loading functionality.

  # lib/britify/translate/translate.rb
  def initialize
    translations_file = File.join(File.dirname(__FILE__), %w[ .. .. .. data translations.yml ])
    @translations = YAML.load( IO.read(translations_file) )
  end

We also need to expose the instance variable as a reader.

  # lib/britify/translate/translate.rb
  
  attr_reader :translations

Okay, so far so good. Our specs are ready for our simple YAML store, and our tests are passing. How about a real translations file.

  # data/translations.yml
  --- 
  # American: British
  shut your mouth: shut your gob
  make out: snog
  not straight: wonky
  rubber boots: wellies

Now’s the time to start getting some real translation tests in place. I’ll adjust the spec for accepting a string to need a real translation.

  # spec/britify/translate/translate_spec.rb
  it "should accept a string on a translate method, and return a string" do
    t = Translate.new
    t.translate("not straight").should be_instance_of(String)
    t.translate("not straight").should == "wonky"
  end

I’ve also noticed I’m repeating myself instantiating a Translate class, let’s dry that up. Place the following immediately after the describe Translate do line in our spec.

  # spec/britify/translate/translate_spec.rb
  setup do
    @t = Translate.new
  end

Now change any reference of t.translate to @t.translate and get rid of the Translate.new calls too (except for the one you just added in the setup block).

Let’s get our specs passing again.

  # lib/britify/translate/translate.rb
  def translate( string )
    @translations[string.downcase]
  end

Summary

At this point we have a working (in a comic way) translation library, it needs a lot of work to make it stable though. Using your new found BDD cycle skills you could easily get in some more functionality and sanitize the input. Here’s some examples of what needs doing:

  1. Sanitize the input from non-alphabetic characters
  2. Create a command line interface for it
  3. Add more translations!

If you’d like to play around with the idea some more, a more rounded version (with the completed tasks above) is available on my github account:

Please do fork it and make changes, I’m open to patches. I hope you’ve enjoyed this view into my development mind and if you have any questions please email me.

Tags: ruby, rails, bdd, testing, rubygem

next ยป
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.