SlideShare a Scribd company logo
Lessons from a rewrite
                Rebecca Murphey • TXJS 2011 • Austin, Texas




Saturday, June 11, 2011
Rewrite projects are ... tempting because they
                always look easier than they actually are.
                If you want to survive a software rewrite,
                don’t throw away your existing software ...
                Write the code in such a way that it co-exists
                with your existing software, perhaps as an
                optional add-on that you can sell.
                                                  James Shore




Saturday, June 11, 2011
Where a new system concept or new
                technology is used, one has to build a system to
                throw away, for even the best planning is not
                so omniscient as to get it right the rst time.
                Hence plan to throw one away; you will,
                anyhow.
                                                    Fred Brooks




Saturday, June 11, 2011
Until you are able to prove that you have a
                viable market ... you shouldn’t be sinking a lot
                of time and money into implementation.
                Assuming you are [competent], you should be
                able to ... take on quite a bit of technical
                debt to build throwaway proof-of-concept
                versions of your product.
                                                 Obie Fernandez




Saturday, June 11, 2011
ere’s a subtle reason that programmers
                always want to throw away the code and start
                over. e reason is that they think the old code
                is a mess. ... e reason that they think the old
                code is a mess is because of a cardinal,
                fundamental law of programming: It’s harder
                to read code than to write it.
                                                     Joel Spolsky




Saturday, June 11, 2011
For the human makers of things,
                the incompleteness and
                inconsistencies of our ideas
                become clear only during
                implementation.
                                            Fred Brooks




Saturday, June 11, 2011
A content management system for the rapid
                creation of content-rich mobile applications.
                A PhoneGap & Dojo system that consumes
                the output of the CMS to generate the
                application.


Saturday, June 11, 2011
Saturday, June 11, 2011
Saturday, June 11, 2011
Saturday, June 11, 2011
Saturday, June 11, 2011
If data is required for a
                                                                                                               route, such as node data
                                                                                                               or the user's favorites, the
                                                                                                               route requests the data
                                                                                                               prior to creating the page.




                                                                                                                          Data                                                                                                                                                    Page Controller
                                                                                                                                                                                                                                                   Component

                                                                                                                                                                                                                                                                                    Component


                                                                                                                                                                                                      Page Controller                              Component                        Component



                                                                                                                                                                                                                                                                                    Component
                                                                                                                                                                                                                                                   Component

        When the app is booted
        from a "cold" state (that   URL Change                               Router                                                                            Page Factory
        is, it's not fast-app-                                                                                                                                                           Page controllers are responsible for           Components are responsible for            Once components are placed on the
        switched), it goes                                                                                                                                                               receiving data from the Page Factory. Once     receiving and rendering data and          page, the Page Controller brokers
        through a bootstrapping                                     The Router detects a                                                                 The Route asks the page         the page controller receives that data, it     reacting to user interaction.             communication between components.
                                                                                                                         Route                                                           determines which components to display in
        process. This process                                       change in the URL, looks                                                             factory to generate a page
                                                                    for a matching route in                                                              controller. If the page         its postMixInProperties method and sets up     Components whose content can change       Components announce events either by
        ensures the application                                                                                                                                                          "placements," which define the components       during a single page view should expose   publishing a topic (for events that may
                                                                    toura.app.Routes, and                                                                controller is for a node, the
        is working with the                                                                                                                                                              that will be placed on the page and the data   an API using setters (i.e.                have app-wide significance) or by calling
                                                                    parses any additional                                                                Page Factory figures out
        most recent data, and                                       parameters out of the URL                                                            which template (Audios,         that needs to be passed to them. The           _setContentAttr) that allows Page         a method (such as onClick). Page
        also sets up various                                        (such as the node ID,                                                                Images, etc.) the node          actual instantiation and placement of          Controllers to update their content.      Controllers can subscribe to these
        application-wide                 Device Storage             asset type, etc.).                                                                   uses. The page factory          components is handled in the postCreate                                                  topics or connect to these method calls
                                                                                                                                                         creates an instance of the      method of toura.pageControllers._Page,         Alternately, a component can define an     (much like connecting to events). This
        functionality, including
                                                                                                                                                         page controller and hands       which is inherited by all Page Controllers.    attributeMap object that specifies how     happens in the Page Controller's
        the Router, UI, Page                                                                                                                                                                                                            the component should react when a         postCreate method.
                                                                                                                                                         the created instance back
        Factory, Data Store,                                                                                                                             to the Route.                                                                  property is set.
        and several others.

        Once these pieces are
        in place, the app                    Remote
        triggers a URL change
        to the home node, and
        the process outlined
        here begins.



                                    Every time the user goes                                                               UI
                                    to a new page, we check
                                    the age of the local data; if
                                    it is more than 8 hours old,
                                    we see whether we need
                                    to do an over-the-air
                                    update.




                                                                                                                       Browser

                                                                            Page Container

                                                                               Old Page Controller                               New Page Controller

                                                                                  Component                                         Component


                                                                                  Component                                         Component



                                                                                  Component                                         Component




                                                                    The Route asks toura.app.UI to place the Page Controller in the UI; in turn, toura.app.UI sets the content
                                                                    attribute of the Page Container. If there is already a page on screen, the Page Container handles the animation
                                                                    between the pages, and calls the destroy method of the old page, which results in the proper teardown of the old
                                                                    page and its components.




Saturday, June 11, 2011
e secret to building large apps is
                never build large apps. Break up your
                applications into small pieces. en,
                assemble those testable, bite-sized pieces
                into your big application.
                                                   Justin Meyer




Saturday, June 11, 2011
Managing data


Saturday, June 11, 2011
}
                       }],
                       "parent": {
                           "_reference": "node-365"
                       },
                       "page_controller": "Audios1",
                       "id": "node-369",
                       "sharing_text": null,
                       "sharing_url": null,
                       "images": [{
                           "caption": {
                               "_reference": "text-asset-60"
                            },
                           "image": {
                               "_reference": "image-174"
                            }
                       }, {
                           "caption": {
                               "_reference": "text-asset-61"
                            },
                           "image": {
                               "_reference": "image-182"
Saturday, June 11, 2011
                            }
backgroundImage : toura.models.BackgroundImage
           },

           constructor : function(data) {
             this._loadData(data);
           },

           _loadData : function(data) {
             this._store = new dojo.data.ItemFileReadStore({
               data : {
                  identifier : 'id',
                  items : data
               },
               hierarchical : false
             });

             this.onLoadData(data);
           },

           onLoadData : function(data) {
             // stub for connection
           },

                      get : function(id, type) {
                        if (!id) {
                          throw new Error('toura.app.Data::getModel requires an id');
Saturday, June 11, 2011 }
toura.app.Tour.search('drink');




Saturday, June 11, 2011
store.fetch({ query : q, onComplete : addItems });




Saturday, June 11, 2011
Building the interface


Saturday, June 11, 2011
Saturday, June 11, 2011
page controllers set up components and
                broker communication between them
                ui components receive and render data;
                announce and react to user interaction
                app components manage app data and state




Saturday, June 11, 2011
dijit._Widget and dijit._Templated




Saturday, June 11, 2011
Saturday, June 11, 2011
toura.app.Tour.get(nodeId);




Saturday, June 11, 2011
this.placements = [
                    [
                      'VideoCaption',
                       { node : this.node },
                      'videoCaption'
                    ],
                    [
                      'VideoList',
                       { node : this.node },
                      'videoList'
                    ],
                    [
                      'VideoPlayer',
                       { node : this.node },
                      'videoPlayer'
                    ]
                 ];



Saturday, June 11, 2011
this.connect(this.videoList, 'onSelect', function(assetId) {
                   var video = this._videoById(assetId);
                   this.videoCaption.set('content', video.caption || '');
                   this.videoPlayer.play(assetId);
                 });




Saturday, June 11, 2011
videoPlayer.set('mediaId', mediaId);




                             _setMediaIdAttr : function(mediaId) {
                               var media = this.media = this.mediasCache[mediaId];

                                  if (this.useHtml5Player && !this.player) {
                                    this._queuedMedia = media;
                                    return;
                                  }

                                  this._queuedMedia = null;

                                  if (this.player) {
                                    this.player.src = media.url;
                                  }
                             },


Saturday, June 11, 2011
pageContainer.set('content', newPage);




Saturday, June 11, 2011
Saturday, June 11, 2011
Saturday, June 11, 2011
myComponent.set()
                to change state

                myComponent.on<Evt>()
                to announce state changes

                myComponent.connect()
                to listen for events & methods

                myComponent.subscribe()
                to react to published topics

                dojo.publish()
                to announce occurrences of app-wide interest


Saturday, June 11, 2011
Facilitating
                development

Saturday, June 11, 2011
/* {{^android}} */
                var mediaPath = "www/media/" + toura.pages.currentId +
                "/";
                /* {{/android}} */
                /* {{#android}} */
                var mediaPath = [Toura.getTouraPath(),
                toura.pages.currentId].join("/");
                /* {{/android}} */
                var imagesList = [], dimensionsList = [], namesList =
                [], thumbsList = [];
                var pos = -1, count = 0;
                /* {{#android}} */
                var pos = 0, count = 0;
                /* {{/android}} */




Saturday, June 11, 2011
toura.app._Config = {
                            // ...
                            device : {
                              os : 'android',
                              type : 'phone'
                            }
                          }




Saturday, June 11, 2011
toura.app.Has = function() {
                   var device = toura.app.Config.get('device');

                      return {
                        cssBackgroundContain : function() {
                          return !(
                              device.os === 'android' &&
                              device.version === '2-1'
                           );
                        },

                          html5Player : function() {
                            return device.os !== 'android';
                          },

                        iScrollZoom : function() {
                           return device.os !== 'android';
                         }
                      };
                 };



Saturday, June 11, 2011
//>>excludeStart('production', kwArgs.production);
                if (toura.features.debugToolbar) { toura.app._Debug(); }
                //>>excludeEnd('production');




Saturday, June 11, 2011
/**
           * This file generates built dojo and toura files for
           * use in production.
           */
          dependencies = {
            /*
              * tell dojo to strip out ie-specific hacks
              */
            webkitMobile : true,

             /*
              * this is a production build -- non-production code should be stripped
              */
             production : true,

             /*
              * the name of the release and where to put it,
              * relative to dojo-release-X.X.X-src/util/buildscripts/.
              */
             releaseName : 'production',

             /*
              * strip all calls to console.log automatically
              */
             stripConsole : 'all',

                      /*
                        * clean the existing release directory, then create a new release
Saturday, June 11, 2011 */
dojo.provide('toura.app.Phonegap.network');

                 toura.app.Phonegap.network = function(pg, device) {
                   var n = navigator;

                     return {
                       isReachable : function(domain) {
                         console.log('toura.app.Phonegap.network::isReachable()');
                         var dfd = new dojo.Deferred();

                          if (n && n.network && n.network.isReachable) {
                            console.log('using phonegap network');
                            n.network.isReachable(
                               domain || 'aws.amazon.com',
                              function(r) {
                                  dfd.resolve(r !== NetworkStatus.NOT_REACHABLE);
                               },
                              function() {
                                 console.log('failure on network is reachable');
                                  dfd.resolve(false);
                               }
                            );
                          } else {
                            // resolve false if PhoneGap is present;
                            // network is not indeed reachable
                            if (pg) {
                               dfd.resolve(false);
Saturday, June 11, 2011
                            } else {
Taming
                asynchronicity

Saturday, June 11, 2011
var myAsyncThing = function() {
                    var dfd = new dojo.Deferred();
                    setTimeout(function() {
                      dfd.resolve('hello');
                    }, 1000);
                    return dfd.promise;
                 };

                 myAsyncThing().then(function(result){
                   console.log(result);
                 });



Saturday, June 11, 2011
myPromise.then(win, fail)
                react to an immutable promise




Saturday, June 11, 2011
dojo.when(fn(), win, fail)
                react to maybe-asynchronous things,
                including promises




Saturday, June 11, 2011
/**
        * @private
        * @returns {Promise} A promise that, if resolved, will be resolved with the
        * remote data.
        */
       _getRemoteData : function() {
         var dfd = new dojo.Deferred();
                                                              _xhr : function(url, dfd) {
                                                                 return dojo.xhrGet({
         toura.app.Phonegap.network.isReachable()
                                                                   url : url,
           .then(
                                                                   preventCache : true,
              dojo.hitch(this, function() {
                                                                   handleAs : 'json',
                this._xhr(this.remoteDataUrl, dfd);
                                                                   contentType : false,
              }),
                                                                   load : dfd.resolve,
             function() {
                                                                   error : dfd.reject
                dfd.reject('Remote is not reachable');
                                                                 });
              }
                                                              },
           );

            return dfd.promise;
       },




Saturday, June 11, 2011
dojo.when(this._getAuth(), dojo.hitch(this, function(t) {
                   this.token = t;
                   this._postMessage(msg, t).then(dfd.resolve, dfd.reject);
                 }));




Saturday, June 11, 2011
Handling state


Saturday, June 11, 2011
index.html#/node/123/image/456
                          index.html#/search/bacon
                          index.html#/favorites
                          index.html#/about




Saturday, June 11, 2011
{
                      route : //node/(.*)/,
                      handler : function(params, route) {
                        var splat = params.splat[0].split('/'),
                            nodeId = splat[0],
                            pageState = {
                               assetType : splat[1],
                               assetId : splat[2],
                               assetSubId : splat[3]
                            };

                          return nodeRoute(route, nodeId, pageState);
                      }
                 },

                 {
                      route : //search/?(.*)/,
                      handler : function(params) {
                        var page = toura.app.UI.getCurrentPage(),
                            term = params.splat && params.splat[0].split('/')[0];

                          if (!page || !page.type || page.type !== 'search') {
Saturday, June 11, 2011
https://github.com/rmurphey/dojo-boilerplate
Saturday, June 11, 2011
dojo.require('dojo.Stateful');

                 dojo.declare('toura.app.UI', [ dojo.Stateful ], {
                   // ...
                 });




Saturday, June 11, 2011
this.watch('fontSize', function(k, oldSize, newSize) {
                   var b = this.body;
                   if (oldSize) { dojo.removeClass(b, oldSize); }
                   dojo.addClass(b, newSize);
                   toura.app.DeviceStorage.set('fontSize', newSize);
                 });




Saturday, June 11, 2011
Writing to rewrite


Saturday, June 11, 2011
Feature: A User can view a node with the Video1 template and play each video

                           Scenario: Videos1: Setup
                             When I am on the home node
                             And I click "Plain Node"
                             And I click "Video Player"
                             Then I should see a Video Player
                             And I should see an Asset List
                             And I should see a Child Nodes list
                             And I should see "movie_for_demo" in the asset list

                           Scenario: Videos1: Controlling the video player from the asset list
                             When I am on the home node
                             And I click "Plain Node"
                             And I click "Video Player"
                             And I click "movie_for_demo" in the asset list
                             Then "video-29" should be selected in the asset list
                             And I should see "videos-29" in the video player

                           Scenario: Navigating to a specific video
                             When I am on the home node
                             And I go to the URL "#/node/node-372/videos/video-29"
                             Then I should see "videos-29" in the video player
                             And "video-29" should be selected in the asset list




Saturday, June 11, 2011
expect(c.queryInput).toBeDefined();
                        expect(c.i18n_placeholderText).toBeTruthy();
                      });
                    });

                    it("should provide an API for setting the search term", function() {
                      c = C();

                      c.set('searchTerm', term);
                      expect(c.queryInput.value).toBe(term);
                    });

                    it("should announce searches when the form is submitted with a term", function() {
                      var submitHandler;

                          toura.app.UI.hasTouch = false;
                          c = C();

                          c.set('searchTerm', term);
                          spyOn(c, 'search');
                          submitHandler = getEventHandlers(c, 'submit', c.searchForm)[0];

                      submitHandler(fakeEventObj);
                      expect(c.search).toHaveBeenCalledWith(term);
                    });

                    it("should not announce searches if the form is submitted with no term", function() {
                      var submitHandler;

                          toura.app.UI.hasTouch = false;
                          c = C();

                          c.set('searchTerm', '');
                          spyOn(c, 'search');
Saturday, June 11, 2011   submitHandler = getEventHandlers(c, 'submit', c.searchForm)[0];
It takes con dence to throw work
                away ... When people rst start drawing,
                they’re often reluctant to redo parts that
                aren’t right ... they convince
                themselves that the drawing is not
                that bad, really — in fact, maybe they
                meant it to look that way.
                                                Paul Graham




Saturday, June 11, 2011
http://pinboard.in/u:rmurphey/t:lessons-from-a-rewrite/




                          rebeccamurphey.com • blog.rebeccamurphey.com • @rmurphey




Saturday, June 11, 2011

More Related Content

Similar to Lessons from a Rewrite

We4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshell
We4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshellWe4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshell
We4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshellWe4IT Group
 
DashMash: a Mashup Environment for End User Development
DashMash: a Mashup Environment for End User DevelopmentDashMash: a Mashup Environment for End User Development
DashMash: a Mashup Environment for End User Development
Matteo Picozzi
 
CSSDP Presentation April 09
CSSDP Presentation April 09CSSDP Presentation April 09
CSSDP Presentation April 09
Cybera Inc.
 
Component based design
Component based designComponent based design
Component based design
Percival Lucena
 
Atomic Design with Next.js
Atomic Design with Next.jsAtomic Design with Next.js
Atomic Design with Next.js
Patrick Smith
 
PEUDOM: a Platform for End User Development Of Mashups
PEUDOM: a Platform for End User Development Of MashupsPEUDOM: a Platform for End User Development Of Mashups
PEUDOM: a Platform for End User Development Of MashupsMatteo Picozzi
 
127801976 mobile-shop-management-system-documentation
127801976 mobile-shop-management-system-documentation127801976 mobile-shop-management-system-documentation
127801976 mobile-shop-management-system-documentation
Nitesh Kumar
 
Introduction to React JS.pptx
Introduction to React JS.pptxIntroduction to React JS.pptx
Introduction to React JS.pptx
SHAIKIRFAN715544
 
Managing Your Runtime With P2
Managing Your Runtime With P2Managing Your Runtime With P2
Managing Your Runtime With P2Pascal Rapicault
 
Data access layer and schema definitions
Data access layer and schema definitionsData access layer and schema definitions
Data access layer and schema definitionsLuciano Resende
 
Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...
Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...
Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...
Edureka!
 
Introduction to ReactJS UI Web Dev .pptx
Introduction to ReactJS UI Web Dev .pptxIntroduction to ReactJS UI Web Dev .pptx
Introduction to ReactJS UI Web Dev .pptx
SHAIKIRFAN715544
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js Simplified
Sunil Yadav
 
RIA Security - Broken By Design
RIA Security - Broken By DesignRIA Security - Broken By Design
RIA Security - Broken By Design
jojule
 
SharePoint Framework y React
SharePoint Framework y ReactSharePoint Framework y React
SharePoint Framework y React
SUGES (SharePoint Users Group España)
 
The Status Of Web Interoperability And Activities In China, Japan And Korea
The Status Of Web Interoperability And Activities In China, Japan And KoreaThe Status Of Web Interoperability And Activities In China, Japan And Korea
The Status Of Web Interoperability And Activities In China, Japan And Korea
Channy Yun
 
Functional Components in Vue.js
Functional Components in Vue.jsFunctional Components in Vue.js
Functional Components in Vue.js
Austin Gil
 

Similar to Lessons from a Rewrite (20)

We4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshell
We4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshellWe4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshell
We4IT LCTY 2013 - x-pages-men - ibm domino xpages - performance in a nutshell
 
DashMash: a Mashup Environment for End User Development
DashMash: a Mashup Environment for End User DevelopmentDashMash: a Mashup Environment for End User Development
DashMash: a Mashup Environment for End User Development
 
CSSDP Presentation April 09
CSSDP Presentation April 09CSSDP Presentation April 09
CSSDP Presentation April 09
 
Component based design
Component based designComponent based design
Component based design
 
Atomic Design with Next.js
Atomic Design with Next.jsAtomic Design with Next.js
Atomic Design with Next.js
 
PEUDOM: a Platform for End User Development Of Mashups
PEUDOM: a Platform for End User Development Of MashupsPEUDOM: a Platform for End User Development Of Mashups
PEUDOM: a Platform for End User Development Of Mashups
 
ER-カエルエックス
ER-カエルエックスER-カエルエックス
ER-カエルエックス
 
127801976 mobile-shop-management-system-documentation
127801976 mobile-shop-management-system-documentation127801976 mobile-shop-management-system-documentation
127801976 mobile-shop-management-system-documentation
 
Introduction to React JS.pptx
Introduction to React JS.pptxIntroduction to React JS.pptx
Introduction to React JS.pptx
 
Managing Your Runtime With P2
Managing Your Runtime With P2Managing Your Runtime With P2
Managing Your Runtime With P2
 
Data access layer and schema definitions
Data access layer and schema definitionsData access layer and schema definitions
Data access layer and schema definitions
 
Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...
Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...
Angular 4 Components | Angular 4 Tutorial For Beginners | Learn Angular 4 | E...
 
Introduction to ReactJS UI Web Dev .pptx
Introduction to ReactJS UI Web Dev .pptxIntroduction to ReactJS UI Web Dev .pptx
Introduction to ReactJS UI Web Dev .pptx
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js Simplified
 
RIA Security - Broken By Design
RIA Security - Broken By DesignRIA Security - Broken By Design
RIA Security - Broken By Design
 
speach
speachspeach
speach
 
SharePoint Framework y React
SharePoint Framework y ReactSharePoint Framework y React
SharePoint Framework y React
 
The Status Of Web Interoperability And Activities In China, Japan And Korea
The Status Of Web Interoperability And Activities In China, Japan And KoreaThe Status Of Web Interoperability And Activities In China, Japan And Korea
The Status Of Web Interoperability And Activities In China, Japan And Korea
 
Functional Components in Vue.js
Functional Components in Vue.jsFunctional Components in Vue.js
Functional Components in Vue.js
 
Ch19
Ch19Ch19
Ch19
 

More from Rebecca Murphey

Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS Apps
Rebecca Murphey
 
A New Baseline for Front-End Devs
A New Baseline for Front-End DevsA New Baseline for Front-End Devs
A New Baseline for Front-End Devs
Rebecca Murphey
 
Getting Started with Mulberry
Getting Started with MulberryGetting Started with Mulberry
Getting Started with MulberryRebecca Murphey
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitRebecca Murphey
 
Cleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQueryCleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQueryRebecca Murphey
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
Rebecca Murphey
 
The jQuery Divide
The jQuery DivideThe jQuery Divide
The jQuery Divide
Rebecca Murphey
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
Rebecca Murphey
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UIRebecca Murphey
 
Dojo Confessions
Dojo ConfessionsDojo Confessions
Dojo Confessions
Rebecca Murphey
 
Using Objects to Organize your jQuery Code
Using Objects to Organize your jQuery CodeUsing Objects to Organize your jQuery Code
Using Objects to Organize your jQuery Code
Rebecca Murphey
 
Jquery Fundamentals
Jquery FundamentalsJquery Fundamentals
Jquery Fundamentals
Rebecca Murphey
 

More from Rebecca Murphey (15)

Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS Apps
 
A New Baseline for Front-End Devs
A New Baseline for Front-End DevsA New Baseline for Front-End Devs
A New Baseline for Front-End Devs
 
BVJS
BVJSBVJS
BVJS
 
Getting Started with Mulberry
Getting Started with MulberryGetting Started with Mulberry
Getting Started with Mulberry
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
 
Introducing Mulberry
Introducing MulberryIntroducing Mulberry
Introducing Mulberry
 
Modern JavaScript
Modern JavaScriptModern JavaScript
Modern JavaScript
 
Cleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQueryCleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQuery
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
 
The jQuery Divide
The jQuery DivideThe jQuery Divide
The jQuery Divide
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UI
 
Dojo Confessions
Dojo ConfessionsDojo Confessions
Dojo Confessions
 
Using Objects to Organize your jQuery Code
Using Objects to Organize your jQuery CodeUsing Objects to Organize your jQuery Code
Using Objects to Organize your jQuery Code
 
Jquery Fundamentals
Jquery FundamentalsJquery Fundamentals
Jquery Fundamentals
 

Recently uploaded

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Nexer Digital
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
Thijs Feryn
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
nkrafacyberclub
 
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
Jen Stirrup
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
UiPath Community Day Dubai: AI at Work..
UiPath Community Day Dubai: AI at Work..UiPath Community Day Dubai: AI at Work..
UiPath Community Day Dubai: AI at Work..
UiPathCommunity
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
UiPathCommunity
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 

Recently uploaded (20)

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
 
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
UiPath Community Day Dubai: AI at Work..
UiPath Community Day Dubai: AI at Work..UiPath Community Day Dubai: AI at Work..
UiPath Community Day Dubai: AI at Work..
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 

Lessons from a Rewrite

  • 1. Lessons from a rewrite Rebecca Murphey • TXJS 2011 • Austin, Texas Saturday, June 11, 2011
  • 2. Rewrite projects are ... tempting because they always look easier than they actually are. If you want to survive a software rewrite, don’t throw away your existing software ... Write the code in such a way that it co-exists with your existing software, perhaps as an optional add-on that you can sell. James Shore Saturday, June 11, 2011
  • 3. Where a new system concept or new technology is used, one has to build a system to throw away, for even the best planning is not so omniscient as to get it right the rst time. Hence plan to throw one away; you will, anyhow. Fred Brooks Saturday, June 11, 2011
  • 4. Until you are able to prove that you have a viable market ... you shouldn’t be sinking a lot of time and money into implementation. Assuming you are [competent], you should be able to ... take on quite a bit of technical debt to build throwaway proof-of-concept versions of your product. Obie Fernandez Saturday, June 11, 2011
  • 5. ere’s a subtle reason that programmers always want to throw away the code and start over. e reason is that they think the old code is a mess. ... e reason that they think the old code is a mess is because of a cardinal, fundamental law of programming: It’s harder to read code than to write it. Joel Spolsky Saturday, June 11, 2011
  • 6. For the human makers of things, the incompleteness and inconsistencies of our ideas become clear only during implementation. Fred Brooks Saturday, June 11, 2011
  • 7. A content management system for the rapid creation of content-rich mobile applications. A PhoneGap & Dojo system that consumes the output of the CMS to generate the application. Saturday, June 11, 2011
  • 12. If data is required for a route, such as node data or the user's favorites, the route requests the data prior to creating the page. Data Page Controller Component Component Page Controller Component Component Component Component When the app is booted from a "cold" state (that URL Change Router Page Factory is, it's not fast-app- Page controllers are responsible for Components are responsible for Once components are placed on the switched), it goes receiving data from the Page Factory. Once receiving and rendering data and page, the Page Controller brokers through a bootstrapping The Router detects a The Route asks the page the page controller receives that data, it reacting to user interaction. communication between components. Route determines which components to display in process. This process change in the URL, looks factory to generate a page for a matching route in controller. If the page its postMixInProperties method and sets up Components whose content can change Components announce events either by ensures the application "placements," which define the components during a single page view should expose publishing a topic (for events that may toura.app.Routes, and controller is for a node, the is working with the that will be placed on the page and the data an API using setters (i.e. have app-wide significance) or by calling parses any additional Page Factory figures out most recent data, and parameters out of the URL which template (Audios, that needs to be passed to them. The _setContentAttr) that allows Page a method (such as onClick). Page also sets up various (such as the node ID, Images, etc.) the node actual instantiation and placement of Controllers to update their content. Controllers can subscribe to these application-wide Device Storage asset type, etc.). uses. The page factory components is handled in the postCreate topics or connect to these method calls creates an instance of the method of toura.pageControllers._Page, Alternately, a component can define an (much like connecting to events). This functionality, including page controller and hands which is inherited by all Page Controllers. attributeMap object that specifies how happens in the Page Controller's the Router, UI, Page the component should react when a postCreate method. the created instance back Factory, Data Store, to the Route. property is set. and several others. Once these pieces are in place, the app Remote triggers a URL change to the home node, and the process outlined here begins. Every time the user goes UI to a new page, we check the age of the local data; if it is more than 8 hours old, we see whether we need to do an over-the-air update. Browser Page Container Old Page Controller New Page Controller Component Component Component Component Component Component The Route asks toura.app.UI to place the Page Controller in the UI; in turn, toura.app.UI sets the content attribute of the Page Container. If there is already a page on screen, the Page Container handles the animation between the pages, and calls the destroy method of the old page, which results in the proper teardown of the old page and its components. Saturday, June 11, 2011
  • 13. e secret to building large apps is never build large apps. Break up your applications into small pieces. en, assemble those testable, bite-sized pieces into your big application. Justin Meyer Saturday, June 11, 2011
  • 15. } }], "parent": { "_reference": "node-365" }, "page_controller": "Audios1", "id": "node-369", "sharing_text": null, "sharing_url": null, "images": [{ "caption": { "_reference": "text-asset-60" }, "image": { "_reference": "image-174" } }, { "caption": { "_reference": "text-asset-61" }, "image": { "_reference": "image-182" Saturday, June 11, 2011 }
  • 16. backgroundImage : toura.models.BackgroundImage }, constructor : function(data) { this._loadData(data); }, _loadData : function(data) { this._store = new dojo.data.ItemFileReadStore({ data : { identifier : 'id', items : data }, hierarchical : false }); this.onLoadData(data); }, onLoadData : function(data) { // stub for connection }, get : function(id, type) { if (!id) { throw new Error('toura.app.Data::getModel requires an id'); Saturday, June 11, 2011 }
  • 18. store.fetch({ query : q, onComplete : addItems }); Saturday, June 11, 2011
  • 21. page controllers set up components and broker communication between them ui components receive and render data; announce and react to user interaction app components manage app data and state Saturday, June 11, 2011
  • 25. this.placements = [ [ 'VideoCaption', { node : this.node }, 'videoCaption' ], [ 'VideoList', { node : this.node }, 'videoList' ], [ 'VideoPlayer', { node : this.node }, 'videoPlayer' ] ]; Saturday, June 11, 2011
  • 26. this.connect(this.videoList, 'onSelect', function(assetId) { var video = this._videoById(assetId); this.videoCaption.set('content', video.caption || ''); this.videoPlayer.play(assetId); }); Saturday, June 11, 2011
  • 27. videoPlayer.set('mediaId', mediaId); _setMediaIdAttr : function(mediaId) { var media = this.media = this.mediasCache[mediaId]; if (this.useHtml5Player && !this.player) { this._queuedMedia = media; return; } this._queuedMedia = null; if (this.player) { this.player.src = media.url; } }, Saturday, June 11, 2011
  • 31. myComponent.set() to change state myComponent.on<Evt>() to announce state changes myComponent.connect() to listen for events & methods myComponent.subscribe() to react to published topics dojo.publish() to announce occurrences of app-wide interest Saturday, June 11, 2011
  • 32. Facilitating development Saturday, June 11, 2011
  • 33. /* {{^android}} */ var mediaPath = "www/media/" + toura.pages.currentId + "/"; /* {{/android}} */ /* {{#android}} */ var mediaPath = [Toura.getTouraPath(), toura.pages.currentId].join("/"); /* {{/android}} */ var imagesList = [], dimensionsList = [], namesList = [], thumbsList = []; var pos = -1, count = 0; /* {{#android}} */ var pos = 0, count = 0; /* {{/android}} */ Saturday, June 11, 2011
  • 34. toura.app._Config = { // ... device : { os : 'android', type : 'phone' } } Saturday, June 11, 2011
  • 35. toura.app.Has = function() { var device = toura.app.Config.get('device'); return { cssBackgroundContain : function() { return !( device.os === 'android' && device.version === '2-1' ); }, html5Player : function() { return device.os !== 'android'; }, iScrollZoom : function() { return device.os !== 'android'; } }; }; Saturday, June 11, 2011
  • 36. //>>excludeStart('production', kwArgs.production); if (toura.features.debugToolbar) { toura.app._Debug(); } //>>excludeEnd('production'); Saturday, June 11, 2011
  • 37. /** * This file generates built dojo and toura files for * use in production. */ dependencies = { /* * tell dojo to strip out ie-specific hacks */ webkitMobile : true, /* * this is a production build -- non-production code should be stripped */ production : true, /* * the name of the release and where to put it, * relative to dojo-release-X.X.X-src/util/buildscripts/. */ releaseName : 'production', /* * strip all calls to console.log automatically */ stripConsole : 'all', /* * clean the existing release directory, then create a new release Saturday, June 11, 2011 */
  • 38. dojo.provide('toura.app.Phonegap.network'); toura.app.Phonegap.network = function(pg, device) { var n = navigator; return { isReachable : function(domain) { console.log('toura.app.Phonegap.network::isReachable()'); var dfd = new dojo.Deferred(); if (n && n.network && n.network.isReachable) { console.log('using phonegap network'); n.network.isReachable( domain || 'aws.amazon.com', function(r) { dfd.resolve(r !== NetworkStatus.NOT_REACHABLE); }, function() { console.log('failure on network is reachable'); dfd.resolve(false); } ); } else { // resolve false if PhoneGap is present; // network is not indeed reachable if (pg) { dfd.resolve(false); Saturday, June 11, 2011 } else {
  • 39. Taming asynchronicity Saturday, June 11, 2011
  • 40. var myAsyncThing = function() { var dfd = new dojo.Deferred(); setTimeout(function() { dfd.resolve('hello'); }, 1000); return dfd.promise; }; myAsyncThing().then(function(result){ console.log(result); }); Saturday, June 11, 2011
  • 41. myPromise.then(win, fail) react to an immutable promise Saturday, June 11, 2011
  • 42. dojo.when(fn(), win, fail) react to maybe-asynchronous things, including promises Saturday, June 11, 2011
  • 43. /** * @private * @returns {Promise} A promise that, if resolved, will be resolved with the * remote data. */ _getRemoteData : function() { var dfd = new dojo.Deferred(); _xhr : function(url, dfd) { return dojo.xhrGet({ toura.app.Phonegap.network.isReachable() url : url, .then( preventCache : true, dojo.hitch(this, function() { handleAs : 'json', this._xhr(this.remoteDataUrl, dfd); contentType : false, }), load : dfd.resolve, function() { error : dfd.reject dfd.reject('Remote is not reachable'); }); } }, ); return dfd.promise; }, Saturday, June 11, 2011
  • 44. dojo.when(this._getAuth(), dojo.hitch(this, function(t) { this.token = t; this._postMessage(msg, t).then(dfd.resolve, dfd.reject); })); Saturday, June 11, 2011
  • 46. index.html#/node/123/image/456 index.html#/search/bacon index.html#/favorites index.html#/about Saturday, June 11, 2011
  • 47. { route : //node/(.*)/, handler : function(params, route) { var splat = params.splat[0].split('/'), nodeId = splat[0], pageState = { assetType : splat[1], assetId : splat[2], assetSubId : splat[3] }; return nodeRoute(route, nodeId, pageState); } }, { route : //search/?(.*)/, handler : function(params) { var page = toura.app.UI.getCurrentPage(), term = params.splat && params.splat[0].split('/')[0]; if (!page || !page.type || page.type !== 'search') { Saturday, June 11, 2011
  • 49. dojo.require('dojo.Stateful'); dojo.declare('toura.app.UI', [ dojo.Stateful ], { // ... }); Saturday, June 11, 2011
  • 50. this.watch('fontSize', function(k, oldSize, newSize) { var b = this.body; if (oldSize) { dojo.removeClass(b, oldSize); } dojo.addClass(b, newSize); toura.app.DeviceStorage.set('fontSize', newSize); }); Saturday, June 11, 2011
  • 52. Feature: A User can view a node with the Video1 template and play each video Scenario: Videos1: Setup When I am on the home node And I click "Plain Node" And I click "Video Player" Then I should see a Video Player And I should see an Asset List And I should see a Child Nodes list And I should see "movie_for_demo" in the asset list Scenario: Videos1: Controlling the video player from the asset list When I am on the home node And I click "Plain Node" And I click "Video Player" And I click "movie_for_demo" in the asset list Then "video-29" should be selected in the asset list And I should see "videos-29" in the video player Scenario: Navigating to a specific video When I am on the home node And I go to the URL "#/node/node-372/videos/video-29" Then I should see "videos-29" in the video player And "video-29" should be selected in the asset list Saturday, June 11, 2011
  • 53. expect(c.queryInput).toBeDefined(); expect(c.i18n_placeholderText).toBeTruthy(); }); }); it("should provide an API for setting the search term", function() { c = C(); c.set('searchTerm', term); expect(c.queryInput.value).toBe(term); }); it("should announce searches when the form is submitted with a term", function() { var submitHandler; toura.app.UI.hasTouch = false; c = C(); c.set('searchTerm', term); spyOn(c, 'search'); submitHandler = getEventHandlers(c, 'submit', c.searchForm)[0]; submitHandler(fakeEventObj); expect(c.search).toHaveBeenCalledWith(term); }); it("should not announce searches if the form is submitted with no term", function() { var submitHandler; toura.app.UI.hasTouch = false; c = C(); c.set('searchTerm', ''); spyOn(c, 'search'); Saturday, June 11, 2011 submitHandler = getEventHandlers(c, 'submit', c.searchForm)[0];
  • 54. It takes con dence to throw work away ... When people rst start drawing, they’re often reluctant to redo parts that aren’t right ... they convince themselves that the drawing is not that bad, really — in fact, maybe they meant it to look that way. Paul Graham Saturday, June 11, 2011
  • 55. http://pinboard.in/u:rmurphey/t:lessons-from-a-rewrite/ rebeccamurphey.com • blog.rebeccamurphey.com • @rmurphey Saturday, June 11, 2011