SlideShare uma empresa Scribd logo
1 de 55
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

Mais conteúdo relacionado

Semelhante a 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 DevelopmentMatteo Picozzi
 
CSSDP Presentation April 09
CSSDP Presentation April 09CSSDP Presentation April 09
CSSDP Presentation April 09Cybera Inc.
 
Atomic Design with Next.js
Atomic Design with Next.jsAtomic Design with Next.js
Atomic Design with Next.jsPatrick 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-documentationNitesh Kumar
 
Introduction to React JS.pptx
Introduction to React JS.pptxIntroduction to React JS.pptx
Introduction to React JS.pptxSHAIKIRFAN715544
 
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 .pptxSHAIKIRFAN715544
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js SimplifiedSunil Yadav
 
RIA Security - Broken By Design
RIA Security - Broken By DesignRIA Security - Broken By Design
RIA Security - Broken By Designjojule
 
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 KoreaChanny Yun
 
Functional Components in Vue.js
Functional Components in Vue.jsFunctional Components in Vue.js
Functional Components in Vue.jsAustin Gil
 

Semelhante a 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
 

Mais de 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 AppsRebecca 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 DevsRebecca 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 OrganizationRebecca Murphey
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery ApplicationsRebecca Murphey
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UIRebecca 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 CodeRebecca Murphey
 

Mais de 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
 

Último

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 

Último (20)

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 

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