Note: We only just registered this site, check back over the next couple of days for improvements

Cocoaheads.au

Oakley's Surf Report

I know HTML. Its on my CV. Expert level. I also know CSS and a whole bunch of JavaScript. I can even do TDD with JavaScript.

And on the iPhone there is this nifty object called UIWebView. Otherwise known as WebKit. Otherwise known as an embedded browser in your iPhone app.

And if you want a sexily awesome looking UI view, like the Today view of the Surf Report app (see right or free on AppStore) that was released on the AppStore recently, then the WebKit is just the best thing since the electric bread slicer for speed of development.

Holy grail of iPhone development?

Well, that’s what we thought. When I chatted with Dan Grigsby last week I mentioned there were good and bad things about the WebKit within an iPhone app.

This article is about good and bad things. The pros and cons. How we managed the integration of the two code-bases. And the answer to the big question: Would we do it again?

Its probably wonderfully useful stuff to know.

Negatives

So first the downsides. It would be remiss of me to fill you full of unbounded promises of easy non-Objective-C victories in your iPhone dev, and not tone them down with a full bucket of wet reality.

Its slow. When the WebKit is first loaded into memory, and we try to do this behind the scenes as soon as we get control of our app from the OS, it can take a good few seconds for your WebKit object to be available. You get notified of its readiness for action via the delegate:

- (void)webViewDidFinishLoad:(UIWebView *)webView

Its slow. You’re running an interpreter (JavaScript runtime) on top of a device with a small CPU and small memory. Go figure.

This reminds me of the fabulous “two minor drawbacks” scene from the 80s British sci-fi comedy Red Dwarf:

CAT: Why don't we drop the defensive shields?
KRYTEN: A superlative suggestion, sir, with just two minor flaws.
  One, we don't have any defensive shields, and
  two, we don't have any defensive shields. Now I realise that,
  technically speaking, that's only one flaw but I thought
  it was such a big one it was worth mentioning twice.

The JavaScript bridge does not appear to block the main thread. This is a good/bad thing. You can invoke JavaScript code within the WebKit via your native Objective-C code.

[webview stringByEvaluatingJavaScriptFromString:@"loadData({some: 'data'})"];

It seems to return control back to your Objective-C code before it has finished executing, so you may need to poll the JavaScript runtime with `isFinished();` calls if you need to know when its complete.

Annoyingly, the Apple documentation for this method suggests otherwise:

JavaScript execution time is limited to 5 seconds for each top-level entry point. If your script executes for more than 5 seconds, Safari stops executing the script. This is likely to occur at a random place in your code, so unintended consequences may result. This limit is imposed because JavaScript execution may cause the main thread to block, so when scripts are running, the user is not able to interact with the webpage.

Quirky.

The JavaScript bridge is one directional. From Objective-C/UIKit you can invoke JavaScript upon the WebKit and henceforth do wonderful things (as per example above).

From JavaScript you have no native nor nice way to invoke methods on Objective-C objects, like you can in the Cocoa implementation of embedded WebKits. What you can do is use custom HTTP protocols, such as surfreport://, to give the OS a way for the webkit to communicate with your app.

We used this for ‘Related Photos’ buttons on some news and athlete’s pages. When you click a button in the webkit, it attempts to redirect to a surfreport:// url. The Objective-C code (your `UIApplicationDelegate`) is notified of this, hides the webkit, and displays the related photos using native UI elements. But we could do anything. Your `UIApplicationDelegate` needs to implement the follow method to receive these requests:

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url

Alternately, you could use JavaScript to invoke this url, possible, via an Ajax call. Haven’t tried it. Might work. Either way, its a dirty hack, and a very annoying situation given that in Cocoa development there is a beautiful two-way bridge. I want that.

Multiple languages in one project. Whilst we mainly just wanted to take a static HTML file, and dynamically update various elements with application data (e.g. the surf and weather conditions in the example above), we needed to do that via JavaScript.

We came up with a consistent API loadData(json) for all pages, as discussed below; yet when something displayed incorrectly we now had an extra possible point of failure: JavaScript, Objective-C, and the original web service data source (e.g. the Surfline data for live updates).

To help isolate issues, we used JavaScript unit testing and a layer of “fixtures” or samples of JSON that might be sent from Objective-C to JavaScript. The tests passed against the fixtures; so if a QA error appeared in the app, we first checked the JSON being sent against the fixtures schema to isolate whether it was a JavaScript error or a data format error. It happened sufficiently often that its worth raising here.

WebKit for rapid prototyping

Nonetheless, the WebKit exists and it is awesome at rendering HTML and CSS, with access to the powerful CSS3 transformations and webkit-specific bonus features.

It is highly likely that your designer can make something beautiful looking in Photoshop and cut it up into HTML + CSS. Comparatively, its highly unlikely they can cut it up into native Objective-C code.

So in the initial phases of application development/prototyping, the WebKit is a sweet option to give the designer on your team direct, immediate access to building the app. If performance is an issue, you later replace it with native UI elements.

Code layout and samples

In our Xcode project, the structure we use for including HTML and associated assets into our apps is:

Classes/           - normal Objective-C .m/.h files
Html/src           - HTML, CSS, and JavaScript files including libraries like jQuery
Html/test          - JavaScript tests
Html/test/fixtures - expected JSON formats to be sent from Objective-C to loadData methods

html and assets

These aren’t Xcode Groups, rather normal filesystem folders. In the Xcode project, the Html/test files are not included nor bundled with the app. The files in Html/src are included in the “Copy Bundle Resources” of the Target (see image) and also linked into the project via a sub-group of “Resources”, so they can be easily accessed within the project.

But when I did the HTML/JavaScript development I worked from the command-line and TextMate, and lived entirely inside the Html/ folder.

For each static HTML page, such as the ‘Today’ view above, there will probably be the following files:

Html/src/today.html
Html/src/today.css
Html/src/today.js
Html/src/jquery.min.js
Html/test/unit/today_test.html

The WebKit will display today.html, which uses normal <link>/<script> to pull in the .css and .js assets. You can quickly see what the page will look like, even before integration into your app, by loading it into the iPhone Simulator (launch the simulator and drag the file in), or even Safari 3.

We settled on a standard API for the primary call from Objective-C into JavaScript: loadData(json). Bare-bones, this method starts off looking like:

// This function is called from Objective-C-land to apply
// the surf information to the HTML template
// +data+ is a hash of key -> value
function loadData(data) {
    $('.data').html('N/A');
    $('img.image_data').removeAttr('src');

    // Assume that any data key can be copied into any HTML element with the same ID
    for (var key in data) {
        var value = data[key];
        var value_exists = fieldExists(value);
        var data_value = value_exists ? value : 'N/A';
        var img_src_value = value_exists ? value : 'unknown';
        $('#' + key).
            filter('.data').html(data_value).end().
            filter('.image_data').attr('src', key + '_' + img_src_value + '.png');
    };
}

What it does is take a data hash like { surf_height_amount: '2-3', surf_height_unit: 'ft' } and HTML like:

<div id="surf_size">
  <span class="data" id="surf_size_amount"></span>
  <span class="data" id="surf_size_unit"></span>
</div>

And the loadData method updates it to:

<div id="surf_size">
  <span class="data" id="surf_size_amount">2-3</span>
  <span class="data" id="surf_size_unit">ft</span>
</div>

So we can build arbitrarily complex HTML templates. By allocating meaningful element IDs (surf_height_amount) and tagging the template elements with class="data" we have a simple mechanism for Objective-C to update the HTML. As the template gets more complex, then so too does the loadData method.

Would we do it again?

Yes.

The WebKit isn’t the holy grail for non-Objective-C developers, but if your grand-poobar level skills are in JavaScript and HTML, and your Objective-C/iPhone skills are still catching up, then its a wonderful prototyping platform. Especially for static, complicated displays of data. Especially if that data includes HTML content from an external feed which needs to be rendered.

For Oakley’s Surf Report app, Anthony is toying with replacing some of the WebKit usage with native UI elements (normal UITableView with custom UITableViewCells) so that we can get back those precious seconds and give them to the user as a Christmas present.

by Dr Nic at November 10, 2008 01:04 PM

Radio is where ugly people go. Podcasts is where ugly, niche people go. It doesn’t get much uglier and nicher than talking about iPhone dev; and the tale of one desperate man X on a desperate journey Y with his desperate sidekick Z, where:

  1. X = Dr Nic
  2. Y = iPhone development
  3. Z = Ruby

From the Mobile Orchard news desk:

drnic.png

Dr. Nic talks about his migration manager for updating SQLite iPhone databases, trying to get Ruby onto an iPhone, using Ruby to unit-test Objective-C iPhone code, his company Mocra, and the Mobile Orchard Podcast’s lavish recording studios.

Dan Grigsby is the interviewer and post-production guy for the interview, and so it is entirely his handy work that it seems like I’m the only one talking for 15 minutes. The original interview took 4 hours and it was mostly him talking. He’s quite a chatterbox. I barely got a word in edgewise. [1]

Dan also created this list of “things we talked about”:

  1. 0:00: Migrations for SQLite
  2. 5:18: Getting Ruby onto an iPhone
  3. 5:59: Nu - Lisp-based language that sits atop Obj-C and would work on the iPhone
  4. 7:37: Unit testing Obj-C from Ruby
  5. 8:12: Webkit UIs
  6. 12:31: Mocra / “Used my blog for evil”


Go to Mobile Orchard (or Mobile Awkward it is accidently called at the end of the interview) to cop a listen.

[1] This paragraph starts well and ends with blatant falsehoods.

by Dr Nic at November 03, 2008 11:51 PM

The New Gem Generator (newgem) was exciting, moderately revolutionary, and definitely helpful two years ago when I created it. Of late it seems to attract a chunk of criticism:

  • making a new gem, but newgem seems broken… hoe hoe
  • NewGem has the “hoe” virus. Much prefer Mr. Bones.
  • the newgem site is begging for someone to put the word fuck on its front page
  • Almost two days fighting with newgem, but today i won! The secret is hoe 1.7.0 and rubygem 1.2.0.
  • unfortunately for the one project I went with newgem, will give Mr. Bones a try on the next gem I throw out there.
  • sow or newgem, neither, Mr. Bones
  • egads, the website newgem sets up for you looks awful
  • newgem seems to be failed to generate package if AUTHOR is an array of authors.

On the positive side of the spectrum is the following list:

  • newgem? really? you rock drnic!

Comparatively, the two lists are awfully different in length. And not in a good way. No, not at all.

I know about these criticisms and platitude(s) because they appear publicly on newgem’s website within a live Twitter search of ‘newgem’. So that the twitter messages on the project’s own homepage are more positive, it was time for a new direction. A new beginning. It was time for a change.

So I fixed it. All of it. As of release 1.0.3 it is perfect [1].

To help you realise how wonderful newgem now is, I shall use the time-proven medium for proving awesomeness: a list. The bullet points are for free.

  • newgem now finally gone 1.0.0. It made it all the way to 0.29.0, but I think 1.0.0 was needed to transfer the message of a new beginning. This is the Obama of Gem Generators.
  • Generated gems are 50% smaller. No more config/hoe.rb. No more website folder (by default). No more tasks folder. No more license file. No more version.rb file.
  • Very little config required before releasing your gem. Just a few fields in the Rakefile.
  • You can use rspec or test/unit for unit testing (option: -T rspec)
  • You can use cucumber for functional testing (option: -i cucumber or run script/generate install_cucumber)
  • Generated gems are future-proofed. They will automatically benefit from future newgem releases.
  • Executable apps within gems now have a lib/appname/cli.rb file for the code base, and a lightweight bin/appname (option: -b appname or run script/generate executable appname)
  • Your README file can be called README.rdoc so it appears nicely formatted on github. No more hoe warning messages about “README.txt is missing” (see feature)
  • GitHub RubyGem support. rake gemspec generates a clean my_project.gemspec file that will work with GitHub
  • RubyForge support. As before, rake release VERSION=X.Y.Z releases your project to RubyForge (see ‘preparing for releases to rubyforge’ help page)
  • newgem’s website is a different colour. Its a nice peppermint green colour. The default website template is now this theme too.
  • You can raise bugs or suggest improvements via Lighthouse tracker

Installation

sudo gem install newgem

Usage

To create your RubyGem scaffold:

newgem mygem
newgem mygem -b myapp              # create a CLI executable
newgem mygem -T rspec -i cucumber  # use rspec and cucumber for gem tests
newgem mygem -w                    # create a simple website
newgem -h                          # get help

Now your code goes in lib folder, and your tests go in test, spec, and/or features as appropriate.

There are a bunch of rails-esque generators (like model or migration) that you can use to help your development:

script/generate executable myapp                       # create your own command-line interface (CLI)
script/generate extconf mylib                          # starting point for C-extensions, plus TDD framework
script/generate component_generator mygenerator scope  # create your own generators for Rails, Merb, RubyGems
script/generate application_generator myapp            # create a CLI that is a generator for something
script/generate -h                                     # get help

Bugs and suggestions

You can raise bugs or suggest improvements via Lighthouse tracker

Thanks goes to… Cucumber

Aside from several days of my time refactoring it, reducing it, and doubling the amount of awesomeness within it, all its wondefulness is thanks to Cucumber.

Cucumber is the successor to Rspec Story Runner. I never found time to play with Story Runner, but Cucumber is blowing my mind with awesomeness. My attention-span is short so I may be forgetting something but I think Cucumber could be the most important piece of software released in 2008 for Ruby-based developers.

Cucumber gave me a framework to specify newgem’s expected behaviour; its features. First I wrote feature descriptions for known, expected behaviour. Then I refactored the crap out of newgem until it was in tip-top shape.

There are over 90 feature steps defining newgem’s current features. To run them:

gem unpack newgem
cd newgem-*
sudo gem install cucumber
cucumber features

And watch the awesomeness of Cucumber unfold before your eyes. What you are seeing isn’t just newgem’s generators being executed, but also the generated code is being executed, rake tasks executed, and generated test files tested.

I can now setup continuous integration for newgem. I have a framework to know that newgem, or any other RubyGem, is doing what it should do.

UPDATE: I want to thank David Chelimsky for our time hanging out in Brazil during RailsSummit. I saw him using Cucumber, and talking about it on stage and then help helped me whilst I integrated it into newgem as a generator and then using it internally itself. For a day and a half we hung out in the hotel foyer. He’s so wonderful.

Cucumber makes me so happy.

Summary

Use newgem. Write gems.

Use mrbones. Write gems.

Use sow. Write gems.

Use gemhub. Write gems.

And write cucumber feature descriptions first. Then unit tests. Then code. Then release. Then profit.

