Edit in JSFiddle

(function($, window, document, undefined){

    // ユーティリティ用のメソッドを定義
    $.extend($.fn, {

        // 要素に紐付けられたモデルデータを返す
        item: function(){

            // 紐付けられたモデルデータを取得
            var item = $(this).tmplItem().data;

            // モデルデータを返す(reloadで再取得してから)
            // http://spinejs.com/api/models内のreloadAPI参照
            return ($.isFunction(item.reload) ? item.reload() : null);

        },

        // 渡されたモデルデータを持つ要素を返す
        forItem: function(item){

            return this.filter(function(){

                // 紐付けられたもモデルデータを代入
                var compare = $(this).tmplItem().data;

                // モデルデータがあり、モデルデータと一致すればその要素を返す
                // http://spinejs.com/api/models内のeqlAPI参照
                if (item.eql && item.eql(compare) || item === compare) return true;

            });

        },

        // 指定された要素内の配列を返す
        serializeForm: function(){

            // 返り値用にオブジェクトを生成
            var result = {};

            // 指定された要素をシリアライズし、配列を返す
            $.each($(this).find('input, textarea').serializeArray(), function(i, item){
                result[item.name] = item.value;
            });

            // JSONオブジェクトを返す(e.x.{ email: '...', first_name: '...', last_name: '...' })
            return result;

        }

    });

    // Contactモデル
    // Spineの最新版だと設定方法は変更されているので注意
    var Contact = Spine.Model.setup('Contact', ['first_name', 'last_name', 'email']);

    // ローカルストレージを利用するのでLocalを継承(要local.js)
    Contact.extend(Spine.Model.Local);

    // テンプレート内で使用するためにインスタンスメソッドを追加
    Contact.include({
        fullName: function(){
            // ファーストネーム、ラストネームが設定されていない場合はfalseを返す
            if (!this.first_name && !this.last_name) return;
            return (this.first_name + ' ' + this.last_name);
        }
    });

    // Sidebarコントローラー
    var Sidebar = Spine.Controller.create({

        // インスタンス変数を追加
        elements: {
            '.items': 'items'
        },

        // buttonをclickするとcreateメソッドを叩く
        events: {
            'click button': 'create'
        },

        // イベントのコールバックとして関数が呼び出された際に、
        // 正しいコンテキストのもとで実行されることを保証
        proxied: ['render'],

        // テンプレートを描画
        template: function(items){
            return ($('#contactsTemplate').tmpl(items));
        },

        // 初期化処理
        init: function(){

            // レコード一覧を作成するクラスからインスタンスを作成
            // .itemクラスが必須(選択された要素には.currentクラスが付与される)
            // https://raw.github.com/maccman/book-assets/master/ch11/spine.contacts/lib/spine.list.js
            this.list = Spine.List.init({
                el: this.items,
                template: this.template
            });

            // リスト上で異なる項目が選択された際に、該当する連絡先を表示
            this.list.bind('change', this.proxy(function(item){

                // グローバルなSpine.Appに対してshow:contactイベントを発火(選択された項目を渡す)
                this.App.trigger('show:contact', item);

            }));

            // 連絡先が変更(あるいは新規作成)された場合に、
            // リスト上で選択されている項目を切り替えます
            // グローバルなSpine.Appに対してイベントを登録
            this.App.bind('show:contact edit:contact', this.list.change);

            // リストが更新あるいは変更された場合に再描画
            Contact.bind('refresh change', this.render);

        },

        // リストを描画
        render: function(){

            // 全モデルインスタンス取得
            var items = Contact.all();

            // listのrenderメソッドを実行(全モデルインスタンスを渡す)
            this.list.render(items);

        },

        // 新規作成のボタンがクリックされた際に呼ばれる
        create: function(){

            // 新規連絡先を追加
            var item = Contact.create();

            // グローバルなSpine.Appに対してedit:contactイベントを発火(新規モデルインスタンスを渡す)
            this.App.trigger('edit:contact', item);

        }

    });

    // Contactsコントローラー
    var Contacts = Spine.Controller.create({

        // インスタンス変数を追加
        elements: {
            '.show': 'showEl',
            '.edit': 'editEl',
            '.show .content': 'showContent',
            '.edit .content': 'editContent'
        },

        // イベントの委譲
        events: {
            'click .optEdit': 'edit',
            'click .optDestroy': 'destroy',
            'click .optSave': 'save'
        },

        // イベントのコールバックとして関数が呼び出された際に、
        // 正しいコンテキストのもとで実行されることを保証
        proxied: ['render', 'show', 'edit'],

        // 初期化処理
        init: function(){

            // 初期表示では連絡先が表示される
            this.show();

            // リストが変更された場合に再描画
            Contact.bind('change', this.render);

            // サイドバーで異なる項目が押下されると発火(連絡先画面を表示)
            // グローバルなSpine.Appに対してイベントを登録
            this.App.bind('show:contact', this.show);

            // サイドバーで新規作成ボタンが押下されると発火(連絡先編集画面を表示)
            // グローバルなSpine.Appに対してイベントを登録
            this.App.bind('edit:contact', this.edit);

        },

        // currentプロパティに選択されたモデルを代入
        change: function(item){
            this.current = item;
            this.render();
        },

        // 選択されている要素の連絡先画面、連絡先編集画面を選択されたモデルを元に描画
        render: function(){
            this.showContent.html($('#contactTemplate').tmpl(this.current));
            this.editContent.html($('#editContactTemplate').tmpl(this.current));
        },

        // 連絡先画面を表示
        show: function(item){

            // モデルが渡されていればchangeイベントを叩く
            if (item && item.model) this.change(item);
            this.showEl.show();
            this.editEl.hide();

        },

        // 連絡先編集画面を表示
        edit: function(item){

            // モデルが渡されていればchangeイベントを叩く
            if (item && item.model) this.change(item);
            this.showEl.hide();
            this.editEl.show();

        },

        // 連絡先を削除
        destroy: function(){

            // 選択されているモデルデータを削除
            this.current.destroy();

        },

        // 連絡先を保存
        save: function(){

            // 指定された要素をシリアライズし、配列を返す
            var atts = this.editEl.serializeForm();

            // 選択されているモデルデータを更新
            // http://spinejs.com/api/models内のupdateAttributesAPI参照
            this.current.updateAttributes(atts);

            // 連絡先画面を表示
            this.show();

        }

    });

    // Appコントローラー
    var App = Spine.Controller.create({

        // el要素を設定
        el: $('body'),

        // インスタンス変数を追加
        elements: {
            '#sidebar': 'sidebarEl',
            '#contacts': 'contactsEl'
        },

        // 初期化処理
        init: function(){

            // Sidebarコントローラを初期化
            this.sidebar = Sidebar.init({ el: this.sidebarEl });

            // Contactsコントローラを初期化
            this.contact = Contacts.init({ el: this.contactsEl });

            // モデルデータをローカルストレージから取得
            Contact.fetch();

        }

    });

    // DOMContentLoaded後にinit()関数を叩く
    $(function(){
        App.init();
    });

}(jQuery, window, this.document));
<script type="text/x-jquery-tmpl" id="contactsTemplate">
    <li class="item">
        {{if fullName()}}
            <div>${fullName()}</div>
        {{else}}
            <div>名前なし</div>
        {{/if}}
    </li>