[1] All claims of perfection are for the express purpose of making you try the product enough to use it, share it with your friends, and wrap it up and give it as a gift to family on Xmas day. Gift boxes are available upon request.

by Dr Nic at October 30, 2008 04:51 PM

I’ve dreamed of the ability to ask TextMate “what frigging bundle command/snippet is being activated by Ctrl+P or Shift+Ctrl+G?” I’ve silently pined for it. (Answer: params[:id] in Rails, and all the Git bundle commands, respectively).

Really I’m an idiot because the correct thing to do is to ask on ##textmate, “is there a way to …?” but because I figured I knew everything about TextMate I just assumed you couldn’t search for bundle commands by their key combo.

I was pairing with chendo and he had obviously stopped listening to me monologuing about how to do TDD with Shoulda and was randomly clicking things on TextMate.

He found the following:

Find bundle items by key binding

Find bundle items by key binding

Gold.

Updated image

Comments suggest that “what to do” isn’t clear above. My bad.

To get this working, there are 3 steps:

  1. Press Ctrl+Cmd+To to bring up the “Select Bundle Item” box. This feature of Textmate is awesome. You can enter the text of a bundle item and it will try to find what you are looking for.
  2. Click the magnifying glass
  3. Select ‘Key equivalent’

And here’s a picture:

by Dr Nic at October 08, 2008 11:54 PM

Sake set

I’ve used sake intermittently in my workflow. It competes against me writing helper/admin scripts in my ~/ruby/bin folder. Normally, executable Ruby scripts have won. But I think I have a new solution that could make sake a permanent winner for me.

Ruby scripts are easy to create and execute. You just open new file, change the TextMate grammar to ‘Ruby’, type ‘rb’ and press TAB and you’re off and running (the ‘rb’ snippet generates #!/usr/bin/env ruby or a variation of that). You then make the file executable and BAM! you can run the script from any folder in your environment.

Sake tasks are more annoying to write. After creating a new file, you need to create the namespace and task wrappers for your functionality, such as:

namespace 'foo' do
  namespace 'bar' do
    desc "This task ..."
    task :baz do

    end
  end
end

Your task isn’t instantly executable either. After each change, you need to uninstall the task (sake -u foo:bar:baz) and then reinstall the sake file (sake -i foo/bar/baz.sake) and then run it (sake foo:bar:baz). Perhaps there’s a way to inline edit a sake task, but I can’t see it from the help options.

But once you’ve got your script installed in sake, you get all the wonders that sake provides: a named list (with summary) of tasks (sake -T) and the ability to run those tasks anywhere. Ok, that’s really only one advantage over standard Ruby scripts. But I like it. Oh, namespacing. The baz task exists in a namespace foo:bar. That’s nice too.

So to make me happy, I need a solution to the dubious “create-install-execute” process above. I also want the raw source for all my sake tasks in one place so I can fix/add/change them, reinstall them and move on with my life. I want simple.

So I’ve forked Chris Wanstrath’s empty sake-tasks repo (mine) and added some infrastructure for managing sake tasks. Of course the repo itself is the repository for my sake tasks (which includes a lot from Luke Melia), but most importantly it has a single rake task to reinstall all the tasks without any manual fuss.

The rest of this article assumes you want to have your own repository for your own sake tasks hosted on github. This paragraph is probably unnecessary, but I don’t want to be accused of not being mildly thorough.

Fork the sake-tasks repo

For thoroughness and a chance to demonstrate some gold-medal git-fu, I’ll show two ways: fork my repo and forking the original repo from Chris and pulling my stuff into yours. It’s git, it’s distributed, you can do anything.

If you want to fork my repo and skip a nifty git lesson, go to my sake-tasks repo and click “fork”. Then follow the clone instructions as you normally do when you are blatantly, systematically duplicating someone else’s hard work, using a command that will look something like:

git clone git@github.com:your-github-username/sake-tasks.git

Now, lazy man, you can skip to the next step.

If you want to flex your git-fu, then go and fork Chris’ repo instead. Again, follow the clone instructions.

empty repo from defunkt

Now take a moment to reflect on just how empty your repository is. A fine moment in open-source where you’ve essentially cloned an empty repository. Hardly worth the effort, but since Chris is a creator of github then if he creates an empty repository then who am I to disagree. Empty it shall start.

Now let’s pull in the code and tasks from my repo. My repo could be any git repo anywhere on the tubes.

One way you could pull my code into your local repository is to add my repo as a remote and then pull in the goodness:

git remote add drnic git://github.com/drnic/sake-tasks.git
git pull drnic master

This is useful if you ever plan on re-pulling from a target repo again in the future.

If you just want to pull from someone’s repo one time only, then you can merge these two lines together:

git pull git://github.com/drnic/sake-tasks.git master

If you get occasional pull requests for your projects, then the latter option is handy to know.

Your local repo is now different to your remote repo (your fork on github) so push it back to your remote:

git push origin master

Installing the sake tasks

I originally created my sake-tasks fork so I could store a git:manpages:install task. I’ve just upgraded to git 1.6 (note to self: I want an ‘upgrade to latest git version via src’ task; UPDATE the repository now includes a git:src:install task to do this) and found some instructions for installing the pre-built manpages. Then I got over excited and refactored all of Luke Melia’s git+mysql+ssh tasks in to my repo so it looked like I’d done a lot of work.

To install all the tasks, first install sake:

sudo gem install sake

Then run the install task (check below for the list of tasks to be installed):

WARNING: This will uninstall any tasks you already have by the same name.

rake install

Now, check that your sake tasks are installed:

sake -T

Gives you:

sake git:analyze:commits:flog_frequent   # Flog the most commonly revised files in the git history
sake git:close                           # Delete the current branch and switch back to master
sake git:manpages:install                # Install man pages for current git version
sake git:open                            # Create a new branch off master
sake git:pull                            # Pull new commits from the repository
sake git:push                            # Push all changes to the repository
sake git:status                          # Show the current status of the checkout
sake git:topic                           # Create a new topic branch
sake git:update                          # Pull new commits from the repository
sake mysql:dump                          # Dump the database to FILE (depends on mysql:params)
sake mysql:load                          # Load the database from FILE (depends on mysql:params)
sake ssh:install_public_key              # Install your public key on a remote server.

Sexy.

Adding new recipes/tasks

The installer rake task rake install works by assuming that each .sake file contains one sake task. This allows the rake task to uninstall the tasks from sake first, and then re-install it (sake barfs if you attempt to reinstall an existing task). Without the one-task-per-file rule, the solution would be to load all the sake tasks as rake tasks into memory. But I like one-task-per-file; it seems clean.

So, to create a task foo:bar:baz, you’ll need to add a folder foo/bar and create a file baz.sake inside it. Within that file you would then specify your task using namespace and task method calls:

namespace 'foo' do
  namespace 'bar' do
    desc "This task ..."
    task :baz do

    end
  end
end

To install new tasks or reinstall modified tasks, just run the rake task (rake install or rake).

TextMate users

The latest Ruby.tmbundle on github includes a task command that generates the above namespace/task snippet based on the path + file name. That is, inside the foo/bar/baz.sake file, make sure your grammar is ‘Ruby’ or ‘Ruby on Rails’ and then type “task” and press TAB. The above snippet will be generated ready for you to specify your task.

Summary

So now I have a single place for all my original sake source and a simple rake task to re-install the tasks if I add or modify them. And because its all in one git repo, if other people fork it and add their own tasks then I can steal them.

by Dr Nic at August 19, 2008 04:02 AM

For the last few months I’ve been contracting to three different projects, and then recently two of them finished, with the third being an on-going project for the last two years which will be launched in a months’ time.

So I’m excited to start looking for a new consulting project to work on.

Travel or relocation is an option. Full-time salary is probably not an option. Either full-time or several days-per-week is an option. Making me wear a uniform and serve hamburgers through a small window is not an option. Working in an office is an option. Working on an oil rig in the Indian Ocean is not an option.

My professional background is in telecom billing systems and third-party integrations, including a roving two year stint in various countries between 2005 and 2007 (India, Sweden, Netherlands and Germany). One part of me is excited by the idea of moving overseas again. The other part of me includes two small children and a ten-hour plane flight to get anywhere.

I do have a PhD from the University of Queensland, though the only proof of it I can find on the InterTubes is on my supervisor’s 1990’s-themed website.

In recent years I’ve worked on several Rails projects, including developing and coaching the founders of imindi.com, a next-generation service for individual and collaborative thinking. I’ve run a bunch of Introduction to Rails workshops. I’m with a small group of Australians who recently started soliciting to develop iPhone SDK + Rails applications (under the brand Mocra; also, see Garath Townsend’s free app I Am Here which is in the top 50 free apps at the moment).

I’ve written dozens and dozens of blog articles (see an amusing graph); authored or hijacked or nudged along dozens and dozens of Ruby projects, JavaScript thingies, and TextMate bundles (see my github profile). I’ve even been allowed to talk at various conferences (my favourite talk was the RubiGen session at RubyConf 2007 [video]), including the exciting Rails Summit Latin American in Brazil later this year.

Open Source is for free and Consulting is for money. In both cases I like to work hard, write lots of code, and accumulate and share knowledge.

It would be wonderful if you can share this post with anyone who would benefit from having me on their team.

I can be contacted at drnicwilliams@gmail.com or on +61 412 002 126 in GMT+10 time zone.

by Dr Nic at August 04, 2008 05:41 AM

rbiphonetest logo

Everything to love about Ruby: the concise, powerful language; the sexy testing frameworks; and finally, the people.

Everything to love about Objective-C: hmmm; well…; and finally, its the only high-level language you can use to write iPhone apps.

On iPhone 2.0, to arrive on the 11th of July, you cannot run RubyCocoa. But you can run it on your Mac, so let’s use it to unit test your Objective-C classes. This tutorial shows you how to get started using a new project rbiphonetest [GitHub | Lighthouse | Google Group]

If you followed some of my recent tweets, this project was previously called “iphoneruby”. And alas, the screencast also calls it “iphoneruby” but that was a crap name. People thought it was a way to run Ruby on the iphone. I can’t do that yet. So, a far better name is ‘rbiphonetest’. [track on summize]

Even if you’ve never touched Objective-C, Cocoa, the iPhone SDK, nor RubyCocoa I recommend watching the video anyway. It should give you hope that if you make the transition to iPhone development you don’t have to go alone without Ruby: your trusty swiss army knife of language/libraries/tools.

The screencast is also available in high-def video (55Mb QuickTime)


Unit Testing iPhone apps using Ruby from Dr Nic on Vimeo.

Installation and Usage

To summarize the video, but change ‘iphoneruby’ to ‘rbiphonetest’, you install the framework via RubyGems:

sudo gem install rbiphonetest

Then change to your project’s folder and install the test framework:

rbiphonetest .

Finally, for each generic, non-UIKit-framework-using class you want to test:

script/generate model WidgetModel

Then write your tests in test/test_widget_model.rb

Supported Cocoa & iPhone frameworks

The mysterious, magical premise upon which rbiphonetest depends is possibly erroneous: that your Objective-C class can be compiled and tested against your OS X/Intel frameworks, and if your tests pass you assume you can then compile and include your class with the the iPhone/ARM frameworks.

I’m willing to go with this assumption until its proven dangerously flawed by some angry 20-year veteran of NextStep/Cocoa/iPhone. But really, how different could NSString be on the iPhone versus your Mac?

Fortunately there is one way to check for significant differences between your available Mac-based frameworks, such as Cocoa, and the iPhone-based frameworks, such as UIKit. We need to compare the framework names, header files and method signatures.

So for example, you cannot currently unit test any class that depends on/includes the UIKit framework. Why? It doesn’t exist on your Mac, so the Mac/Intel compiler cannot link it in. We’re compiling and running our tests with RubyCocoa, which itself is built against the Mac/Intel frameworks, not the iPhone frameworks. Hell, Laurent doesn’t even own an iPhone :) [Laurent is the Apple-employee maintainer of RubyCocoa and the newer MacRuby]

Similarly, its no use including/linking the Cocoa framework into your Objective-C class. Why? It doesn’t exist on the iPhone. It has its own UI frameworks, collectively called ‘UIKit’.

So for the moment we cannot test UI-related, iPhone-API-specific code. But we can test generic Objective-C. That’s better than a kick in the teeth. Surely. I mean, in the teeth… that’d friggin’ hurt.

“Fair enough Dr Nic, so which frameworks can my code use and yet still unit test it with your oh-so-special test library thingy?” Keep your pants on, I’m getting there. [ref]

To the best of my ability, I’ve compared the two sets of frameworks and listed the available Frameworks that are available on both the iPhone and your Mac. There are about a dozen. The most important is called ‘Foundation’. It holds gold nuggets like ‘NSString’.

The list of platform differences is on the wiki as a reference.

Note, this list doesn’t guarantee that any two framework classes - the iPhone and matching Mac framework - will behave the same. This list is compiled by finding the set of Frameworks with the same name on both platforms, e.g. Foundation.

Then it compares the set of public header files (Foundation.framework/Headers/*.h files) This comparison is by method signature. It pulls all lines from each header that start with + or - (+ is a class method and - is an instance method in Objective-C) and compares the two lists. If there is a single difference in the method signatures of the header files in the two platforms it is marked on the wiki page. You’ll need to look at the two header files yourself to see the differences. Some header files are ugly. C-based anything starts ugly and goes down from there, I think.

Python testing of iPhone Objective-C?

In the Python world there is PyObjC, a bridge-based twin to RubyCocoa. If you are a Python developer you could easily port this project to use PyObjC I would think. Ping me if you are attempting this and need any help.

Summary

I think this project can give Ruby developers a happy place to work from as they write their Objective-C/iPhone code. You still need to wire up your UI views and controller classes manually, but if you push all the “oooh that code really needs some tests” classes away from the UI-dependent frameworks then you can hook it up to rbiphonetest and write your tests in Ruby.

Currently the generator creates test/unit test stubs. I personally then add the Shoulda gem into my test_helper.rb for my apps. If an rspec and/or test/spec developer can help with adding support to the generators I’m certain the large rspec user-base would be happy campers.

Similarly, someone might like to investigate using MacRuby to run the tests instead of RubyCocoa. Fast tests vs slow tests. You choose.

What the?

Sometimes I re-read what I’ve written and notice things that don’t seem to make sense, but are in my vocabulary nonetheless. Yep, the things you learn living in Australia.

“Keep your pants on” - this seems to imply that until I mentioned otherwise you were about to take your pants off. Hardly relevant at any stage during this article, we’d both agree. Most code-based blog articles are “pants on”. This phrase means “don’t get upset”. You can try to figure out how you go from “don’t get upset” to “keep your pants on”. I have no idea.

by Dr Nic at July 03, 2008 07:26 PM

what is *jour?
Uploaded with plasq’s Skitch!

RailsConf 2008 was a few weeks ago, and I didn’t go. I made babies instead. But if you ignore the whoohaa of MagLev and DHH’s Surplus talk, there was one piece of genuine, new awesomeness that came out of RailsConf. This awesomeness, plus Twitter created at least 6 new projects in the last month. These apps are going to make RailsCamp awesome.

That is if RailsConf never happened this piece of delicious coding happiness never would have occurred. It was built at RailsConf, by RailsConf attendees. It was built to solve a problem that only happens at conferences. Crappy internet.

The answer is gitj… no wait, I have more introductory story. [skip it]

Instead of going to RailsConf this year I “get” to go to RailsCamp, which is like a conference for Rails developers, but without the conference part. What’s left without the conference? Hacking, Guitar Hero3, rejectconf-esque talks, and alcohol. Its’ the Lord’s way of saying “you’re in the right profession.”

RailsCamp has two minor drawbacks, since it is held out in the country. One, there is no internet connection. Two, there is no internet connection. Now I know that is only one draw back but I thought it was such a big one it was worth mentioning twice [oh I miss Red Dwarf].

So, 6 months ago we solved one internet-related problem: we turned to Git and git-svn clone to take all our svn repositories with us camping. 6 months on, git has taken over the Ruby OSS world, thanks mostly to GitHub which made remote hosting of git repos easy peasy. But, Github won’t be with us at RailsCamp. So we’re back to where we were 6 mths ago - being gumbies at hosting remote git repos.

So the question to be solved: how do you host a git repository, or indeed, lots of git repositories from your development machine; and conversely, how do you find someone else’s git repositories on their machine and clone it?

And whilst we’re at it… how do you easily share the RubyGems you have installed on your machine with other people, and how do you easily find gems on other people’s machines that you don’t currently have, if you don’t have access to rubyforge.org or github.com to get them?

And furthermore… if you’re running a fun little web app off your dev machine, how can you easily advertise it and other people find it and use it? “Hey, what’s your machine’s name?” “drnic-mac” “Thanks. ..pause… Um, what port’s the app on?” “4050”…

Same problem. Local devs, running local services, but how to share with everyone in the room?

Bonjour

The core solution is a technology created by the boffins at Apple that is now completely open-source (afaik): Bonjour. From wikipedia:

Bonjour, formerly Rendezvous, is Apple Inc.’s trade name for its implementation of Zeroconf, a service discovery protocol. Bonjour locates devices such as printers, as well as other computers, and the services that those devices offer on a local network using multicast Domain Name System service records.

In the Ruby world there are two RubyGems to integrate Bonjour into your app: dnssd and net-mdns. The former is only for Mac OS X, and the latter is a Ruby-only implementation that can be used anywhere. Bingo.

gitjour

So, Chad Fowler, Evan Phoenix, Rich Kilmer and a cast of others started a humble project to solve the first problem: sharing git repositories across a local network, called gitjour.

This project became a happy hack place for many many people.

one or two additions to gitjour
Uploaded with plasq’s Skitch!

NOTE: I’ve included all the installation instructions that should work. If they don’t then try building the gems from src.

On OS X:

    sudo gem install dnssd
    sudo gem install gitjour

    or from source

    sudo gem install dnssd
    git clone git://github.com/chad/gitjour.git
    cd gitjour
    rake install_gem

On Linux/Windows:

    sudo gem install net-mdns
    sudo gem install_gem nogoth-gitjour --source=http://gems.github.com

    or from source

    sudo gem install net-mdns
    git clone git://github.com/nogoth/gitjour.git
    cd gitjour
    rake install

The gitjour project uses dnssd gem, with mac os x dependencies, and GitHub user ‘nogoth’ has ported the project to use the net-mdns gem for Linux/Windows.

Now, open up two terminal windows so you can emulate both sides of “serving” and “finding” a git repository.

Window 1:

    cd a/git/project
    gitjour serve

Window 2:

    gitjour list
    gitjour clone project

In Window 1, you can also serve an entire folder of repositories. Lots of them all at one time. Go into your project’s parent folder and run gitjour serve and you’ll see it Registered dr_nic_magic_models on port 9418. Starting service. a lot. In Window 2, you just gitjour clone xxx the project you want.

See, genius. No mucking around with Apache or setting up local “remote” repositories in your public folder so people can pull from it. Just run gitjour serve from your parent folder and you are instantly hosting ALL of your git repositories. Top Tip: move any private work into another folder first wink

So, that’s the git-sharing problem fixed.

pastejour

After RailsConf, twitter was alive with the coolness of gitjour, and very quickly John Barnette created pastejour to publish and find single pastie’s of code, given that without the internet you don’t have pastie websites.

To install on OS X:

    sudo gem install dnssd
    sudo gem install jbarnette-pastejour --source=http://gems.github.com

    or install from src:

    sudo gem install dnssd
    git clone git://github.com/jbarnette/pastejour.git
    cd pastejour
    rake install

On Linux/Windows:

    sudo gem install net-mdns
    sudo gem install nogoth-pastejour --source=http://gems.github.com

    or install from src:

    sudo gem install net-mdns
    git clone git://github.com/nogoth/pastejour.git
    cd pastejour
    rake install

Pastejour publishes a single block of text until one person retrieves it. Essentially, you yell at the person that there is a paste for them and they get it.

Window 3:

    pastejour <<< "Hello Window 2"

Window 4:

    $ pastejour -f
    Searching for servers (3 seconds)
    Found pastejour at 'drnic'
    $ pastejour drnic
    (drnic from drnic-mac.local.:42424)
    Hello Window 2

gemjour

After John’s announcement on Twitter, I asked “do we have a gemjour yet for sharing gems?”

Later that day, Evan Phoenix replies with “@drnic Enjoy! http://github.com/evanphx/gemjour/tree/master

three-jours
Uploaded with plasq’s Skitch!

This gem serves up all your gems to anyone who wants to install them from you. Don’t have merb but want to try it? Grab it from someone else.

To install on OS X:

    sudo gem install dnssd
    sudo gem install evanphx-gemjour --source=http://gems.github.com

    or install from src:

    sudo gem install dnssd
    git clone git://github.com/evanphx/gemjour.git
    cd gemjour
    rake install

On Linux/Windows:

    sudo gem install net-mdns
    sudo gem install nogoth-gemjour --source=http://gems.github.com

    or install from src:

    sudo gem install net-mdns
    git clone git://github.com/nogoth/gemjour.git
    cd gemjour
    rake install

Window 5:

    gemjour serve

Window 6:

    gemjour list
    gemjour list someuser
    gemjour diff someuser  -- shows diff btw you and them
    sudo gemjour install someuser newgem -- install newgem from someuser
    sudo gemjour install_diff someuser -- installs all gems from someuser you don't have

Go around the room and steal everyone gems in a few minutes.

appjour

Room full of people hacking up cool things? Publish and find them easily with appjour, written by Lachie Cox

To install on OS X:

    sudo gem install dnssd
    sudo gem install lachie-appjour --source=http://gems.github.com

    or install from src:

    sudo gem install dnssd
    git clone git://github.com/lachie/appjour.git
    cd appjour
    rake install

To use it, after you’ve started a web app that you want to publish, open another window to run appjour:

Window 7:

    cd some/web/app
    script/serve -p 4000  # or whatever for your framework
    appjour leet_app 4000

Window 8:

    $ appjour
    Searching for servers (3 seconds)
  found web app called 'leet_app'

  $ appjour leet_app
    ... app loaded into browser

Mongrel publishing bonjour events

Courtenay hacked in a few lines to mongrel_rails so that each mongrel publishes itself to Bonjour.

The magic of this is that mongrels, or the applications behind them, could use Bonjour to self-discover each other within a mongrel cluster, and then talk to each other.

One man’s solution is another man’s problem

As new people join the circus, new gems, git repositories, web apps and pasties are added and the new problem is: how to keep in touch with the newness?

First solution: publish them to Growl with dejour by Aaron Patterson, tagged “Discover awesome services near you.”

On Mac OS X:

    sudo gem install dnssd
    sudo gem install aaronp-dejour --source=http://gems.github.com

    or install from src:

    sudo gem install dnssd
    git clone git://github.com/aaronp/dejour.git
    cd dejour
    rake install

There is no fork of dejour for Linux/Windows yet.

Window 9:

    $ dejour

All your current services should appear as Growl notifications (from Windows 1, 3, 5 and 7 above).

But Growl notifications fly-by pretty quickly. How to keep track?

Finally, *jour to the rescue (starjour)

I really felt that this problem was best solved with a GUI. Mostly cause I was keen to try out RubyCocoa. I don’t think I did productive work for a week, whilst I read books, tutorials and anything I could find on RubyCocoa, Cocoa, Xcode + Interface Builder, Objective-C and after trying and failing a few times starjour was created. By Lachie Cox (who also wrote appjour).

Starjour - panels branch
Uploaded with plasq’s Skitch!

F@#$@#.

Really, I just couldn’t get my head around Cocoa Bindings at the time, but my experience of using RubyCocoa was very positive, once I’d learnt the underlying framework and the thinking-patterns of a Cocoa developer. Best book to read to learn Cocoa/RubyCocoa? The book is by Aaron Hillegass - Cocoa Programming for Mac OS X (3rd ed). After struggling with other people’s books and tutorials this one was wonderful. The “CollectionView” message in the image above was me telling Lachie that I’d figured out and used CollectionViews to replace his TableView. Now i just need to bind the bonjour message type (gitjour/gemjour etc) to an image. And more.

But, nonetheless, starjour (read: *jour as all these apps were collectively named on Twitter) did one job: it sat there showing which services were currently available.

Unlike all the others, this one used the native Cocoa framework for Bonjour, fwiw, and is doomed as an OS X-only app.

On Mac OS X, we don’t really have a place to download and run it, so grab it from src:

    git clone git://github.com/lachie/starjour.git
    cd starjour
    rake run

This requires Xcode and RubyCocoa (available on OS X Leopard or via rubycocoa.com on sourceforge)

Wow! What just happened last month?

Chronologically: gitjour, pastejour, gemjour, dejour, starjour and appjour. And I think there are others. Its incredible.

Firstly, it is going to be an awesome RailsCamp - these tools are killer apps for an internet-less hackfest like RailsCamp, or massive internet-starved conferences like RailsConf.

Secondly, it has been very exciting to watch the creation of these projects over the last few weeks, with the *jour meme bubbling through twitter and irc. Another happy month for our open source communities.

by Dr Nic at June 18, 2008 05:11 AM

I didn’t know you could run Ruby within TextMate snippets. As a consequence, a lot of the TextMate bundles I work on either have simplistic Snippets or the advanced code is run via Commands with code pushed into explicit Ruby files in the Support folder.

But sometimes I just want a clever snippet. For example, I want the ‘cla’ snippet to use the current filename to create the default class name instead of the current ‘ClassName’ default. I want default foreign key names to be meaningful.

I’ve now figured this out (thanks to Ciaran Walsh), and …

Um, lost already? Ok, let me show you via screencast on Snippets and Commands with Ruby (QuickTime (11Mb)):


TextMate Snippets running Ruby from Dr Nic on Vimeo.

Want to learn more about living with TextMate and Ruby?

The TextMate website has a series of videos, including one by the Ruby.tmbundle’s own James Edward Gray II (JEG2).

In addition, there is the latest TextMate for Rails 2 Peepcode written by myself and spoken by Geoffrey Grosenbach. Its cheap at $9, good value at $15.50, and perhaps overpriced in the $20-$30 range. Lucky its only $9.

The snippets used throughout the video

The current Ruby.tmbundle snippet (activated via ‘cla’):

class ${1:ClassName}
	$0
end

An attempt to use regular expressions to convert the filename to a classname:

class ${1:${TM_FILENAME/[[:alpha:]]+|(_)/(?1::\u$0)/g}}
	$0
end

The final snippet, with embedded Ruby to do the heavy lifting (note: added ’singluarize’ to the snippet):

class ${1:`#!/usr/bin/env ruby
    require 'rubygems'
    require "active_support"
    puts ENV['TM_FILENAME'].gsub(/\.rb$/, '').camelize.singularize
    `}
	$0
end

Add this to your own Ruby.tmbundle, or clone mine (which is a clone of the original subversion repo).

by Dr Nic at June 11, 2008 01:51 AM

Two years ago Dave Thomas did a keynote at the first RailsConf in 2006 and outlined a few things missing in Rails. One was the seeming unnecessary un-DRYness of duplicating associations and validations in Active Record models since the same information is in the DB schema. Another was support for Composite Primary Keys on Active Record models.

A few weeks later I created my first RubyGems as an attempt to solve these problems: Dr Nic’s Magic Models and Composite Primary Keys. The former was funny and an entertaining way to use const_missing? and Class.new. The latter was not funny. It started at the heights of “mildly entertaining,” dropped down from there and then over the subsequent months it never again rose above the humour-scale heights of “please shoot me in the foot.”

There were entire months that I hated ever having created the Composite Primary Keys project :)

Why? It sits precariously atop of ActiveRecord::Base, and a half dozen other ActiveRecord classes. It overrides methods, recreates entire blocks of SQL, and must work on all the different database adapters. That is, it is very sensitive to many changes in edge Rails. And refactoring ActiveRecord is a favourite pastime of the Rails Core team.

Unlike the Magic Models project which is perfectly useful for party tricks but probably partially pointless elsewhere, the CPK project just seemed to be so damned useful to some people.

Many people kept using it, several people contributed patches for adapter support and new features, and finally the Prophet arrived. The man that would lead the faithful forward to the Promised Land.

His name is Darrin “Champion” Holst.

Today, CPK was given the golden release number 1.0.0 and officially supports Rails 2.1 (but no longer supports 2.0.2 afaik).

CPK users might remember that we went 0.9.90 back in January, and its now June. This led Darrin to comment in his release email to the mailing list:

I’d like to name this release the “we’re running out of 0.9.x numbers, so it has to go to 1.0 sooner or later” release.

Maintaining CPK is like being on the team that paints the Sydney Harbour Bridge - it takes then 12 mths to paint it from one end to the other, which is just in time to restart painting the bloody thing again.

So in every way that I (tried to) abandon the project, I am proud of Darrin for looking after it and all its users.

by Dr Nic at June 06, 2008 03:29 AM

Sometimes when you are perusing code you ask the question: why the hell is that there? or why does this even work?

Now you can instantly navigate from that erroneous line to the git commit where it was added, and then using github’s commenting system add a full-flavoured remark about that person’s code. I’m not sure if profanity is against the GitHub Terms of Service, but I’d rather ask forgiveness than permission.


TextMate + GitHub - how to comment/discuss on a line via GitHub from Dr Nic on Vimeo.

Download and installation instructions are available in all good bookstores.

Just for TextMate?

There is an editor for Windows - E-TextEditor - that was designed to support TextMate bundles. So far, the GitHub bundle doesn’t use any special features of TextMate’s latest-and-greatest UI libraries, so it should be usable on E-TextEditor.

Also, a VIM project has been created to port the GitHub bundle, by Christoph Blank. Cristoph can be found hanging around #hobo on irc as ’solars’, if you want more goodies in the VIM bundle.

by Dr Nic at May 28, 2008 05:48 AM

I wanted to go from a source file to the equivalent file on github. I wanted a selection of lines in TextMate editor to also be selected when I was taken to github.com. I wanted to cut back on my senseless killing of innocent pasties.

Finally, I wanted to make a nice little video to show off the new GitHub.tmbundle.


TextMate and GitHub: Show the current file in GitHub from Dr Nic on Vimeo.

Which remote repository is it choosing?

If you have multiple remote references to github.com repositories, then the algorithm picks one in the following order:

  1. A remote named ‘github’
  2. A remote named ‘origin’
  3. The first remote for a github.com repository

What else could go in a GitHub textmate bundle?

by Dr Nic at May 26, 2008 07:30 AM

If you are using TextMate (OS X) or E Text Editor (Windows) then you’ll be dead keen to know there is a TextMate bundle for Merb, and another one for Datamapper.

[This is cross-posted on the new Merbunity community site for Merb]

Currently, you can access the bundles via git:

cd "~/Library/Application Support/TextMate/Bundles"
git clone git://github.com/drnic/datamapper-tmbundle.git Datamapper.tmbundle
git clone git://github.com/drnic/merb-tmbundle.git Merb.tmbundle

Now “Reload Bundles” or restart TextMate.

You can now select from three Merb syntax/scopes. Press Shift+Alt+Cmd+M to see the options (below)

Merb TextMate bundle - select ORM

  • If you select Merb (Datamapper) you will have access to the Merb bundle AND the Datamapper bundle.
  • If you select Merb (ActiveRecord) you have access to the Merb bundle AND the Ruby on Rails bundle.
  • If you select Merb (Sequel) then nothing special happens as there isn’t a Sequel bundle yet. Want to create one?

Currently there is no separation in the Rails bundle for ActiveRecord and non-ActiveRecord snippets and commands, so if you use Merb (ActiveRecord) there will be some snippets + commands that you don’t want from Rails. In future, there may be more separation to help the Merb bundle. Also, TextMate 2 may help this cause.

As always, to learn what snippets are available at any given cursor position, use Ctrl+Cmd+T.

If you have new snippets for any of the bundles, I suggest forking the git repositories, pushing up the changes to github and sending Pull Requests to me (drnic).

Sponsored by Engine Yard

The initial development of this bundle was sponsored by Engine Yard because they care.

Rails bundle

There is also the hughly popular Ruby on Rails bundle for TextMate that you can use. Download from http://railsbundle.com/ or via github.

If you want to learn everything about the new Ruby on Rails bundle for TextMate and daily life with TextMate (short cuts, editing bundles) then I highly recommend the new TextMate for Rails 2 Peepcode:

TextMate for Rails 2

This recommendation comes from inside information on the quality of the Peepcode… it was written by me, and incorporates the vocal skills of the operatic Geoffrey Grosenbach.

by Dr Nic at May 12, 2008 12:31 AM

GitHub Badge

The killer app for JavaScript in the 90s was to take a perfectly readable sequence of words - aka “a sentence” - and turn every single character a different colour. You did that, then you went back to doing normal work.

The killer app for JavaScript in the 00s is widgets/badges. Taking perfectly useful stuff from one website - aka “data” - and re-posting it on your blog sidebar.

In both circumstances, across two decades, you did this to look cool. Sure, it never works but you do it anyway.

Thusly, riding high on the world-dominating success of GitHub there seems to be a vacancy in the department of “JavaScript badge for my blog to make me look cool” projects, in the sub-category of “GitHub”.

The first entrant and thus market leader of the “Blog Badges for GitHub” micro-industry is the “GitHub Badge” It is beautifully demonstrated in static image form above, or if you click the image through to the website (or here to my blog and its aesthetically appealing enhancement with said GitHub Badge) you’ll see it live and interactive.

Touch it. Feel it. Press the “Show more” link for hours of entertainment.

Installation

Then get it for yourself. Read the GitHub Badge website or just…

Slap the following into your blog sidebar:

<div id="github-badge"></div>
<script type="text/javascript" charset="utf-8">
  GITHUB_USERNAME="drnic";
  GITHUB_LIST_LENGTH=10;
  GITHUB_HEAD="div"; // e.g. change to "h2" for wordpress sidebars
</script>
<script src="http://drnicjavascript.rubyforge.org/github_badge/dist/github-badge-launcher.js" type="text/javascript"></script>

Only GITHUB_USERNAME is a required pre-set variable. The others above show the defaults, and can be changed by being specified.

For my WordPress sidebar, which uses <li> and <h2> for sections and headers, I use the following:

<li id="github-badge"></li>
<script type="text/javascript" charset="utf-8">
  GITHUB_USERNAME="drnic";
  GITHUB_HEAD="h2";
</script>
<script src="http://drnicjavascript.rubyforge.org/github_badge/dist/github-badge-launcher.js" type="text/javascript"></script>

Thus the badge is inserted in the <li> element, and the “My projects (drnic)” header is an <h2> instead of a <div>.

Source and tests on github

You can fetch the source from github - http://github.com/drnic/github_badges/tree/master - using:

git clone git://github.com/drnic/github_badges.git

You might want to add an option to turn off the auto-CSS generator so you can theme it yourself.

The JavaScript tests are in test/ folder. Either run them with rake test, or individually open the files in your browser.

Future cool things the badge could do…

Ultimately, when Chris Wanstrath (harass him on twitter) adds more fields and APIs to the GitHub API then the badge can do more. Currently it orders the projects in reverse order - that is, newest projects first. If I can get the network/watching counters then I can order the list using those values, etc.

Any other cool ideas for what the badge could do?

I’d like to be able to show project version numbers (e.g. gem version numbers) and other project meta-information. This would require GitHub to offer a CRUD UI for generic key/values and for the fields to be returned via their API.

If you know Chris and can throw heavy objects at him with moderate certainty of hitting him and not damaging any expensive equipment, please do so til he gives me this stuff.

by Dr Nic at May 03, 2008 04:14 AM

Any first year computer science student should be able to explain the divide and conquer algorithm, why it is important, and when to use it.

But how many developers apply it to their software engineering practices?

Stories

Agile developers work with the concept of stories. Essentially these are short explanations of what someone should be able to do with the software once the story is implemented.

A user should be able to manage their account.

This story is vague, open ended and typical of client feature requests.

Before estimating this story, you’re going to need to get some fine grained detail into it. After talking with your client you might end up with the following:

Managing their account consists of changing their name, updating their contact details, changing their profile picture, resetting their password and swapping between username/password and open-id for authentication.

Now you have enough information to estimate this story, you know what the client means by “manage their account”.

You might come up with an estimate of say 100 points.

This example is contrived but if every story your client sends you follows this same process you’re heading straight for a world of pain.

The Effect of Large Stories on Iterations

So you start work on your iteration with 10 large stories with your team and half way through you’re looking at the number of stories you’ve sent to your client for testing.

That number is zero and you’re starting to feel a bit stressed out. It’s ok though because by the end of the day 5 stories have moved into the testing phase.

You move on to the rest of the stories in the iteration and your client gets started testing the stories you’ve completed. Some of them pass and others get sent back because something is not quite right.

The cumulative effect of multiple large stories is an exponential story completeness graph.

The Effect of Large Stories on Developers

Working on the same story for a long period of time is painful. As a developer you feel like you’re climbing a mountain that just keeps getting bigger.

You’re not sure where the top is, and sometimes you think you’ve reached it, but as it turns out you’ve forgotten something and you’re not quite at the top.

The effect on the team is that everyone goes through this same journey every iteration.

Your client experiences the same in reverse. They sit around waiting to climb the mountain, and they wait, and then bang, they’re expected to be half way up the mountain almost instantly.

Divide and Conquer for Happiness

Large stories can often be broken down into smaller stories. In fact you already did this when estimating your stories. But you finished one step before you should have.

You should have split the original story into multiple smaller ones:

A user should be able to change their name.

A user should be able to change their contact details.

A user should be able to change their profile picture.

A user should be able to change their password.

A user should be able to switch between username/password and openid for authentication.

Each one of these stories is smaller, has easier to meet acceptance criteria and will be finished earlier.

Your client will love you because they don’t spend half their time sitting around and waiting for stories to test.

The developers in your team will love you because instead of climbing one massive mountain, they are climbing 5 mounds.

Everyone feels like they are constantly moving forwards through the iteration.

The story completeness graph moves from being exponential to linear.

That’s a much more positive graph to have hanging on your office wall.

April 16, 2008 11:28 AM

How many times have you come across a comment block in a piece of code like this?:

# TODO – Make this work properly for all cases.

At some point in time this comment made sense. Someone, somewhere had every intention of fixing this and removing the comment.

It’s quite possible that someone was you, but you don’t remember anymore.

todos are irrelevant

todos live in no-mans land. Stuck in a time warp for all eternity. Don’t kid yourself, you’re never going to re-visit a todo. They’re irrelevant.

They fly under the radar because they have no visibility. Out of sight, out of mind. If you’re not working on the code with the todo, then you don’t know it’s there, and you don’t care.

Other than doing a project wide search, you’re never going to find them all. Even if you do, you’re not going to understand the meaning of them all.

They’re irrelevant.

making todos relevant again

What’s the point of a todo list if nobody knows about it?

Take control of your todos and enshrine them in code that everyone sees every day. Write them into your test suite as pending tests.

Failing tests are bad. Passing tests are good. Pending tests are just plain annoying.

They clearly indicate that something hasn’t been finished properly. They’re visible every time you run your test suite. This means they annoy everyone until they’re fixed.

They define todos better than any other mechanism at your disposal.

Sprinkle in a touch of guilt

While a quick flick through svn blame will find the culprit, it’s not visible and it means work on your behalf.

Sprinkle in a touch of guilt by mandating all pending tests be created with the Authors name and todays date in the pending message.

That’ll ensure they don’t hang around like a bad smell for too long.

April 16, 2008 09:43 AM

rubyforge has git - account creation

First there was CVS, then came SVN to RubyForge. Actually, I’m guessing that CVS was the sole SCM initially, and SVN was added later. I just can’t imagine them starting RubyForge from scratch and explicitly saying “yeah we’ll offer CVS because …”. I don’t know how that sentence would have finished. It must have been added first. There is no valid “because …”.

But now, with each RubyForge project there is a Git repository. This is great for two very good reasons:

  1. the end is nigh for empty or unmaintained SVN repositories
  2. less centralised decentralised development

GitHub + Gitorious are the go-to-guys for somewhere to hang your proverbial hat: somewhere you push your local git repository to so other people can access it.

Yet when I’m investigate someone’s new RubyGem or other project, the first place I look for its source is RubyForge. I assume the project name is the same as the gem name, and goto http://rubyforge.org/projects/pastiepacker for example.

rubyforge scm link

Then I click on the SCM tab. Normally this gives SVN instructions. In this modern era, many projects SVN repos can now be blank or unmaintained with Git repos being preferred by many.

So, the reason to push your git repository to RubyForge’s new git system is to make life easier for me. I mean, I don’t know who else clicks on the SCM link, but I do.

While this is all well and great for new projects - you can select “git” instead of “svn” as per the first image above - but I’m not yet sure how to migrate old projects [follow support ticket].

The other reason is to actually help your distributed repository be distributed. Push it to github. Push it to gitorious. And now you can push it to rubyforge. Sweet.

Adding another remote repository

Since your default remote repository is probably called “origin”, you may wonder how such a naming schema could ever expand to multiple remote repositories? Might I suggest “rubyforge” as the name of the rubyforge repository?

git remote add rubyforge gitosis@rubyforge.org:pastiepacker.git
git push rubyforge master
git push origin master

If you do this for all your projects, perhaps a bash script is in order:

function gpall() {
  git push rubyforge master
  git push origin master
}

One project equals one repository, but multiple packages

Using a Rubyforge project’s git repository might be problematic where you release multiple packages/rubygems (codeforpeople, seattlerb, mongrel, drnicutilities, etc). Here I’d prefer one repository per package, rather than one repository for the whole “project”.

Perhaps you could push the master branch from your local repository for each package into different branches of the rubyforge git repository. Thoughts?

Can’t push to your rubyforge git repo?

From the FAQ:

RubyForge Git repositories are managed via gitosis; gitosis does authentication via public keys. This means that in order to push to a RubyForge Git repository you’ll need to upload a public key to your account - see notes on that here.

Generally, Git support is something we’ve just introduced, so feedback about it on the forums would be quite welcome.

What is Pastie Packer?

I can’t tell you. Its a secret.

by Dr Nic at April 08, 2008 02:27 AM

When you define a “static” or “class” method on a Ruby class, it actually stores the method on that class’s metaclass/singleton class/eigenclass.

_why’s metaid gem gives you a metaclass method to explicit access this object:

require 'metaid'
class Person
  def self.oldest
    # find oldest person
  end
end
Person.methods.grep(/oldest/) # => ['oldest']
Person.metaclass.instance_methods.grep(/oldest/) # => ['oldest']

So now here’s a new, fun way to access the metaclass of a class, look for a constant suffixed with ‘Metaclass’. For the Person class, look for PersonMetaclass. Yep, we can have explicit metaclass constants. Or try PersonClass or PersonEigen or PersonEigenclass. No one can agree on what they are called, so I made them all work.

$ gem install magic_metaclass
$ irb

In irb try:

require 'rubygems'
require 'magic_metaclass'
class Person; end
Person
# => Person
PersonMetaclass
# => #<Class:Person>
PersonClass
# => #<Class:Person>
PersonEigenclass
# => #<Class:Person>
PersonEigen
# => #<Class:Person>

Neat.

Finally, the example from above:

class Person
  def self.oldest
    # find oldest person
  end
end
PersonMetaclass.instance_methods.grep(/oldest/) # => ['oldest']

I wrote this gem with no known use cases. If you find any, let me know.

by Dr Nic at April 03, 2008 07:51 AM