</script>

<script type="text/x-jquery-tmpl" id="contactTemplate">
    <dl>
        <dt>名前</dt>
        <dd>${first_name} ${last_name}</dd>
        <dt>メールアドレス</dt>
        {{if email}}
            <dd>${email}</dd>
        {{else}}
            <dd>なし</dd>
        {{/if}}
    </dl>
</script>

<script type="text/x-jquery-tmpl" id="editContactTemplate">
    <dl>
        <dt>ファーストネーム</dt>
        <dd><input type="text" name="first_name" value="${first_name}" autofocus></dd>
        <dt>ラストネーム</dt>
        <dd><input type="text" name="last_name" value="${last_name}"></dd>
        <dt>メールアドレス</dt>
        <dd><input type="text" name="email" value="${email}"></dd>
    </dl>
</script>

<div id="sidebar">
    <ul class="items"></ul>
    <footer>
        <button>新規連絡先</button>
    </footer>
</div>
<div id="contacts">
    <div class="show">
        <ul class="options">
            <li class="optEdit">連絡先の編集</li>
        </ul>
        <div class="content"></div>
    </div>
    <div class="edit">
        <ul class="options">
            <li class="optSave default">連絡先の保存</li>
            <li class="optDestroy">連絡先の削除</li>
        </ul>
        <div class="content"></div>
    </div>
</div>
body{
    line-height:2;
}
#sidebar{
    width:20%;
    float:left;
}
#sidebar .current{
    background:#000;
    color:#fff;
}
#contacts{
    width:75%;
    float:right;
}
#contacts dt{
    background:#efefef;
}
#contacts .options{
    margin-bottom:1em;
    overflow:hidden;
}
#contacts .options li{
    width:10em;
    margin-right:1em;
    float:left;
    background:#000;
    color:#fff;
    text-align:center;
}

External resources loaded into this fiddle: