PageRenderTime 314ms CodeModel.GetById 41ms RepoModel.GetById 3ms app.codeStats 2ms

/backbone-fundamentals.db

https://github.com/arkentos/backbone-fundamentals
Unknown | 10016 lines | 9502 code | 514 blank | 0 comment | 0 complexity | 3c9a05535dd9097fd5fcc98f1a7284f4 MD5 | raw file
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
  3. "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
  4. <article>
  5. <articleinfo>
  6. <title></title>
  7. </articleinfo>
  8. <sect1 id="prelude">
  9. <title>Prelude</title>
  10. <para>
  11. Welcome to my (in-progress) book about the
  12. <ulink url="http://documentcloud.github.com/backbone/">Backbone.js</ulink>
  13. framework for structuring JavaScript applications. Its released
  14. under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0
  15. Unported
  16. <ulink url="http://creativecommons.org/licenses/by-nc-sa/3.0/">license</ulink>
  17. meaning you can both grab a copy of the book for free or help to
  18. further
  19. <ulink url="https://github.com/addyosmani/backbone-fundamentals/">improve</ulink>
  20. it.
  21. </para>
  22. <para>
  23. Im very pleased to announce that this book will be out in physical
  24. form in a few months time via
  25. <ulink url="http://oreilly.com">OReilly Media</ulink>. Readers will
  26. have the option of purchasing the latest version in either print or
  27. a number of digital formats then or can grab a recent version from
  28. this repository.
  29. </para>
  30. <para>
  31. Corrections to existing material are always welcome and I hope that
  32. together we can provide the community with an up-to-date resource
  33. that is of help. My extended thanks go out to
  34. <ulink url="https://github.com/jashkenas">Jeremy Ashkenas</ulink>
  35. for creating Backbone.js and
  36. <ulink url="https://github.com/addyosmani/backbone-fundamentals/contributors">these</ulink>
  37. members of the community for their assistance tweaking this project.
  38. </para>
  39. <para>
  40. I hope you find this book helpful!
  41. </para>
  42. </sect1>
  43. <sect1 id="table-of-contents">
  44. <title>Table Of Contents</title>
  45. <itemizedlist>
  46. <listitem>
  47. </listitem>
  48. <listitem>
  49. <itemizedlist>
  50. <listitem>
  51. <para>
  52. <link linkend="mvc-mvp">MVC, MVP &amp; Backbone.js</link>
  53. </para>
  54. </listitem>
  55. </itemizedlist>
  56. </listitem>
  57. <listitem>
  58. <itemizedlist>
  59. <listitem>
  60. <para>
  61. <link linkend="models">Models</link>
  62. </para>
  63. </listitem>
  64. <listitem>
  65. <para>
  66. <link linkend="views">Views</link>
  67. </para>
  68. </listitem>
  69. <listitem>
  70. <para>
  71. <link linkend="collections">Collections</link>
  72. </para>
  73. </listitem>
  74. <listitem>
  75. <para>
  76. <link linkend="routers">Routers</link>
  77. </para>
  78. </listitem>
  79. <listitem>
  80. <para>
  81. <link linkend="namespacing">Namespacing</link>
  82. </para>
  83. </listitem>
  84. <listitem>
  85. <para>
  86. <link linkend="additional-tips">Additional tips</link>
  87. </para>
  88. </listitem>
  89. </itemizedlist>
  90. </listitem>
  91. <listitem>
  92. <itemizedlist>
  93. <listitem>
  94. <para>
  95. <link linkend="restful">Building RESTful applications with
  96. Backbone</link>
  97. </para>
  98. </listitem>
  99. <listitem>
  100. <para>
  101. <link linkend="stack1">Building Backbone apps with Node.js,
  102. Express, Mongoose and MongoDB</link>
  103. </para>
  104. </listitem>
  105. <listitem>
  106. <para>
  107. <link linkend="stack2">Building Backbone apps with Ruby,
  108. Sinatra, Haml and MongoDB</link>
  109. </para>
  110. </listitem>
  111. <listitem>
  112. <para>
  113. <link linkend="pagination">Paginating Backbone.js Requests
  114. &amp; Collections</link>
  115. </para>
  116. </listitem>
  117. </itemizedlist>
  118. </listitem>
  119. <listitem>
  120. <itemizedlist>
  121. <listitem>
  122. <para>
  123. <link linkend="modularjs">Modular JavaScript</link>
  124. </para>
  125. </listitem>
  126. <listitem>
  127. <para>
  128. <link linkend="organizingmodules">Organizing modules with
  129. RequireJS and AMD</link>
  130. </para>
  131. </listitem>
  132. <listitem>
  133. <para>
  134. <link linkend="externaltemplates">Keeping your templates
  135. external with the RequireJS text plugin</link>
  136. </para>
  137. </listitem>
  138. <listitem>
  139. <para>
  140. <link linkend="optimizingrequirejs">Optimizing Backbone apps
  141. for production with the RequireJS Optimizer</link>
  142. </para>
  143. </listitem>
  144. <listitem>
  145. <para>
  146. <link linkend="practicalrequirejs">Practical: Building a
  147. modular Backbone app with AMD &amp; RequireJS</link>
  148. </para>
  149. </listitem>
  150. <listitem>
  151. <para>
  152. <link linkend="decouplingbackbone">Decoupling Backbone with
  153. the Mediator and Facade patterns</link>
  154. </para>
  155. </listitem>
  156. <listitem>
  157. <para>
  158. Backbone &amp; jQuery Mobile
  159. </para>
  160. </listitem>
  161. <listitem>
  162. <para>
  163. Practical: Building A Modular Mobile App With Backbone &amp;
  164. jQuery Mobile
  165. </para>
  166. </listitem>
  167. </itemizedlist>
  168. </listitem>
  169. <listitem>
  170. <itemizedlist>
  171. <listitem>
  172. <para>
  173. <link linkend="unittestingjasmine">Unit Testing Backbone
  174. Applications With Jasmine</link>
  175. </para>
  176. </listitem>
  177. <listitem>
  178. <para>
  179. Introduction
  180. </para>
  181. </listitem>
  182. <listitem>
  183. <para>
  184. Jasmine
  185. </para>
  186. <itemizedlist>
  187. <listitem>
  188. <para>
  189. Suites, Specs And Spies
  190. </para>
  191. </listitem>
  192. <listitem>
  193. <para>
  194. TDD With Backbone
  195. </para>
  196. </listitem>
  197. <listitem>
  198. <para>
  199. <link linkend="testing-jasmine-models">Testing
  200. Models</link>
  201. </para>
  202. </listitem>
  203. <listitem>
  204. <para>
  205. <link linkend="testing-jasmine-collections">Testing
  206. Collections</link>
  207. </para>
  208. </listitem>
  209. <listitem>
  210. <para>
  211. <link linkend="testing-jasmine-views">Testing
  212. Views</link>
  213. </para>
  214. </listitem>
  215. </itemizedlist>
  216. </listitem>
  217. <listitem>
  218. <para>
  219. <link linkend="unittestingqunit">Unit Testing Backbone
  220. Applications With QUnit And SinonJS</link>
  221. </para>
  222. </listitem>
  223. <listitem>
  224. <para>
  225. Introduction
  226. </para>
  227. </listitem>
  228. <listitem>
  229. <para>
  230. QUnit
  231. </para>
  232. <itemizedlist>
  233. <listitem>
  234. <para>
  235. Assertions
  236. </para>
  237. </listitem>
  238. <listitem>
  239. <para>
  240. Adding structure to assertions
  241. </para>
  242. </listitem>
  243. <listitem>
  244. <para>
  245. Assertion examples
  246. </para>
  247. </listitem>
  248. <listitem>
  249. <para>
  250. Fixtures
  251. </para>
  252. </listitem>
  253. <listitem>
  254. <para>
  255. Asynchronous code
  256. </para>
  257. </listitem>
  258. </itemizedlist>
  259. </listitem>
  260. <listitem>
  261. <para>
  262. SinonJS
  263. </para>
  264. <itemizedlist>
  265. <listitem>
  266. <para>
  267. Stubs
  268. </para>
  269. </listitem>
  270. <listitem>
  271. <para>
  272. Mocks
  273. </para>
  274. </listitem>
  275. </itemizedlist>
  276. </listitem>
  277. <listitem>
  278. <para>
  279. Practical
  280. </para>
  281. <itemizedlist>
  282. <listitem>
  283. <para>
  284. Testing Models
  285. </para>
  286. </listitem>
  287. <listitem>
  288. <para>
  289. Testing Collections
  290. </para>
  291. </listitem>
  292. <listitem>
  293. <para>
  294. Testing Views
  295. </para>
  296. </listitem>
  297. <listitem>
  298. <para>
  299. Testing Events
  300. </para>
  301. </listitem>
  302. </itemizedlist>
  303. </listitem>
  304. </itemizedlist>
  305. </listitem>
  306. <listitem>
  307. </listitem>
  308. </itemizedlist>
  309. </sect1>
  310. <sect1 id="introduction">
  311. <title><a name="introduction">Introduction</a></title>
  312. <para>
  313. As JavaScript developers, we are at an interesting point in time
  314. where not only do we have mature solutions to help organize the
  315. JavaScript powering our applications based on a separation of
  316. concerns, but developers looking to build non-trivial projects are
  317. almost spoiled for choice for frameworks that can help structure
  318. their applications.
  319. </para>
  320. <para>
  321. Maturity in software (framework) development isnt simply about how
  322. long a framework has been around. Its about how solid the framework
  323. is and more importantly how well its evolved to fill its role. Has
  324. it become more effective at solving common problems? Does it
  325. continue to improve as developers build larger and more complex
  326. applications with it?
  327. </para>
  328. <para>
  329. In this book, I will be covering the popular Backbone.js, which I
  330. consider the best of the current family of JavaScript architectural
  331. frameworks.
  332. </para>
  333. <para>
  334. Topics will include MVC theory and how to build applications using
  335. Backbones models, views, collections and routers. Ill also be
  336. taking you through advanced topics like modular development with
  337. Backbone.js and AMD (via RequireJS), how to build applications using
  338. modern software stacks (like Node and Express), how to solve the
  339. routing problems with Backbone and jQuery Mobile, tips about
  340. scaffolding tools, and a lot more.
  341. </para>
  342. <para>
  343. If this is your first time looking at Backbone.js and youre still
  344. unsure whether or not to give it a try, why not take a look at how
  345. <ulink url="http://github.com/addyosmani/todomvc">a Todo
  346. application</ulink> can be implemented in Backbone and several other
  347. popular Javascript frameworks before reading further?
  348. </para>
  349. <para>
  350. The goal of this book is to create an authoritative and centralized
  351. repository of information that can help those developing real-world
  352. apps with Backbone. If you come across a section or topic which you
  353. think could be improved or expanded on, please feel free to submit a
  354. pull-request. It wont take long and youll be helping other
  355. developers avoid problems youve run into before.
  356. </para>
  357. </sect1>
  358. <sect1 id="fundamentals">
  359. <title><a name="fundamentals">Fundamentals</a></title>
  360. <para>
  361. In this section we are going to cover the context into which a
  362. framework like Backbone.js fits. Lets begin our journey into
  363. understanding Backbone better with a look at code architecture.
  364. </para>
  365. <sect2 id="mvc-mvp-backbone.js">
  366. <title><a name="mvc-mvp">MVC, MVP &amp; Backbone.js</a></title>
  367. <para>
  368. Before exploring any JavaScript frameworks that assist in
  369. structuring applications, it can be useful to gain a basic
  370. understanding of architectural design patterns. Design patterns
  371. are proven solutions to common development problems and can
  372. suggest structural approaches to help guide developers in adding
  373. some organization to their applications.
  374. </para>
  375. <para>
  376. Patterns are useful because theyre a set of practices that build
  377. upon the collective experience of skilled developers who have
  378. repeatedly solved similar problems. Although developers 10 or 20
  379. years ago may not have been using the same programming languages
  380. when implementing patterns in their projects, there are many
  381. lessons we can learn from their efforts.
  382. </para>
  383. <para>
  384. In this section, were going to review two popular patterns - MVC
  385. and MVP. Well be exploring in greater detail how Backbone.js
  386. implements these patterns shortly to better appreciate where it
  387. fits in.
  388. </para>
  389. </sect2>
  390. </sect1>
  391. <sect1 id="mvc">
  392. <title>MVC</title>
  393. <para>
  394. MVC (Model-View-Controller) is an architectural design pattern that
  395. encourages improved application organization through a separation of
  396. concerns. It enforces the isolation of business data (Models) from
  397. user interfaces (Views), with a third component (Controllers)
  398. traditionally present to manage logic, user-input and the
  399. coordination of models and views. The pattern was originally
  400. designed by
  401. <ulink url="http://en.wikipedia.org/wiki/Trygve_Reenskaug">Trygve
  402. Reenskaug</ulink> while working on Smalltalk-80 (1979), where it was
  403. initially called Model-View-Controller-Editor. MVC was described in
  404. depth in
  405. <ulink url="http://www.amazon.co.uk/Design-patterns-elements-reusable-object-oriented/dp/0201633612"><quote>Design
  406. Patterns: Elements of Reusable Object-Oriented
  407. Software</quote></ulink> (The <quote>GoF</quote> or <quote>Gang of
  408. Four</quote> book) in 1994, which played a role in popularizing its
  409. use.
  410. </para>
  411. <sect2 id="smalltalk-80-mvc">
  412. <title>Smalltalk-80 MVC</title>
  413. <para>
  414. Its important to understand what the original MVC pattern was
  415. aiming to solve as it has changed quite heavily since the days of
  416. its origin. Back in the 70s, graphical user-interfaces were far
  417. and few between. An approach known as
  418. <ulink url="http://martinfowler.com/eaaDev/uiArchs.html">Separated
  419. Presentation</ulink> began to be used as a means to make a clear
  420. division between domain objects which modeled concepts in the real
  421. world (e.g a photo, a person) and the presentation objects which
  422. were rendered to the users screen.
  423. </para>
  424. <para>
  425. The Smalltalk-80 implementation of MVC took this concept further
  426. and had an objective of separating out the application logic from
  427. the user interface. The idea was that decoupling these parts of
  428. the application would also allow the reuse of models for other
  429. interfaces in the application. There are some interesting points
  430. worth noting about Smalltalk-80s MVC architecture:
  431. </para>
  432. <itemizedlist>
  433. <listitem>
  434. <para>
  435. A Domain element was known as a Model and were ignorant of the
  436. user-interface (Views and Controllers)
  437. </para>
  438. </listitem>
  439. <listitem>
  440. <para>
  441. Presentation was taken care of by the View and the Controller,
  442. but there wasnt just a single view and controller. A
  443. View-Controller pair was required for each element being
  444. displayed on the screen and so there was no true separation
  445. between them
  446. </para>
  447. </listitem>
  448. <listitem>
  449. <para>
  450. The Controllers role in this pair was handling user input
  451. (such as key-presses and click events), doing something
  452. sensible with them.
  453. </para>
  454. </listitem>
  455. <listitem>
  456. <para>
  457. The Observer pattern was relied upon for updating the View
  458. whenever the Model changed
  459. </para>
  460. </listitem>
  461. </itemizedlist>
  462. <para>
  463. Developers are sometimes surprised when they learn that the
  464. Observer pattern (nowadays commonly implemented as a
  465. Publish/Subscribe system) was included as a part of MVCs
  466. architecture decades ago. In Smalltalk-80s MVC, the View and
  467. Controller both observe the Model: anytime the Model changes, the
  468. Views react. A simple example of this is an application backed by
  469. stock market data - for the application to show real-time
  470. information, any change to the data in its Models should result in
  471. the View being refreshed instantly.
  472. </para>
  473. <para>
  474. Martin Fowler has done an excellent job of writing about the
  475. <ulink url="http://martinfowler.com/eaaDev/uiArchs.html">origins</ulink>
  476. of MVC over the years and if you are interested in further
  477. historical information about Smalltalk-80s MVC, I recommend
  478. reading his work.
  479. </para>
  480. </sect2>
  481. </sect1>
  482. <sect1 id="mvc-as-we-know-it">
  483. <title>MVC As We Know It</title>
  484. <para>
  485. Weve reviewed the 70s, but let us now return to the here and now.
  486. The MVC pattern has been applied to a diverse range of programming
  487. languages. For example, the popular Ruby on Rails is an
  488. implementation of a web application framework based on MVC for the
  489. Ruby language. JavaScript now has a number of MVC frameworks,
  490. including Ember.js, JavaScriptMVC, and of course Backbone.js. Given
  491. the importance of avoiding <quote>spaghetti</quote> code, a term
  492. which describes code that is very difficult to read or maintain due
  493. to its lack of structure, lets look at what the MVC pattern enables
  494. the Javascript developer to do.
  495. </para>
  496. <para>
  497. MVC is composed of three core components:
  498. </para>
  499. <sect2 id="models">
  500. <title>Models</title>
  501. <para>
  502. Models manage the data for an application. They are concerned with
  503. neither the user-interface nor presentation layers, but instead
  504. represent structured data that an application may require. When a
  505. model changes (e.g when it is updated), it will typically notify
  506. its observers (e.g views, a concept we will cover shortly) that a
  507. change has occurred so that they may react accordingly.
  508. </para>
  509. <para>
  510. To understand models better, let us imagine we have a JavaScript
  511. photo gallery application. In a photo gallery, a photo would merit
  512. its own model, as it represents a unique kind of domain-specific
  513. data. The Photo model may represent attributes such as a caption,
  514. image source and additional meta-data. A specific photo would be
  515. stored in an instance of a model. Heres an example of a simple
  516. Photo model implemented with Backbone.js:
  517. </para>
  518. <programlisting language="javascript">
  519. var Photo = Backbone.Model.extend({
  520. // Default attributes for the photo
  521. defaults: {
  522. // Ensure that each photo created has an `src`.
  523. src: &quot;placeholder.jpg&quot;,
  524. caption: &quot;A default image&quot;,
  525. viewed: false
  526. },
  527. initialize: function() {
  528. }
  529. });
  530. </programlisting>
  531. <para>
  532. The built-in capabilities of models vary across frameworks,
  533. however its common for them to support validation of attributes,
  534. where attributes represent the properties of the model, such as a
  535. model identifier. When using models in real-world applications we
  536. generally also need a way of persisting models. Persistence allows
  537. us to edit and update models with the knowledge that their most
  538. recent states will be saved somewhere, for example in a web
  539. browsers localStorage data-store or synchronized with a database.
  540. </para>
  541. <para>
  542. A model may also have multiple views observing it. Imagine our
  543. Photo model contained meta-data such as the longitude and latitude
  544. where the photo was taken, a list of people present in the photo,
  545. and a list of tags. A developer could create a single view that
  546. displayed all these attributes, or might create three separate
  547. views to display each attribute. The important detail is that the
  548. Photo model doesnt care how these views are organized, it simply
  549. announces updates to its data as necessary. Well come back to
  550. Views in more detail later.
  551. </para>
  552. <para>
  553. It is not uncommon for modern MVC/MV* frameworks to provide a
  554. means to group models together. In Backbone, these groups are
  555. called <quote>Collections</quote>. Managing models in groups
  556. allows us to write application logic based on notifications from
  557. the group, should any model it contains change. This avoids the
  558. need to manually observe individual model instances.
  559. </para>
  560. <para>
  561. Heres how we might group Photo models into a simplified Backbone
  562. Collection:
  563. </para>
  564. <programlisting language="javascript">
  565. var PhotoGallery = Backbone.Collection.extend({
  566. // Reference to this collection's model.
  567. model: Photo,
  568. // Filter down the list of all photos that have been viewed
  569. viewed: function() {
  570. return this.filter(function(photo){ return photo.get('viewed'); });
  571. },
  572. // Filter down the list to only photos that have not yet been viewed
  573. unviewed: function() {
  574. return this.without.apply(this, this.viewed());
  575. }
  576. });
  577. </programlisting>
  578. <para>
  579. If you read older texts on MVC, you may come across a description
  580. of models as also managing application <quote>state</quote>. In
  581. JavaScript applications <quote>state</quote> has a specific
  582. meaning, typically referring to the current <quote>state</quote>
  583. of a view or sub-view on a users screen at a fixed time. State is
  584. a topic which is regularly discussed when looking at Single-page
  585. applications, where the concept of state needs to be simulated.
  586. </para>
  587. </sect2>
  588. <sect2 id="views">
  589. <title>Views</title>
  590. <para>
  591. Views are a visual representation of models that present a
  592. filtered view of their current state. A view typically observes a
  593. model and is notified when the model changes, allowing the view to
  594. update itself accordingly. Design pattern literature commonly
  595. refers to views as <quote>dumb</quote>, given that their knowledge
  596. of models and controllers in an application is limited.
  597. </para>
  598. <para>
  599. Users interact with views, which usually means reading and editing
  600. model data. For example, in our photo gallery application example,
  601. model viewing might happen in a user interface with a big image, a
  602. caption, and a list of tags. Model editing could be done through
  603. an <quote>edit</quote> view where a user who has selected a
  604. specific photo could edit its caption, tags, or other metadata in
  605. a form.
  606. </para>
  607. <para>
  608. In MVC, the actual task of updating the Model falls to
  609. Controllers, which well be covering shortly.
  610. </para>
  611. <para>
  612. Lets explore Views a little further using a simple JavaScript
  613. example. Below we can see a function that creates a single Photo
  614. view, consuming both a model instance and a controller instance.
  615. </para>
  616. <para>
  617. We define a <literal>render()</literal> utility within our view
  618. which is responsible for rendering the contents of the
  619. <literal>photoModel</literal> using a JavaScript templating engine
  620. (Underscore templating) and updating the contents of our view,
  621. referenced by <literal>photoEl</literal>.
  622. </para>
  623. <para>
  624. The <literal>photoModel</literal> then adds our
  625. <literal>render()</literal> callback as one of its subscribers, so
  626. that through the Observer pattern it can trigger the view to
  627. update when the model changes.
  628. </para>
  629. <para>
  630. You may wonder where user interaction comes into play here. When
  631. users click on any elements within the view, its not the views
  632. responsibility to know what to do next. A Controller makes this
  633. decision. In our sample implementation, this is achieved by adding
  634. an event listener to <literal>photoEl</literal> which will
  635. delegate handling the click behavior back to the controller,
  636. passing the model information along with it in case its needed.
  637. </para>
  638. <para>
  639. The benefit of this architecture is that each component plays its
  640. own separate role in making the application function as needed.
  641. </para>
  642. <programlisting language="javascript">
  643. var buildPhotoView = function( photoModel, photoController ){
  644. var base = document.createElement('div'),
  645. photoEl = document.createElement('div');
  646. base.appendChild(photoEl);
  647. var render= function(){
  648. // We use a templating library such as Underscore
  649. // templating which generates the HTML for our
  650. // photo entry
  651. photoEl.innerHTML = _.template('photoTemplate', {src: photoModel.getSrc()});
  652. }
  653. photoModel.addSubscriber( render );
  654. photoEl.addEventListener('click', function(){
  655. photoController.handleEvent('click', photoModel );
  656. });
  657. var show = function(){
  658. photoEl.style.display = '';
  659. }
  660. var hide = function(){
  661. photoEl.style.display = 'none';
  662. }
  663. return{
  664. showView: show,
  665. hideView: hide
  666. }
  667. }
  668. </programlisting>
  669. <para>
  670. <emphasis role="strong">Templating</emphasis>
  671. </para>
  672. <para>
  673. In the context of JavaScript frameworks that support MVC/MV*, it
  674. is worth looking more closely at JavaScript templating and its
  675. relationship to Views.
  676. </para>
  677. <para>
  678. It has long been considered bad practice (and computationally
  679. expensive) to manually create large blocks of HTML markup
  680. in-memory through string concatenation. Developers using this
  681. technique often find themselves iterating through their data,
  682. wrapping it in nested divs and using outdated techniques such as
  683. <literal>document.write</literal> to inject the
  684. <quote>template</quote> into the DOM. This approach often means
  685. keeping scripted markup inline with standard markup, which can
  686. quickly become difficult to read and maintain, especially when
  687. building large applications.
  688. </para>
  689. <para>
  690. JavaScript templating libraries (such as Handlebars.js or
  691. Mustache) are often used to define templates for views as HTML
  692. markup containing template variables. These template blocks can be
  693. either stored externally or within script tags with a custom type
  694. (e.g <quote>text/template</quote>). Variables are deliminated
  695. using a variable syntax (e.g {{name}}). Javascript template
  696. libraries typically accept data in JSON, and the grunt work of
  697. populating templates with data is taken care of by the framework
  698. itself. This has a several benefits, particularly when opting to
  699. store templates externally as this can let applications load
  700. templates dynamically on an as-needed basis.
  701. </para>
  702. <para>
  703. Lets compare two examples of HTML templates. One is implemented
  704. using the popular Handlebars.js library, and the other uses
  705. Underscores <quote>microtemplates</quote>.
  706. </para>
  707. <para>
  708. <emphasis role="strong">Handlebars.js:</emphasis>
  709. </para>
  710. <programlisting language="html">
  711. &lt;li class=&quot;photo&quot;&gt;
  712. &lt;h2&gt;{{caption}}&lt;/h2&gt;
  713. &lt;img class=&quot;source&quot; src=&quot;{{src}}&quot;/&gt;
  714. &lt;div class=&quot;meta-data&quot;&gt;
  715. {{metadata}}
  716. &lt;/div&gt;
  717. &lt;/li&gt;
  718. </programlisting>
  719. <para>
  720. <emphasis role="strong">Underscore.js Microtemplates:</emphasis>
  721. </para>
  722. <programlisting language="html">
  723. &lt;li class=&quot;photo&quot;&gt;
  724. &lt;h2&gt;&lt;%= caption %&gt;&lt;/h2&gt;
  725. &lt;img class=&quot;source&quot; src=&quot;&lt;%= src %&gt;&quot;/&gt;
  726. &lt;div class=&quot;meta-data&quot;&gt;
  727. &lt;%= metadata %&gt;
  728. &lt;/div&gt;
  729. &lt;/li&gt;
  730. </programlisting>
  731. <para>
  732. You may also use double curly brackets (i.e
  733. <literal>{{}}</literal>) (or any other tag you feel comfortable
  734. with) in Microtemplates. In the case of curly brackets, this can
  735. be done by setting the Underscore
  736. <literal>templateSettings</literal> attribute as follows:
  737. </para>
  738. <programlisting language="javascript">
  739. _.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };
  740. </programlisting>
  741. <para>
  742. <emphasis role="strong">A note on navigation and state</emphasis>
  743. </para>
  744. <para>
  745. It is also worth noting that in classical web development,
  746. navigating between independent views required the use of a page
  747. refresh. In single-page JavaScript applications, however, once
  748. data is fetched from a server via Ajax, it can be dynamically
  749. rendered in a new view within the same page. Since this doesnt
  750. automatically update the URL, the role of navigation thus falls to
  751. a <quote>router</quote>, which assists in managing application
  752. state (e.g allowing users to bookmark a particular view they have
  753. navigated to). As routers are however neither a part of MVC nor
  754. present in every MVC-like framework, I will not be going into them
  755. in greater detail in this section.
  756. </para>
  757. </sect2>
  758. <sect2 id="controllers">
  759. <title>Controllers</title>
  760. <para>
  761. Controllers are an intermediary between models and views which are
  762. classically responsible for two tasks: they both update the view
  763. when the model changes and update the model when the user
  764. manipulates the view.
  765. </para>
  766. <para>
  767. In our photo gallery application, a controller would be
  768. responsible for handling changes the user made to the edit view
  769. for a particular photo, updating a specific photo model when a
  770. user has finished editing.
  771. </para>
  772. <para>
  773. Its with controllers that most JavaScript MVC frameworks depart
  774. from this interpretation of the MVC pattern. The reasons for this
  775. vary, but in my opinion, Javascript framework authors likely
  776. initially looked at server-side interpretations of MVC (such as
  777. Ruby on Rails), realized that that approach didnt translate 1:1
  778. on the client-side, and so re-interpreted the C in MVC to solve
  779. their state management problem. This was a clever approach, but it
  780. can make it hard for developers coming to MVC for the first time
  781. to understand both the classical MVC pattern and the
  782. <quote>proper</quote> role of controllers in other non-Javascript
  783. frameworks.
  784. </para>
  785. <para>
  786. So does Backbone.js have Controllers? Not really. Backbones Views
  787. typically contain <quote>controller</quote> logic, and Routers
  788. (discussed below) are used to help manage application state, but
  789. neither are true Controllers according to classical MVC.
  790. </para>
  791. <para>
  792. In this respect, contrary to what might be mentioned in the
  793. official documentation or in blog posts, Backbone is neither a
  794. truly MVC/MVP nor MVVM framework. Its in fact better to see it a
  795. member of the MV* family which approaches architecture in its own
  796. way. There is of course nothing wrong with this, but it is
  797. important to distinguish between classical MVC and MV* should you
  798. be relying on discussions of MVC to help with your Backbone
  799. projects.
  800. </para>
  801. </sect2>
  802. <sect2 id="controllers-in-spine.js-vs-backbone.js">
  803. <title>Controllers in Spine.js vs Backbone.js</title>
  804. <para>
  805. <emphasis role="strong">Spine.js</emphasis>
  806. </para>
  807. <para>
  808. We now know that controllers are traditionally responsible for
  809. updating the view when the model changes (and similarly the model
  810. when the user updates the view). Since Backbone doesnt have its
  811. <emphasis role="strong">own</emphasis> explicit controllers, its
  812. useful to review the controller from another MVC framework to
  813. appreciate the difference in implementations. Lets take a look at
  814. <ulink url="http://spinejs.com/">Spine.js</ulink>:
  815. </para>
  816. <para>
  817. In this example, were going to have a controller called
  818. <literal>PhotosController</literal> which will be in charge of
  819. individual photos in the application. It will ensure that when the
  820. view updates (e.g a user edited the photo meta-data) the
  821. corresponding model does too.
  822. </para>
  823. <para>
  824. (Note: We wont be delving heavily into Spine.js beyond this
  825. example, but its worth looking at it to learn more about
  826. Javascript frameworks in general.)
  827. </para>
  828. <programlisting language="javascript">
  829. // Controllers in Spine are created by inheriting from Spine.Controller
  830. var PhotosController = Spine.Controller.sub({
  831. init: function(){
  832. this.item.bind(&quot;update&quot;, this.proxy(this.render));
  833. this.item.bind(&quot;destroy&quot;, this.proxy(this.remove));
  834. },
  835. render: function(){
  836. // Handle templating
  837. this.replace($(&quot;#photoTemplate&quot;).tmpl(this.item));
  838. return this;
  839. },
  840. remove: function(){
  841. this.el.remove();
  842. this.release();
  843. }
  844. });
  845. </programlisting>
  846. <para>
  847. In Spine, controllers are considered the glue for an application,
  848. adding and responding to DOM events, rendering templates and
  849. ensuring that views and models are kept in sync (which makes sense
  850. in the context of what we know to be a controller).
  851. </para>
  852. <para>
  853. What were doing in the above example is setting up listeners in
  854. the <literal>update</literal> and <literal>destroy</literal>
  855. events using <literal>render()</literal> and
  856. <literal>remove()</literal>. When a photo entry gets updated, we
  857. re-render the view to reflect the changes to the meta-data.
  858. Similarly, if the photo gets deleted from the gallery, we remove
  859. it from the view. In case you were wondering about the
  860. <literal>tmpl()</literal> function in the code snippet: in the
  861. <literal>render()</literal> function, were using this to render a
  862. JavaScript template called #photoTemplate which simply returns a
  863. HTML string used to replace the controllers current element.
  864. </para>
  865. <para>
  866. What this provides us with is a very lightweight, simple way to
  867. manage changes between the model and the view.
  868. </para>
  869. <para>
  870. <emphasis role="strong">Backbone.js</emphasis>
  871. </para>
  872. <para>
  873. Later on in this section were going to revisit the differences
  874. between Backbone and traditional MVC, but for now lets focus on
  875. controllers.
  876. </para>
  877. <para>
  878. In Backbone, controller logic is shared between Backbone.View and
  879. Backbone.Router. Earlier releases of Backbone contained something
  880. called Backbone.Controller, but it was renamed to Router to
  881. clarify its role.
  882. </para>
  883. <para>
  884. A Routers main purpose is to translate URL requests into
  885. application states. When a user browses to the URL
  886. www.example.com/photos/42, a Router could be used to show the
  887. photo with that ID, and to define what application behavior should
  888. be run in response to that request. Routers
  889. <emphasis>can</emphasis> contain traditional controller
  890. responsibilities, such as binding the events between models and
  891. views, or rendering parts of the page. However, Backbone
  892. contributor Tim Branyen has pointed out that its possible to get
  893. away without needing Backbone.Router at all for this, so a way to
  894. think about it using the Router paradigm is probably:
  895. </para>
  896. <programlisting language="javascript">
  897. var PhotoRouter = Backbone.Router.extend({
  898. routes: { &quot;photos/:id&quot;: &quot;route&quot; },
  899. route: function(id) {
  900. var item = photoCollection.get(id);
  901. var view = new PhotoView({ model: item });
  902. something.html( view.render().el );
  903. }
  904. }):
  905. </programlisting>
  906. </sect2>
  907. </sect1>
  908. <sect1 id="what-does-mvc-give-us">
  909. <title>What does MVC give us?</title>
  910. <para>
  911. To summarize, the separation of concerns in MVC facilitates
  912. modularization of an applications functionality and enables:
  913. </para>
  914. <itemizedlist>
  915. <listitem>
  916. <para>
  917. Easier overall maintenance. When updates need to be made to the
  918. application it is clear whether the changes are data-centric,
  919. meaning changes to models and possibly controllers, or merely
  920. visual, meaning changes to views.<literallayout></literallayout>
  921. </para>
  922. </listitem>
  923. <listitem>
  924. <para>
  925. Decoupling models and views means that its straight-forward to
  926. write unit tests for business
  927. logic<literallayout></literallayout>
  928. </para>
  929. </listitem>
  930. <listitem>
  931. <para>
  932. Duplication of low-level model and controller code is eliminated
  933. across the application
  934. </para>
  935. </listitem>
  936. <listitem>
  937. <para>
  938. Depending on the size of the application and separation of
  939. roles, this modularity allows developers responsible for core
  940. logic and developers working on the user-interfaces to work
  941. simultaneously
  942. </para>
  943. </listitem>
  944. </itemizedlist>
  945. <sect2 id="delving-deeper">
  946. <title>Delving deeper</title>
  947. <para>
  948. Right now, you likely have a basic understanding of what the MVC
  949. pattern provides, but for the curious, well explore it a little
  950. further.
  951. </para>
  952. <para>
  953. The GoF (Gang of Four) do not refer to MVC as a design pattern,
  954. but rather consider it a <quote>set of classes to build a user
  955. interface</quote>. In their view, its actually a variation of
  956. three other classical design patterns: the Observer (Pub/Sub),
  957. Strategy and Composite patterns. Depending on how MVC has been
  958. implemented in a framework, it may also use the Factory and
  959. Decorator patterns. Ive covered some of these patterns in my
  960. other free book, JavaScript Design Patterns For Beginners if you
  961. would like to read into them further.
  962. </para>
  963. <para>
  964. As weve discussed, models represent application data, while views
  965. handle what the user is presented on screen. As such, MVC relies
  966. on Pub/Sub for some of its core communication (something that
  967. surprisingly isnt covered in many articles about the MVC
  968. pattern). When a model is changed it <quote>publishes</quote> to
  969. the rest of the application that it has been updated. The
  970. <quote>subscriber</quote>generally a Controllerthen updates the
  971. view accordingly. The observer-viewer nature of this relationship
  972. is what facilitates multiple views being attached to the same
  973. model.
  974. </para>
  975. <para>
  976. For developers interested in knowing more about the decoupled
  977. nature of MVC (once again, depending on the implementation), one
  978. of the goals of the pattern is to help define one-to-many
  979. relationships between a topic and its observers. When a topic
  980. changes, its observers are updated. Views and controllers have a
  981. slightly different relationship. Controllers facilitate views to
  982. respond to different user input and are an example of the Strategy
  983. pattern.
  984. </para>
  985. </sect2>
  986. <sect2 id="summary">
  987. <title>Summary</title>
  988. <para>
  989. Having reviewed the classical MVC pattern, your should now
  990. understand how it allows developers to cleanly separate concerns
  991. in an application. You should also now appreciate how JavaScript
  992. MVC frameworks may differ in their interpretation of MVC, and how
  993. they share some of the fundamental concepts of the original
  994. pattern.
  995. </para>
  996. <para>
  997. When reviewing a new JavaScript MVC/MV* framework, remember - it
  998. can be useful to step back and consider how its opted to approach
  999. Models, Views, Controllers or other alternatives, as this can
  1000. better help you grok how the framework expects to be used.
  1001. </para>
  1002. </sect2>
  1003. </sect1>
  1004. <sect1 id="mvp">
  1005. <title>MVP</title>
  1006. <para>
  1007. Model-view-presenter (MVP) is a derivative of the MVC design pattern
  1008. which focuses on improving presentation logic. It originated at a
  1009. company named
  1010. <ulink url="http://en.wikipedia.org/wiki/Taligent">Taligent</ulink>
  1011. in the early 1990s while they were working on a model for a C++
  1012. CommonPoint environment. Whilst both MVC and MVP target the
  1013. separation of concerns across multiple components, there are some
  1014. fundamental differences between them.
  1015. </para>
  1016. <para>
  1017. For the purposes of this summary we will focus on the version of MVP
  1018. most suitable for web-based architectures.
  1019. </para>
  1020. <sect2 id="models-views-presenters">
  1021. <title>Models, Views &amp; Presenters</title>
  1022. <para>
  1023. The P in MVP stands for presenter. Its a component which contains
  1024. the user-interface business logic for the view. Unlike MVC,
  1025. invocations from the view are delegated to the presenter, which
  1026. are decoupled from the view and instead talk to it through an
  1027. interface. This allows for all kinds of useful things such as
  1028. being able to mock views in unit tests.
  1029. </para>
  1030. <para>
  1031. The most common implementation of MVP is one which uses a Passive
  1032. View (a view which is for all intents and purposes
  1033. <quote>dumb</quote>), containing little to no logic. MVP models
  1034. are almost identical to MVC models and handle application data.
  1035. The presenter acts as a mediator which talks to both the view and
  1036. model, however both of these are isolated from each other. They
  1037. effectively bind models to views, a responsibility held by
  1038. Controllers in MVC. Presenters are at the heart of the MVP pattern
  1039. and as you can guess, incorporate the presentation logic behind
  1040. views.
  1041. </para>
  1042. <para>
  1043. Solicited by a view, presenters perform any work to do with user
  1044. requests and pass data back to them. In this respect, they
  1045. retrieve data, manipulate it and determine how the data should be
  1046. displayed in the view. In some implementations, the presenter also
  1047. interacts with a service layer to persist data (models). Models
  1048. may trigger events but its the presenters role to subscribe to
  1049. them so that it can update the view. In this passive architecture,
  1050. we have no concept of direct data binding. Views expose setters
  1051. which presenters can use to set data.
  1052. </para>
  1053. <para>
  1054. The benefit of this change from MVC is that it increases the
  1055. testability of your application and provides a more clean
  1056. separation between the view and the model. This isnt however
  1057. without its costs as the lack of data binding support in the
  1058. pattern can often mean having to take care of this task
  1059. separately.
  1060. </para>
  1061. <para>
  1062. Although a common implementation of a
  1063. <ulink url="http://martinfowler.com/eaaDev/PassiveScreen.html">Passive
  1064. View</ulink> is for the view to implement an interface, there are
  1065. variations on it, including the use of events which can decouple
  1066. the View from the Presenter a little more. As we dont have the
  1067. interface construct in JavaScript, were using it more as more a
  1068. protocol than an explicit interface here. Its technically still
  1069. an API and its probably fair for us to refer to it as an
  1070. interface from that perspective.
  1071. </para>
  1072. <para>
  1073. There is also a
  1074. <ulink url="http://martinfowler.com/eaaDev/SupervisingPresenter.html">Supervising
  1075. Controller</ulink> variation of MVP, which is closer to the MVC
  1076. and
  1077. <ulink url="http://en.wikipedia.org/wiki/Model_View_ViewModel">MVVM</ulink>
  1078. patterns as it provides data-binding from the Model directly from
  1079. the View. Key-value observing (KVO) plugins (such as Derick
  1080. Baileys Backbone.ModelBinding plugin) introduce this idea of a
  1081. Supervising Controller to Backbone.
  1082. </para>
  1083. </sect2>
  1084. </sect1>
  1085. <sect1 id="mvp-or-mvc">
  1086. <title>MVP or MVC?</title>
  1087. <para>
  1088. MVP is generally used most often in enterprise-level applications
  1089. where its necessary to reuse as much presentation logic as
  1090. possible. Applications with very complex views and a great deal of
  1091. user interaction may find that MVC doesnt quite fit the bill here
  1092. as solving this problem may mean heavily relying on multiple
  1093. controllers. In MVP, all of this complex logic can be encapsulated
  1094. in a presenter, which can simplify maintenance greatly.
  1095. </para>
  1096. <para>
  1097. As MVP views are defined through an interface and the interface is
  1098. technically the only point of contact between the system and the
  1099. view (other than a presenter), this pattern also allows developers
  1100. to write presentation logic without needing to wait for designers to
  1101. produce layouts and graphics for the application.
  1102. </para>
  1103. <para>
  1104. Depending on the implementation, MVP may be more easy to
  1105. automatically unit test than MVC. The reason often cited for this is
  1106. that the presenter can be used as a complete mock of the
  1107. user-interface and so it can be unit tested independent of other
  1108. components. In my experience this really depends on the languages
  1109. you are implementing MVP in (theres quite a difference between
  1110. opting for MVP for a JavaScript project over one for say, ASP.net).
  1111. </para>
  1112. <para>
  1113. At the end of the day, the underlying concerns you may have with MVC
  1114. will likely hold true for MVP given that the differences between
  1115. them are mainly semantic. As long as you are cleanly separating
  1116. concerns into models, views and controllers (or presenters) you
  1117. should be achieving most of the same benefits regardless of the
  1118. pattern you opt for.
  1119. </para>
  1120. </sect1>
  1121. <sect1 id="mvc-mvp-and-backbone.js">
  1122. <title>MVC, MVP and Backbone.js</title>
  1123. <para>
  1124. There are very few, if any architectural JavaScript frameworks that
  1125. claim to implement the MVC or MVP patterns in their classical form
  1126. as many JavaScript developers dont view MVC and MVP as being
  1127. mutually exclusive (we are actually more likely to see MVP strictly
  1128. implemented when looking at web frameworks such as ASP.net or GWT).
  1129. This is because its possible to have additional presenter/view
  1130. logic in your application and yet still consider it a flavor of MVC.
  1131. </para>
  1132. <para>
  1133. Backbone contributor <ulink url="http://ireneros.com/">Irene
  1134. Ros</ulink> subscribes to this way of thinking as when she separates
  1135. Backbone views out into their own distinct components, she needs
  1136. something to actually assemble them for her. This could either be a
  1137. controller route (such as a <literal>Backbone.Router</literal>,
  1138. covered later in the book) or a callback in response to data being
  1139. fetched.
  1140. </para>
  1141. <para>
  1142. That said, some developers do however feel that Backbone.js better
  1143. fits the description of MVP than it does MVC . Their view is that:
  1144. </para>
  1145. <itemizedlist>
  1146. <listitem>
  1147. <para>
  1148. The presenter in MVP better describes the
  1149. <literal>Backbone.View</literal> (the layer between View
  1150. templates and the data bound to it) than a controller does
  1151. </para>
  1152. </listitem>
  1153. <listitem>
  1154. <para>
  1155. The model fits <literal>Backbone.Model</literal> (it isnt that
  1156. different from the classical MVC
  1157. <quote>Model</quote>)<literallayout></literallayout>
  1158. </para>
  1159. </listitem>
  1160. <listitem>
  1161. <para>
  1162. The views best represent templates (e.g Handlebars/Mustache
  1163. markup templates)
  1164. </para>
  1165. </listitem>
  1166. </itemizedlist>
  1167. <para>
  1168. A response to this could be that the view can also just be a View
  1169. (as per MVC) because Backbone is flexible enough to let it be used
  1170. for multiple purposes. The V in MVC and the P in MVP can both be
  1171. accomplished by <literal>Backbone.View</literal> because theyre
  1172. able to achieve two purposes: both rendering atomic components and
  1173. assembling those components rendered by other views.
  1174. </para>
  1175. <para>
  1176. Weve also seen that in Backbone the responsibility of a controller
  1177. is shared with both the Backbone.View and Backbone.Router and in the
  1178. following example we can actually see that aspects of that are
  1179. certainly true.
  1180. </para>
  1181. <para>
  1182. Here, our Backbone <literal>PhotoView</literal> uses the Observer
  1183. pattern to <quote>subscribe</quote> to changes to a Views model in
  1184. the line <literal>this.model.bind('change',...)</literal>. It also
  1185. handles templating in the <literal>render()</literal> method, but
  1186. unlike some other implementations, user interaction is also handled
  1187. in the View (see <literal>events</literal>).
  1188. </para>
  1189. <programlisting language="javascript">
  1190. var PhotoView = Backbone.View.extend({
  1191. //... is a list tag.
  1192. tagName: &quot;li&quot;,
  1193. // Pass the contents of the photo template through a templating
  1194. // function, cache it for a single photo
  1195. template: _.template($('#photo-template').html()),
  1196. // The DOM events specific to an item.
  1197. events: {
  1198. &quot;click img&quot; : &quot;toggleViewed&quot;
  1199. },
  1200. // The PhotoView listens for changes to its model, re-rendering. Since there's
  1201. // a one-to-one correspondence between a **Photo** and a **PhotoView** in this
  1202. // app, we set a direct reference on the model for convenience.
  1203. initialize: function() {
  1204. _.bindAll(this, 'render');
  1205. this.model.bind('change', this.render);
  1206. this.model.bind('destroy', this.remove);
  1207. },
  1208. // Re-render the photo entry
  1209. render: function() {
  1210. $(this.el).html(this.template(this.model.toJSON()));
  1211. return this;
  1212. },
  1213. // Toggle the `&quot;viewed&quot;` state of the model.
  1214. toggleViewed: function() {
  1215. this.model.viewed();
  1216. }
  1217. });
  1218. </programlisting>
  1219. <para>
  1220. Another (quite different) opinion is that Backbone more closely
  1221. resembles
  1222. <ulink url="http://martinfowler.com/eaaDev/uiArchs.html#ModelViewController">Smalltalk-80
  1223. MVC</ulink>, which we went through earlier.
  1224. </para>
  1225. <para>
  1226. As regular Backbone user Derick Bailey has
  1227. <ulink url="http://lostechies.com/derickbailey/2011/12/23/backbone-js-is-not-an-mvc-framework/">written</ulink>,
  1228. its ultimately best not to force Backbone to fit any specific
  1229. design patterns. Design patterns should be considered flexible
  1230. guides to how applications may be structured and in this respect,
  1231. Backbone doesnt fit either MVC nor MVP perfectly. Instead, it
  1232. borrows some of the best concepts from multiple architectural
  1233. patterns and creates a flexible framework that just works well. Call
  1234. it <emphasis role="strong">the Backbone way</emphasis>, MV* or
  1235. whatever helps reference its flavor of application architecture.
  1236. </para>
  1237. <para>
  1238. It <emphasis>is</emphasis> however worth understanding where and why
  1239. these concepts originated, so I hope that my explanations of MVC and
  1240. MVP have been of help. Most structural JavaScript frameworks will
  1241. adopt their own take on classical patterns, either intentionally or
  1242. by accident, but the important thing is that they help us develop
  1243. applications which are organized, clean and can be easily
  1244. maintained.
  1245. </para>
  1246. </sect1>
  1247. <sect1 id="fast-facts">
  1248. <title>Fast facts</title>
  1249. <sect2 id="backbone.js">
  1250. <title>Backbone.js</title>
  1251. <itemizedlist>
  1252. <listitem>
  1253. <para>
  1254. Core components: Model, View, Collection, Router. Enforces its
  1255. own flavor of MV*
  1256. </para>
  1257. </listitem>
  1258. <listitem>
  1259. <para>
  1260. Good documentation, with more improvements on the way
  1261. </para>
  1262. </listitem>
  1263. <listitem>
  1264. <para>
  1265. Used by large companies such as SoundCloud and Foursquare to
  1266. build non-trivial applications
  1267. </para>
  1268. </listitem>
  1269. <listitem>
  1270. <para>
  1271. Event-driven communication between views and models. As well
  1272. see, its relatively straight-forward to add event listeners
  1273. to any attribute in a model, giving developers fine-grained
  1274. control over what changes in the view
  1275. </para>
  1276. </listitem>
  1277. <listitem>
  1278. <para>
  1279. Supports data bindings through manual events or a separate
  1280. Key-value observing (KVO) library
  1281. </para>
  1282. </listitem>
  1283. <listitem>
  1284. <para>
  1285. Great support for RESTful interfaces out of the box, so models
  1286. can be easily tied to a backend
  1287. </para>
  1288. </listitem>
  1289. <listitem>
  1290. <para>
  1291. Extensive eventing system. Its
  1292. <ulink url="http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/">trivial</ulink>
  1293. to add support for pub/sub in Backbone
  1294. </para>
  1295. </listitem>
  1296. <listitem>
  1297. <para>
  1298. Prototypes are instantiated with the <literal>new</literal>
  1299. keyword, which some developers prefer
  1300. </para>
  1301. </listitem>
  1302. <listitem>
  1303. <para>
  1304. Agnostic about templating frameworks, however Underscores
  1305. micro-templating is available by default. Backbone works well
  1306. with libraries like Handlebars
  1307. </para>
  1308. </listitem>
  1309. <listitem>
  1310. <para>
  1311. Doesnt support deeply nested models, though there are
  1312. Backbone plugins such as
  1313. <ulink url="https://github.com/PaulUithol/Backbone-relational">this</ulink>
  1314. which can help<literallayout></literallayout>
  1315. </para>
  1316. </listitem>
  1317. <listitem>
  1318. <para>
  1319. Clear and flexible conventions for structuring applications.
  1320. Backbone doesnt force usage of all of its components and can
  1321. work with only those needed.
  1322. </para>
  1323. </listitem>
  1324. </itemizedlist>
  1325. </sect2>
  1326. </sect1>
  1327. <sect1 id="the-basics">
  1328. <title>## <a name="thebasics">The Basics</a></title>
  1329. <sect2 id="what-is-backbone">
  1330. <title>What is Backbone?</title>
  1331. <para>
  1332. Backbone.js is one of a number of JavaScript frameworks for
  1333. creating MVC-like web applications. On the front-end, its my
  1334. architectural framework of choice as its both mature, relatively
  1335. lightweight and can be easily tested using third-party toolkits
  1336. such as Jasmine or QUnit. Other MVC frameworks you may be familiar
  1337. with include Ember.js (SproutCore 2.0), Spine, YUILibrary and
  1338. JavaScriptMVC.
  1339. </para>
  1340. <para>
  1341. Backbone is maintained by a number of contributors, most notably:
  1342. Jeremy Ashkenas, creator of CoffeeScript, Docco and Underscore.js.
  1343. As Jeremy is a believer in detailed documentation, theres a level
  1344. of comfort in knowing youre unlikely to run into issues which are
  1345. either not explained in the official docs or which cant be nailed
  1346. down with some assistance from the #documentcloud IRC channel. I
  1347. strongly recommend using the latter if you find yourself getting
  1348. stuck.
  1349. </para>
  1350. </sect2>
  1351. <sect2 id="why-should-you-consider-using-it">
  1352. <title>Why should you consider using it?</title>
  1353. <para>
  1354. Backbones main benefits, regardless of your target platform or
  1355. device, include helping:
  1356. </para>
  1357. <itemizedlist>
  1358. <listitem>
  1359. <para>
  1360. Organize the structure to your application
  1361. </para>
  1362. </listitem>
  1363. <listitem>
  1364. <para>
  1365. Simplify server-side persistence
  1366. </para>
  1367. </listitem>
  1368. <listitem>
  1369. <para>
  1370. Decouple the DOM from your pages data
  1371. </para>
  1372. </listitem>
  1373. <listitem>
  1374. <para>
  1375. Model data, views and routers in a succinct manner
  1376. </para>
  1377. </listitem>
  1378. <listitem>
  1379. <para>
  1380. Provide DOM, model and collection synchronization
  1381. </para>
  1382. </listitem>
  1383. </itemizedlist>
  1384. </sect2>
  1385. </sect1>
  1386. <sect1 id="the-basics-1">
  1387. <title>The Basics</title>
  1388. <para>
  1389. In this section, youll learn the essentials of Backbones models,
  1390. views, collections and routers, as well as about using namespacing
  1391. to organize your code. This isnt meant as a replacement for the
  1392. official documentation, but it will help you understand many of the
  1393. core concepts behind Backbone before you start building applications
  1394. with it.
  1395. </para>
  1396. <itemizedlist>
  1397. <listitem>
  1398. <para>
  1399. Models
  1400. </para>
  1401. </listitem>
  1402. <listitem>
  1403. <para>
  1404. Collections
  1405. </para>
  1406. </listitem>
  1407. <listitem>
  1408. <para>
  1409. Routers
  1410. </para>
  1411. </listitem>
  1412. <listitem>
  1413. <para>
  1414. Views
  1415. </para>
  1416. </listitem>
  1417. <listitem>
  1418. <para>
  1419. Namespacing
  1420. </para>
  1421. </listitem>
  1422. </itemizedlist>
  1423. <sect2 id="models-1">
  1424. <title><a name="models">Models</a></title>
  1425. <para>
  1426. Backbone models contain interactive data for an application as
  1427. well as the logic around this data. For example, we can use a
  1428. model to represent the concept of a photo object including its
  1429. attributes like tags, titles and a location.
  1430. </para>
  1431. <para>
  1432. Models can be created by extending
  1433. <literal>Backbone.Model</literal> as follows:
  1434. </para>
  1435. <programlisting language="javascript">
  1436. var Photo = Backbone.Model.extend({
  1437. defaults: {
  1438. src: 'placeholder.jpg',
  1439. title: 'an image placeholder',
  1440. coordinates: [0,0]
  1441. },
  1442. initialize: function(){
  1443. this.bind(&quot;change:src&quot;, function(){
  1444. var src = this.get(&quot;src&quot;);
  1445. console.log('Image source updated to ' + src);
  1446. });
  1447. },
  1448. changeSrc: function( source ){
  1449. this.set({ src: source });
  1450. }
  1451. });
  1452. var somePhoto = new Photo({ src: &quot;test.jpg&quot;, title:&quot;testing&quot;});
  1453. somePhoto.changeSrc(&quot;magic.jpg&quot;); // which triggers &quot;change:src&quot; and logs an update message to the console.
  1454. </programlisting>
  1455. <sect3 id="initialization">
  1456. <title>Initialization</title>
  1457. <para>
  1458. The <literal>initialize()</literal> method is called when a new
  1459. instance of a model is created. Its use is optional, however
  1460. youll see why its good practice to use it below.
  1461. </para>
  1462. <programlisting language="javascript">
  1463. var Photo = Backbone.Model.extend({
  1464. initialize: function(){
  1465. console.log('this model has been initialized');
  1466. }
  1467. });
  1468. // We can then create our own instance of a photo as follows:
  1469. var myPhoto = new Photo();
  1470. </programlisting>
  1471. </sect3>
  1472. <sect3 id="getters-setters">
  1473. <title>Getters &amp; Setters</title>
  1474. <para>
  1475. <emphasis role="strong">Model.get()</emphasis>
  1476. </para>
  1477. <para>
  1478. <literal>Model.get()</literal> provides easy access to a models
  1479. attributes. Attributes which are passed through to the model on
  1480. instantiation are instantly available for retrieval.
  1481. </para>
  1482. <programlisting language="javascript">
  1483. var myPhoto = new Photo({ title: &quot;My awesome photo&quot;,
  1484. src:&quot;boston.jpg&quot;,
  1485. location: &quot;Boston&quot;,
  1486. tags:['the big game', 'vacation']}),
  1487. title = myPhoto.get(&quot;title&quot;), //My awesome photo
  1488. location = myPhoto.get(&quot;location&quot;), //Boston
  1489. tags = myPhoto.get(&quot;tags&quot;), // ['the big game','vacation']
  1490. photoSrc = myPhoto.get(&quot;src&quot;); //boston.jpg
  1491. </programlisting>
  1492. <para>
  1493. Alternatively, if you wish to directly access all of the
  1494. attributes in a models instance directly, you can achieve this
  1495. as follows:
  1496. </para>
  1497. <programlisting language="javascript">
  1498. var myAttributes = myPhoto.attributes;
  1499. console.log(myAttributes);
  1500. </programlisting>
  1501. <para>
  1502. It is best practice to use <literal>Model.set()</literal> or
  1503. direct instantiation to set the values of a models attributes.
  1504. </para>
  1505. <para>
  1506. Accessing <literal>Model.attributes</literal> directly is
  1507. generally discouraged. Instead, should you need to read or clone
  1508. data, <literal>Model.toJSON()</literal> is recommended for this
  1509. purpose. If you would like to access or copy a models
  1510. attributes for purposes such as JSON stringification (e.g. for
  1511. serialization prior to being passed to a view), this can be
  1512. achieved using <literal>Model.toJSON()</literal>:
  1513. </para>
  1514. <programlisting language="javascript">
  1515. var myAttributes = myPhoto.toJSON();
  1516. console.log(myAttributes);
  1517. /* this returns { title: &quot;My awesome photo&quot;,
  1518. src:&quot;boston.jpg&quot;,
  1519. location: &quot;Boston&quot;,
  1520. tags:['the big game', 'vacation']}*/
  1521. </programlisting>
  1522. </sect3>
  1523. <sect3 id="model.set">
  1524. <title>Model.set()</title>
  1525. <para>
  1526. <literal>Model.set()</literal> allows us to pass attributes into
  1527. an instance of our model. Attributes can either be set during
  1528. initialization or at any time afterwards. Its important to
  1529. avoid trying to set a Models attributes directly (for example
  1530. Model.caption = <quote>A new caption</quote>). Backbone uses
  1531. Model.set() to know when to broadcast that a models data has
  1532. changed.
  1533. </para>
  1534. <programlisting language="javascript">
  1535. var Photo = Backbone.Model.extend({
  1536. initialize: function(){
  1537. console.log('this model has been initialized');
  1538. }
  1539. });
  1540. // Setting the value of attributes via instantiation
  1541. var myPhoto = new Photo({ title: 'My awesome photo', location: 'Boston' });
  1542. var myPhoto2 = new Photo();
  1543. // Setting the value of attributes through Model.set()
  1544. myPhoto2.set({ title:'Vacation in Florida', location: 'Florida' });
  1545. </programlisting>
  1546. <para>
  1547. <emphasis role="strong">Default values</emphasis>
  1548. </para>
  1549. <para>
  1550. There are times when you want your model to have a set of
  1551. default values (e.g. in a scenario where a complete set of data
  1552. isnt provided by the user). This can be set using a property
  1553. called <literal>defaults</literal> in your model.
  1554. </para>
  1555. <programlisting language="javascript">
  1556. var Photo = Backbone.Model.extend({
  1557. defaults:{
  1558. title: 'Another photo!',
  1559. tags: ['untagged'],
  1560. location: 'home',
  1561. src: 'placeholder.jpg'
  1562. },
  1563. initialize: function(){
  1564. }
  1565. });
  1566. var myPhoto = new Photo({ location: &quot;Boston&quot;,
  1567. tags:['the big game', 'vacation']}),
  1568. title = myPhoto.get(&quot;title&quot;), //Another photo!
  1569. location = myPhoto.get(&quot;location&quot;), //Boston
  1570. tags = myPhoto.get(&quot;tags&quot;), // ['the big game','vacation']
  1571. photoSrc = myPhoto.get(&quot;src&quot;); //placeholder.jpg
  1572. </programlisting>
  1573. <para>
  1574. <emphasis role="strong">Listening for changes to your
  1575. model</emphasis>
  1576. </para>
  1577. <para>
  1578. Any and all of the attributes in a Backbone model can have
  1579. listeners bound to them which detect when their values change.
  1580. Listeners can be added to the <literal>initialize()</literal>
  1581. function:
  1582. </para>
  1583. <programlisting language="javascript">
  1584. this.bind('change', function(){
  1585. console.log('values for this model have changed');
  1586. });
  1587. </programlisting>
  1588. <para>
  1589. In the following example, we log a message whenever a specific
  1590. attribute (the title of our Photo model) is altered.
  1591. </para>
  1592. <programlisting language="javascript">
  1593. var Photo = Backbone.Model.extend({
  1594. defaults:{
  1595. title: 'Another photo!',
  1596. tags: ['untagged'],
  1597. location: 'home',
  1598. src: 'placeholder.jpg'
  1599. },
  1600. initialize: function(){
  1601. console.log('this model has been initialized');
  1602. this.bind(&quot;change:title&quot;, function(){
  1603. var title = this.get(&quot;title&quot;);
  1604. console.log(&quot;My title has been changed to.. &quot; + title);
  1605. });
  1606. },
  1607. setTitle: function(newTitle){
  1608. this.set({ title: newTitle });
  1609. }
  1610. });
  1611. var myPhoto = new Photo({ title:&quot;Fishing at the lake&quot;, src:&quot;fishing.jpg&quot;});
  1612. myPhoto.setTitle('Fishing at sea');
  1613. //logs 'My title has been changed to.. Fishing at sea'
  1614. </programlisting>
  1615. <para>
  1616. <emphasis role="strong">Validation</emphasis>
  1617. </para>
  1618. <para>
  1619. Backbone supports model validation through
  1620. <literal>Model.validate()</literal>, which allows checking the
  1621. attribute values for a model prior to them being set.
  1622. </para>
  1623. <para>
  1624. Validation functions can be as simple or complex as necessary.
  1625. If the attributes provided are valid, nothing should be returned
  1626. from <literal>.validate()</literal>. If they are invalid, a
  1627. custom error can be returned instead.
  1628. </para>
  1629. <para>
  1630. A basic example for validation can be seen below:
  1631. </para>
  1632. <programlisting language="javascript">
  1633. var Photo = Backbone.Model.extend({
  1634. validate: function(attribs){
  1635. if(attribs.src === undefined){
  1636. return &quot;Remember to set a source for your image!&quot;;
  1637. }
  1638. },
  1639. initialize: function(){
  1640. console.log('this model has been initialized');
  1641. this.bind(&quot;error&quot;, function(model, error){
  1642. console.log(error);
  1643. });
  1644. }
  1645. });
  1646. var myPhoto = new Photo();
  1647. myPhoto.set({ title: &quot;On the beach&quot; });
  1648. //logs Remember to set a source for your image!
  1649. </programlisting>
  1650. </sect3>
  1651. </sect2>
  1652. <sect2 id="views-1">
  1653. <title><a name="views">Views</a></title>
  1654. <para>
  1655. Views in Backbone dont contain the markup for your application,
  1656. but rather they are there to support models by defining the logic
  1657. for how they should be represented to the user. This is usually
  1658. achieved using JavaScript templating (e.g. Mustache, jQuery-tmpl,
  1659. etc.). A views <literal>render()</literal> function can be bound
  1660. to a models <literal>change()</literal> event, allowing the view
  1661. to always be up to date without requiring a full page refresh.
  1662. </para>
  1663. <sect3 id="creating-new-views">
  1664. <title>Creating new views</title>
  1665. <para>
  1666. Similar to the previous sections, creating a new view is
  1667. relatively straight-forward. To create a new View, simply extend
  1668. <literal>Backbone.View</literal>. Ill explain this code in
  1669. detail below:
  1670. </para>
  1671. <programlisting language="javascript">
  1672. var PhotoSearch = Backbone.View.extend({
  1673. el: $('#results'),
  1674. render: function( event ){
  1675. var compiled_template = _.template( $(&quot;#results-template&quot;).html() );
  1676. this.el.html( compiled_template(this.model.toJSON()) );
  1677. return this; //recommended as this enables calls to be chained.
  1678. },
  1679. events: {
  1680. &quot;submit #searchForm&quot;: &quot;search&quot;,
  1681. &quot;click .reset&quot;: &quot;reset&quot;,
  1682. &quot;click .advanced&quot;: &quot;switchContext&quot;
  1683. },
  1684. search: function( event ){
  1685. //executed when a form '#searchForm' has been submitted
  1686. },
  1687. reset: function( event ){
  1688. //executed when an element with class &quot;reset&quot; has been clicked.
  1689. },
  1690. switchContext: function( event ){
  1691. //executed when an element with class &quot;advanced&quot; has been clicked.
  1692. }
  1693. });
  1694. </programlisting>
  1695. </sect3>
  1696. <sect3 id="what-is-el">
  1697. <title>What is <literal>el</literal>?</title>
  1698. <para>
  1699. <literal>el</literal> is basically a reference to a DOM element
  1700. and all views must have one. It allows for all of the contents
  1701. of a view to be inserted into the DOM at once, which makes for
  1702. faster rendering as browser performs the minimum required
  1703. reflows and repaints.
  1704. </para>
  1705. <para>
  1706. There are two ways to attach a DOM element to a view: the
  1707. element already exists in the page or a new element is created
  1708. for the view and added manually by the developer. If the element
  1709. already exists in the page, you can set <literal>el</literal> as
  1710. either a CSS selector that matches the element or a simple
  1711. reference to the DOM element.
  1712. </para>
  1713. <programlisting language="javascript">
  1714. el: '#footer',
  1715. // OR
  1716. el: document.getElementById( 'footer' )
  1717. </programlisting>
  1718. <para>
  1719. If you want to create a new element for you view, set any
  1720. combination of the following views properties:
  1721. <literal>tagName</literal>, <literal>id</literal> and
  1722. <literal>className</literal>. A new element will be created for
  1723. you by the framework and a reference to it will be available at
  1724. the <literal>el</literal> property.
  1725. </para>
  1726. <programlisting>
  1727. tagName: 'p', // required, but defaults to 'div' if not set
  1728. className: 'container', // optional, you can assign multiple classes to this property like so 'container homepage'
  1729. id: 'header', // optional
  1730. </programlisting>
  1731. <para>
  1732. The above code creates the <literal>DOMElement</literal> below
  1733. but doesnt append it to the DOM.
  1734. </para>
  1735. <programlisting>
  1736. &lt;p id=&quot;header&quot; class=&quot;container&quot;&gt;&lt;/p&gt;
  1737. </programlisting>
  1738. <para>
  1739. <emphasis role="strong">Understanding
  1740. <literal>render()</literal></emphasis>
  1741. </para>
  1742. <para>
  1743. <literal>render()</literal> is an optional function that defines
  1744. the logic for rendering a template. Well use Underscores
  1745. micro-templating in these examples, but remember you can use
  1746. other templating frameworks if you prefer.
  1747. </para>
  1748. <para>
  1749. The <literal>_.template</literal> method in Underscore compiles
  1750. JavaScript templates into functions which can be evaluated for
  1751. rendering. In the above view, Im passing the markup from a
  1752. template with id <literal>results-template</literal> to
  1753. <literal>_.template()</literal> to be compiled. Next, I set the
  1754. html of the <literal>el</literal> DOM element to the output of
  1755. processing a JSON version of the model associated with the view
  1756. through the compiled template.
  1757. </para>
  1758. <para>
  1759. Presto! This populates the template, giving you a data-complete
  1760. set of markup in just a few short lines of code.
  1761. </para>
  1762. <para>
  1763. <emphasis role="strong">The <literal>events</literal>
  1764. attribute</emphasis>
  1765. </para>
  1766. <para>
  1767. The Backbone <literal>events</literal> attribute allows us to
  1768. attach event listeners to either custom selectors, or directly
  1769. to <literal>el</literal> if no selector is provided. An event
  1770. takes the form
  1771. <literal>{&quot;eventName selector&quot;: &quot;callbackFunction&quot;}</literal>
  1772. and a number of event-types are supported, including
  1773. <literal>click</literal>, <literal>submit</literal>,
  1774. <literal>mouseover</literal>, <literal>dblclick</literal> and
  1775. more.
  1776. </para>
  1777. <para>
  1778. What isnt instantly obvious is that under the bonnet, Backbone
  1779. uses jQuerys <literal>.delegate()</literal> to provide instant
  1780. support for event delegation but goes a little further,
  1781. extending it so that <literal>this</literal> always refers to
  1782. the current view object. The only thing to really keep in mind
  1783. is that any string callback supplied to the events attribute
  1784. must have a corresponding function with the same name within the
  1785. scope of your view.
  1786. </para>
  1787. </sect3>
  1788. </sect2>
  1789. <sect2 id="collections">
  1790. <title><a name="collections">Collections</a></title>
  1791. <para>
  1792. Collections are sets of Models and are created by extending
  1793. <literal>Backbone.Collection</literal>.
  1794. </para>
  1795. <para>
  1796. Normally, when creating a collection youll also want to pass
  1797. through a property specifying the model that your collection will
  1798. contain, as well as any instance properties required.
  1799. </para>
  1800. <para>
  1801. In the following example, we create a PhotoCollection that will
  1802. contain our Photo models:
  1803. </para>
  1804. <programlisting language="javascript">
  1805. var PhotoCollection = Backbone.Collection.extend({
  1806. model: Photo
  1807. });
  1808. </programlisting>
  1809. <para>
  1810. <emphasis role="strong">Getters and Setters</emphasis>
  1811. </para>
  1812. <para>
  1813. There are a few different ways to retrieve a model from a
  1814. collection. The most straight-forward is to use
  1815. <literal>Collection.get()</literal> which accepts a single id as
  1816. follows:
  1817. </para>
  1818. <programlisting language="javascript">
  1819. var skiingEpicness = PhotoCollection.get(2);
  1820. </programlisting>
  1821. <para>
  1822. Sometimes you may also want to get a model based on its client id.
  1823. The client id is a property that Backbone automatically assigns
  1824. models that have not yet been saved. You can get a models client
  1825. id from its <literal>.cid</literal> property.
  1826. </para>
  1827. <programlisting language="javascript">
  1828. var mySkiingCrash = PhotoCollection.getByCid(456);
  1829. </programlisting>
  1830. <para>
  1831. Backbone Collections dont have setters as such, but do support
  1832. adding new models via <literal>.add()</literal> and removing
  1833. models via <literal>.remove()</literal>.
  1834. </para>
  1835. <programlisting language="javascript">
  1836. var a = new Backbone.Model({ title: 'my vacation'}),
  1837. b = new Backbone.Model({ title: 'my holiday'});
  1838. var photoCollection = new PhotoCollection([a,b]);
  1839. photoCollection.remove([a,b]);
  1840. </programlisting>
  1841. <para>
  1842. <emphasis role="strong">Listening for events</emphasis>
  1843. </para>
  1844. <para>
  1845. As collections represent a group of items, were also able to
  1846. listen for <literal>add</literal> and <literal>remove</literal>
  1847. events for when new models are added or removed from the
  1848. collection. Heres an example:
  1849. </para>
  1850. <programlisting language="javascript">
  1851. var PhotoCollection = new Backbone.Collection();
  1852. PhotoCollection.bind(&quot;add&quot;, function(photo) {
  1853. console.log(&quot;I liked &quot; + photo.get(&quot;title&quot;) + ' its this one, right? ' + photo.get(&quot;src&quot;));
  1854. });
  1855. PhotoCollection.add([
  1856. {title: &quot;My trip to Bali&quot;, src: &quot;bali-trip.jpg&quot;},
  1857. {title: &quot;The flight home&quot;, src: &quot;long-flight-oofta.jpg&quot;},
  1858. {title: &quot;Uploading pix&quot;, src: &quot;too-many-pics.jpg&quot;}
  1859. ]);
  1860. </programlisting>
  1861. <para>
  1862. In addition, were able to bind a <literal>change</literal> event
  1863. to listen for changes to models in the collection.
  1864. </para>
  1865. <programlisting language="javascript">
  1866. PhotoCollection.bind(&quot;change:title&quot;, function(){
  1867. console.log('there have been updates made to this collections titles');
  1868. });
  1869. </programlisting>
  1870. <para>
  1871. <emphasis role="strong">Fetching models from the server</emphasis>
  1872. </para>
  1873. <para>
  1874. <literal>Collections.fetch()</literal> retrieves a default set of
  1875. models from the server in the form of a JSON array. When this data
  1876. returns, the current collections contents will be replaced with
  1877. the contents of the array.
  1878. </para>
  1879. <programlisting language="javascript">
  1880. var PhotoCollection = new Backbone.Collection;
  1881. PhotoCollection.url = '/photos';
  1882. PhotoCollection.fetch();
  1883. </programlisting>
  1884. <para>
  1885. Under the covers, <literal>Backbone.sync</literal> is the function
  1886. called every time Backbone tries to read or save models to the
  1887. server. It uses jQuery or Zeptos ajax implementations to make
  1888. these RESTful requests, however this can be overridden as per your
  1889. needs.
  1890. </para>
  1891. <para>
  1892. In the above example if we wanted to log an event when
  1893. <literal>.sync()</literal> was called, we could do this:
  1894. </para>
  1895. <programlisting language="javascript">
  1896. Backbone.sync = function(method, model) {
  1897. console.log(&quot;I've been passed &quot; + method + &quot; with &quot; + JSON.stringify(model));
  1898. };
  1899. </programlisting>
  1900. <para>
  1901. <emphasis role="strong">Resetting/Refreshing
  1902. Collections</emphasis>
  1903. </para>
  1904. <para>
  1905. Rather than adding or removing models individually, you might
  1906. occasionally wish to update an entire collection at once.
  1907. <literal>Collection.reset()</literal> allows us to replace an
  1908. entire collection with new models as follows:
  1909. </para>
  1910. <programlisting language="javascript">
  1911. PhotoCollection.reset([
  1912. {title: &quot;My trip to Scotland&quot;, src: &quot;scotland-trip.jpg&quot;},
  1913. {title: &quot;The flight from Scotland&quot;, src: &quot;long-flight.jpg&quot;},
  1914. {title: &quot;Latest snap of lock-ness&quot;, src: &quot;lockness.jpg&quot;}]);
  1915. </programlisting>
  1916. </sect2>
  1917. <sect2 id="underscore-utility-functions">
  1918. <title>Underscore utility functions</title>
  1919. <para>
  1920. As Backbone requires Underscore as a hard dependency, were able
  1921. to use many of the utilities it has to offer to aid with our
  1922. application development. Heres an example of how Underscores
  1923. <literal>sortBy()</literal> method can be used to sort a
  1924. collection of photos based on a particular attribute.
  1925. </para>
  1926. <programlisting language="javascript">
  1927. var sortedByAlphabet = PhotoCollection.sortBy(function (photo) {
  1928. return photo.get(&quot;title&quot;).toLowerCase();
  1929. });
  1930. </programlisting>
  1931. <para>
  1932. The complete list of what Underscore can do is beyond the scope of
  1933. this guide, but can be found in its official
  1934. <ulink url="http://documentcloud.github.com/underscore/">docs</ulink>.
  1935. </para>
  1936. </sect2>
  1937. <sect2 id="routers">
  1938. <title><a name="routers">Routers</a></title>
  1939. <para>
  1940. In Backbone, routers are used to help manage application state and
  1941. for connecting URLs to application events. This is achieved using
  1942. hash-tags with URL fragments, or using the browsers pushState and
  1943. History API. Some examples of routes may be seen below:
  1944. </para>
  1945. <programlisting language="javascript">
  1946. http://unicorns.com/#whatsup
  1947. http://unicorns.com/#search/seasonal-horns/page2
  1948. </programlisting>
  1949. <para>
  1950. Note: An application will usually have at least one route mapping
  1951. a URL route to a function that determines what happens when a user
  1952. reaches that particular route. This relationship is defined as
  1953. follows:
  1954. </para>
  1955. <programlisting language="javascript">
  1956. &quot;route&quot; : &quot;mappedFunction&quot;
  1957. </programlisting>
  1958. <para>
  1959. Let us now define our first controller by extending
  1960. <literal>Backbone.Router</literal>. For the purposes of this
  1961. guide, were going to continue pretending were creating a photo
  1962. gallery application that requires a GalleryRouter.
  1963. </para>
  1964. <para>
  1965. Note the inline comments in the code example below as they
  1966. continue the rest of the lesson on routers.
  1967. </para>
  1968. <programlisting language="javascript">
  1969. var GalleryRouter = Backbone.Router.extend({
  1970. /* define the route and function maps for this router */
  1971. routes: {
  1972. &quot;about&quot; : &quot;showAbout&quot;,
  1973. /*Sample usage: http://unicorns.com/#about*/
  1974. &quot;photos/:id&quot; : &quot;getPhoto&quot;,
  1975. /*This is an example of using a &quot;:param&quot; variable which allows us to match
  1976. any of the components between two URL slashes*/
  1977. /*Sample usage: http://unicorns.com/#photos/5*/
  1978. &quot;search/:query&quot; : &quot;searchPhotos&quot;
  1979. /*We can also define multiple routes that are bound to the same map function,
  1980. in this case searchPhotos(). Note below how we're optionally passing in a
  1981. reference to a page number if one is supplied*/
  1982. /*Sample usage: http://unicorns.com/#search/lolcats*/
  1983. &quot;search/:query/p:page&quot; : &quot;searchPhotos&quot;,
  1984. /*As we can see, URLs may contain as many &quot;:param&quot;s as we wish*/
  1985. /*Sample usage: http://unicorns.com/#search/lolcats/p1*/
  1986. &quot;photos/:id/download/*imagePath&quot; : &quot;downloadPhoto&quot;,
  1987. /*This is an example of using a *splat. splats are able to match any number of
  1988. URL components and can be combined with &quot;:param&quot;s*/
  1989. /*Sample usage: http://unicorns.com/#photos/5/download/files/lolcat-car.jpg*/
  1990. /*If you wish to use splats for anything beyond default routing, it's probably a good
  1991. idea to leave them at the end of a URL otherwise you may need to apply regular
  1992. expression parsing on your fragment*/
  1993. &quot;*other&quot; : &quot;defaultRoute&quot;
  1994. /*This is a default route that also uses a *splat. Consider the
  1995. default route a wildcard for URLs that are either not matched or where
  1996. the user has incorrectly typed in a route path manually*/
  1997. /*Sample usage: http://unicorns.com/#anything*/
  1998. },
  1999. showAbout: function(){
  2000. },
  2001. getPhoto: function(id){
  2002. /*
  2003. Note that the id matched in the above route will be passed to this function
  2004. */
  2005. console.log(&quot;You are trying to reach photo &quot; + id);
  2006. },
  2007. searchPhotos: function(query, page){
  2008. console.log(&quot;Page number: &quot; + page + &quot; of the results for &quot; + query);
  2009. },
  2010. downloadPhoto: function(id, path){
  2011. },
  2012. defaultRoute: function(other){
  2013. console.log(&quot;Invalid. You attempted to reach:&quot; + other);
  2014. }
  2015. });
  2016. /* Now that we have a router setup, remember to instantiate it*/
  2017. var myGalleryRouter = new GalleryRouter();
  2018. </programlisting>
  2019. <para>
  2020. As of Backbone 0.5+, its possible to opt-in for HTML5 pushState
  2021. support via <literal>window.history.pushState</literal>. This
  2022. permits you to define routes such as
  2023. http://www.scriptjunkie.com/just/an/example. This will be
  2024. supported with automatic degradation when a users browser doesnt
  2025. support pushState. For the purposes of this tutorial, well use
  2026. the hashtag method.
  2027. </para>
  2028. <sect3 id="backbone.history">
  2029. <title>Backbone.history</title>
  2030. <para>
  2031. Next, we need to initialize <literal>Backbone.history</literal>
  2032. as it handles <literal>hashchange</literal> events in our
  2033. application. This will automatically handle routes that have
  2034. been defined and trigger callbacks when theyve been accessed.
  2035. </para>
  2036. <para>
  2037. The <literal>Backbone.history.start()</literal> method will
  2038. simply tell Backbone that its OK to begin monitoring all
  2039. <literal>hashchange</literal> events as follows:
  2040. </para>
  2041. <programlisting language="javascript">
  2042. Backbone.history.start();
  2043. Router.navigate();
  2044. </programlisting>
  2045. <para>
  2046. As an aside, if you would like to save application state to the
  2047. URL at a particular point you can use the
  2048. <literal>.navigate()</literal> method to achieve this. It simply
  2049. updates your URL fragment without the need to trigger the
  2050. <literal>hashchange</literal> event:
  2051. </para>
  2052. <programlisting language="javascript">
  2053. /*Lets imagine we would like a specific fragment for when a user zooms into a photo*/
  2054. zoomPhoto: function(factor){
  2055. this.zoom(factor); //imagine this zooms into the image
  2056. this.navigate(&quot;zoom/&quot; + factor); //updates the fragment for us, but doesn't trigger the route
  2057. }
  2058. </programlisting>
  2059. <para>
  2060. It is also possible for <literal>Router.navigate()</literal> to
  2061. trigger the route as well as updating the URL fragment.
  2062. </para>
  2063. <programlisting language="javascript">
  2064. zoomPhoto: function(factor){
  2065. this.zoom(factor); //imagine this zooms into the image
  2066. this.navigate(&quot;zoom/&quot; + factor, true); //updates the fragment for us and triggers the route
  2067. }
  2068. </programlisting>
  2069. </sect3>
  2070. </sect2>
  2071. <sect2 id="namespacing">
  2072. <title><a name="namespacing">Namespacing</a></title>
  2073. <para>
  2074. When learning how to use Backbone, an important and commonly
  2075. overlooked area by tutorials is namespacing. If you already have
  2076. experience with namespacing in JavaScript, the following section
  2077. will provide some advice on how to specifically apply concepts you
  2078. know to Backbone, however I will also be covering explanations for
  2079. beginners to ensure everyone is on the same page.
  2080. </para>
  2081. <sect3 id="what-is-namespacing">
  2082. <title>What is namespacing?</title>
  2083. <para>
  2084. The basic idea around namespacing is to avoid collisions with
  2085. other objects or variables in the global namespace. Theyre
  2086. important as its best to safeguard your code from breaking in
  2087. the event of another script on the page using the same variable
  2088. names as you are. As a good <quote>citizen</quote> of the global
  2089. namespace, its also imperative that you do your best to
  2090. similarly not prevent other developers scripts executing due to
  2091. the same issues.
  2092. </para>
  2093. <para>
  2094. JavaScript doesnt really have built-in support for namespaces
  2095. like other languages, however it does have closures which can be
  2096. used to achieve a similar effect.
  2097. </para>
  2098. <para>
  2099. In this section well be taking a look shortly at some examples
  2100. of how you can namespace your models, views, routers and other
  2101. components specifically. The patterns well be examining are:
  2102. </para>
  2103. <itemizedlist>
  2104. <listitem>
  2105. <para>
  2106. Single global variables
  2107. </para>
  2108. </listitem>
  2109. <listitem>
  2110. <para>
  2111. Object Literals
  2112. </para>
  2113. </listitem>
  2114. <listitem>
  2115. <para>
  2116. Nested namespacing
  2117. </para>
  2118. </listitem>
  2119. </itemizedlist>
  2120. <para>
  2121. <emphasis role="strong">Single global variables</emphasis>
  2122. </para>
  2123. <para>
  2124. One popular pattern for namespacing in JavaScript is opting for
  2125. a single global variable as your primary object of reference. A
  2126. skeleton implementation of this where we return an object with
  2127. functions and properties can be found below:
  2128. </para>
  2129. <programlisting language="javascript">
  2130. var myApplication = (function(){
  2131. function(){
  2132. // ...
  2133. },
  2134. return {
  2135. // ...
  2136. }
  2137. })();
  2138. </programlisting>
  2139. <para>
  2140. Youve probably seen this technique before. A Backbone-specific
  2141. example might look like this:
  2142. </para>
  2143. <programlisting language="javascript">
  2144. var myViews = (function(){
  2145. return {
  2146. PhotoView: Backbone.View.extend({ .. }),
  2147. GalleryView: Backbone.View.extend({ .. }),
  2148. AboutView: Backbone.View.extend({ .. });
  2149. //etc.
  2150. };
  2151. })();
  2152. </programlisting>
  2153. <para>
  2154. Here we can return a set of views, but the same technique could
  2155. return an entire collection of models, views and routers
  2156. depending on how you decide to structure your application.
  2157. Although this works for certain situations, the biggest
  2158. challenge with the single global variable pattern is ensuring
  2159. that no one else has used the same global variable name as you
  2160. have in the page.
  2161. </para>
  2162. <para>
  2163. One solution to this problem, as mentioned by Peter Michaux, is
  2164. to use prefix namespacing. Its a simple concept at heart, but
  2165. the idea is you select a common prefix name (in this example,
  2166. <literal>myApplication_</literal>) and then define any methods,
  2167. variables or other objects after the prefix.
  2168. </para>
  2169. <programlisting language="javascript">
  2170. var myApplication_photoView = Backbone.View.extend({}),
  2171. myApplication_galleryView = Backbone.View.extend({});
  2172. </programlisting>
  2173. <para>
  2174. This is effective from the perspective of trying to lower the
  2175. chances of a particular variable existing in the global scope,
  2176. but remember that a uniquely named object can have the same
  2177. effect. This aside, the biggest issue with the pattern is that
  2178. it can result in a large number of global objects once your
  2179. application starts to grow.
  2180. </para>
  2181. <para>
  2182. For more on Peters views about the single global variable
  2183. pattern, read his
  2184. <ulink url="http://michaux.ca/articles/javascript-namespacing">excellent
  2185. post on them</ulink>.
  2186. </para>
  2187. <para>
  2188. Note: There are several other variations on the single global
  2189. variable pattern out in the wild, however having reviewed quite
  2190. a few, I felt the prefixing approach applied best to Backbone.
  2191. </para>
  2192. <para>
  2193. <emphasis role="strong">Object Literals</emphasis>
  2194. </para>
  2195. <para>
  2196. Object Literals have the advantage of not polluting the global
  2197. namespace but assist in organizing code and parameters
  2198. logically. Theyre beneficial if you wish to create easily
  2199. readable structures that can be expanded to support deep
  2200. nesting. Unlike simple global variables, Object Literals often
  2201. also take into account tests for the existence of a variable by
  2202. the same name, which helps reduce the chances of collision.
  2203. </para>
  2204. <para>
  2205. This example demonstrates two ways you can check to see if a
  2206. namespace already exists before defining it. I commonly use
  2207. Option 2.
  2208. </para>
  2209. <programlisting language="javascript">
  2210. /*Doesn't check for existence of myApplication*/
  2211. var myApplication = {};
  2212. /*
  2213. Does check for existence. If already defined, we use that instance.
  2214. Option 1: if(!myApplication) myApplication = {};
  2215. Option 2: var myApplication = myApplication || {};
  2216. We can then populate our object literal to support models, views and collections (or any data, really):
  2217. */
  2218. var myApplication = {
  2219. models : {},
  2220. views : {
  2221. pages : {}
  2222. },
  2223. collections : {}
  2224. };
  2225. </programlisting>
  2226. <para>
  2227. One can also opt for adding properties directly to the namespace
  2228. (such as your views, in the following example):
  2229. </para>
  2230. <programlisting language="javascript">
  2231. var myGalleryViews = myGalleryViews || {};
  2232. myGalleryViews.photoView = Backbone.View.extend({});
  2233. myGalleryViews.galleryView = Backbone.View.extend({});
  2234. </programlisting>
  2235. <para>
  2236. The benefit of this pattern is that youre able to easily
  2237. encapsulate all of your models, views, routers etc. in a way
  2238. that clearly separates them and provides a solid foundation for
  2239. extending your code.
  2240. </para>
  2241. <para>
  2242. This pattern has a number of benefits. Its often a good idea to
  2243. decouple the default configuration for your application into a
  2244. single area that can be easily modified without the need to
  2245. search through your entire codebase just to alter it. Heres an
  2246. example of a hypothetical object literal that stores application
  2247. configuration settings:
  2248. </para>
  2249. <programlisting language="javascript">
  2250. var myConfig = {
  2251. language: 'english',
  2252. defaults: {
  2253. enableGeolocation: true,
  2254. enableSharing: false,
  2255. maxPhotos: 20
  2256. },
  2257. theme: {
  2258. skin: 'a',
  2259. toolbars: {
  2260. index: 'ui-navigation-toolbar',
  2261. pages: 'ui-custom-toolbar'
  2262. }
  2263. }
  2264. }
  2265. </programlisting>
  2266. <para>
  2267. Note that there are really only minor syntactical differences
  2268. between the Object Literal pattern and a standard JSON data set.
  2269. If for any reason you wish to use JSON for storing your
  2270. configurations instead (e.g. for simpler storage when sending to
  2271. the back-end), feel free to.
  2272. </para>
  2273. <para>
  2274. For more on the Object Literal pattern, I recommend reading
  2275. Rebecca Murpheys
  2276. <ulink url="http://blog.rebeccamurphey.com/2009/10/15/using-objects-to-organize-your-code">excellent
  2277. article on the topic</ulink>.
  2278. </para>
  2279. <para>
  2280. <emphasis role="strong">Nested namespacing</emphasis>
  2281. </para>
  2282. <para>
  2283. An extension of the Object Literal pattern is nested
  2284. namespacing. Its another common pattern used that offers a
  2285. lower risk of collision due to the fact that even if a top-level
  2286. namespace already exists, its unlikely the same nested children
  2287. do. For example, Yahoos YUI uses the nested object namespacing
  2288. pattern extensively:
  2289. </para>
  2290. <programlisting language="javascript">
  2291. YAHOO.util.Dom.getElementsByClassName('test');
  2292. </programlisting>
  2293. <para>
  2294. Yahoos YUI uses the nested object namespacing pattern regularly
  2295. and even DocumentCloud (the creators of Backbone) use the nested
  2296. namespacing pattern in their main applications. A sample
  2297. implementation of nested namespacing with Backbone may look like
  2298. this:
  2299. </para>
  2300. <programlisting language="javascript">
  2301. var galleryApp = galleryApp || {};
  2302. // perform similar check for nested children
  2303. galleryApp.routers = galleryApp.routers || {};
  2304. galleryApp.model = galleryApp.model || {};
  2305. galleryApp.model.special = galleryApp.model.special || {};
  2306. // routers
  2307. galleryApp.routers.Workspace = Backbone.Router.extend({});
  2308. galleryApp.routers.PhotoSearch = Backbone.Router.extend({});
  2309. // models
  2310. galleryApp.model.Photo = Backbone.Model.extend({});
  2311. galleryApp.model.Comment = Backbone.Model.extend({});
  2312. // special models
  2313. galleryApp.model.special.Admin = Backbone.Model.extend({});
  2314. </programlisting>
  2315. <para>
  2316. This is readable, clearly organized, and is a relatively safe
  2317. way of namespacing your Backbone application. The only real
  2318. caveat however is that it requires your browsers JavaScript
  2319. engine to first locate the galleryApp object, then dig down
  2320. until it gets to the function youre calling. However,
  2321. developers such as Juriy Zaytsev (kangax) have tested and found
  2322. the performance differences between single object namespacing vs
  2323. the <quote>nested</quote> approach to be quite negligible.
  2324. </para>
  2325. <para>
  2326. <emphasis role="strong">Recommendation</emphasis>
  2327. </para>
  2328. <para>
  2329. Reviewing the namespace patterns above, the option that I prefer
  2330. when writing Backbone applications is nested object namespacing
  2331. with the object literal pattern.
  2332. </para>
  2333. <para>
  2334. Single global variables may work fine for applications that are
  2335. relatively trivial. However, larger codebases requiring both
  2336. namespaces and deep sub-namespaces require a succinct solution
  2337. thats both readable and scalable. I feel this pattern achieves
  2338. both of these objectives and is a good choice for most Backbone
  2339. development.
  2340. </para>
  2341. </sect3>
  2342. </sect2>
  2343. <sect2 id="additional-tips">
  2344. <title><a name="additional-tips">Additional Tips</a></title>
  2345. <sect3 id="automated-backbone-scaffolding">
  2346. <title>Automated Backbone Scaffolding</title>
  2347. <para>
  2348. Scaffolding can assist in expediting how quickly you can begin a
  2349. new application by creating the basic files required for a
  2350. project automatically. If you enjoy the idea of automated MVC
  2351. scaffolding using Backbone, Im happy to recommend checking out
  2352. a tool called
  2353. <ulink url="https://github.com/brunch/brunch">Brunch</ulink>.
  2354. </para>
  2355. <para>
  2356. It works very well with Backbone, Underscore, jQuery and
  2357. CoffeeScript and is even used by companies such as Red Bull and
  2358. Jim Beam. You may have to update any third party dependencies
  2359. (e.g. latest jQuery or Zepto) when using it, but other than that
  2360. it should be fairly stable to use right out of the box.
  2361. </para>
  2362. <para>
  2363. Brunch can be installed via the nodejs package manager and is
  2364. easy to get started with. If you happen to use Vim or Textmate
  2365. as your editor of choice, youll be happy to know that there are
  2366. Brunch bundles available for both.
  2367. </para>
  2368. </sect3>
  2369. <sect3 id="is-there-a-limit-to-the-number-of-routers-i-should-be-using">
  2370. <title>Is there a limit to the number of routers I should be
  2371. using?</title>
  2372. <para>
  2373. Andrew de Andrade has pointed out that DocumentCloud themselves
  2374. usually only use a single router in most of their applications.
  2375. Youre very likely to not require more than one or two routers
  2376. in your own projects as the majority of your application routing
  2377. can be kept organized in a single controller without it getting
  2378. unwieldy.
  2379. </para>
  2380. </sect3>
  2381. <sect3 id="is-backbone-too-small-for-my-applications-needs">
  2382. <title>Is Backbone too small for my applications needs?</title>
  2383. <para>
  2384. If you find yourself unsure of whether or not your application
  2385. is too large to use Backbone, I recommend reading my post on
  2386. building large-scale jQuery &amp; JavaScript applications or
  2387. reviewing my slides on client-side MVC architecture options. In
  2388. both, I cover alternative solutions and my thoughts on the
  2389. suitability of current MVC solutions for scaled application
  2390. development.
  2391. </para>
  2392. <para>
  2393. Backbone can be used for building both trivial and complex
  2394. applications as demonstrated by the many examples Ashkenas has
  2395. been referencing in the Backbone documentation. As with any MVC
  2396. framework however, its important to dedicate time towards
  2397. planning out what models and views your application really
  2398. needs. Diving straight into development without doing this can
  2399. result in either spaghetti code or a large refactor later on and
  2400. its best to avoid this where possible.
  2401. </para>
  2402. <para>
  2403. At the end of the day, the key to building large applications is
  2404. not to build large applications in the first place. If you
  2405. however find Backbone doesnt cut it for your requirements I
  2406. strongly recommend checking out JavaScriptMVC or SproutCore as
  2407. these both offer a little more than Backbone out of the box.
  2408. Dojo and Dojo Mobile may also be of interest as these have also
  2409. been used to build significantly complex apps by other
  2410. developers.
  2411. </para>
  2412. </sect3>
  2413. </sect2>
  2414. </sect1>
  2415. <sect1 id="restful-applications">
  2416. <title>## <a name="restfulapps">RESTful Applications</a></title>
  2417. <para>
  2418. </para>
  2419. </sect1>
  2420. <sect1 id="building-restful-applications-with-backbone">
  2421. <title><a name="restful">Building RESTful applications with
  2422. Backbone</a></title>
  2423. <para>
  2424. In this section of the book, were going to take a look at
  2425. developing RESTful applications using Backbone.js and modern
  2426. technology stacks. When the data for your back-end is exposed
  2427. through a purely RESTful API, tasks such as retrieving (GET),
  2428. creating (POST), updating (PUT) and deleting (DELETE) models are
  2429. made easy through Backbones Model API. This API is so intuitive in
  2430. fact that switching from storing records in a local data-store (e.g
  2431. localStorage) to a database/noSQL data-store is a lot simpler than
  2432. you may think.
  2433. </para>
  2434. </sect1>
  2435. <sect1 id="stack-1-building-a-backbone-app-with-node.js-express-mongoose-and-mongodb">
  2436. <title><a name="stack1">Stack 1: Building A Backbone App With Node.js,
  2437. Express, Mongoose and MongoDB</a></title>
  2438. <para>
  2439. The first stack well be looking at is:
  2440. </para>
  2441. <itemizedlist>
  2442. <listitem>
  2443. <para>
  2444. <ulink url="nodejs.org">Node.js</ulink>
  2445. </para>
  2446. </listitem>
  2447. <listitem>
  2448. <para>
  2449. <ulink url="http://expressjs.com/">Express</ulink>
  2450. </para>
  2451. </listitem>
  2452. <listitem>
  2453. <para>
  2454. <ulink url="http://mongoosejs.com/">Mongoose</ulink>
  2455. </para>
  2456. </listitem>
  2457. <listitem>
  2458. <para>
  2459. and <ulink url="http://www.mongodb.org/">MongoDB</ulink>
  2460. </para>
  2461. </listitem>
  2462. </itemizedlist>
  2463. <para>
  2464. with <ulink url="http://jade-lang.com/">Jade</ulink> used optionally
  2465. as a view/templating engine.
  2466. </para>
  2467. <sect2 id="reviewing-the-stack">
  2468. <title>Reviewing the stack</title>
  2469. <para>
  2470. As you may know, node.js is an event-driven platform (built on the
  2471. <ulink url="http://code.google.com/apis/v8/design.html">V8</ulink>
  2472. runtime), designed for writing fast, scalable network
  2473. applications. Its reasonably lightweight, efficient and great for
  2474. real-time applications that are data-intensive.
  2475. </para>
  2476. <para>
  2477. Express is a small web-development framework written with node.js,
  2478. based on <ulink url="http://www.sinatrarb.com/">Sinatra</ulink>.
  2479. It supports a number of useful features such as intuitive views,
  2480. robust routing and a focus on high performance.
  2481. </para>
  2482. <para>
  2483. Next on the list are MongoDB and Mongoose. MongoDB is an
  2484. open-source, document-oriented database store designed with
  2485. scalability and agility in mind. As a
  2486. <ulink url="http://en.wikipedia.org/wiki/NoSQL">noSQL</ulink>
  2487. database, rather than storing data in tables and rows (something
  2488. were very used to doing with relational databases), with MongoDB
  2489. we instead store JSON-like documents using dynamic schemas. One of
  2490. the goals of Mongo is to try bridging the gap between key-value
  2491. stores (speed, scalability) and
  2492. <ulink url="http://en.wikipedia.org/wiki/Relational_database">relational</ulink>
  2493. databases (rich functionality).
  2494. </para>
  2495. <para>
  2496. Mongoose is a JavaScript library that simplifies how we interact
  2497. with Mongo. Like Express, its designed to work within the node.js
  2498. environment and tries to solve some of the complexities with
  2499. asynchronous data storage by offering a more user-friendly API. It
  2500. also adds chaining features into the mix, allowing for a slightly
  2501. more expressive way of dealing with our data.
  2502. </para>
  2503. <para>
  2504. Jade is a template engine influenced by Haml (which well be
  2505. looking at later). Its implemented with JavaScript (and also runs
  2506. under node). In addition to supporting Express out of the box, it
  2507. boasts a number of useful features including support for mixins,
  2508. includes, caching, template inheritance and much more. Whilst
  2509. abstractions like Jade certainly arent for everyone, our
  2510. practical will cover working both with and without it.
  2511. </para>
  2512. </sect2>
  2513. <sect2 id="practical">
  2514. <title>Practical</title>
  2515. <para>
  2516. For this practical, were going to once again look at extending
  2517. the popular Backbone Todo application. Rather than relying on
  2518. localStorage for data persistence, were going to switch to
  2519. storing Todos in a MongoDB document-store instead. The code for
  2520. this practical can be found in
  2521. <literal>practicals\stacks\option2</literal>
  2522. </para>
  2523. <para>
  2524. <emphasis role="strong">app.js</emphasis>
  2525. </para>
  2526. <para>
  2527. (See
  2528. <ulink url="https://github.com/addyosmani/backbone-boilerplates/blob/master/option2/app.js">here</ulink>
  2529. for the source)
  2530. </para>
  2531. <para>
  2532. We must first include the node dependencies required by our
  2533. application. These are Express, Mongoose and Path (a module
  2534. containing utilities for dealing with file paths.
  2535. </para>
  2536. <programlisting language="javascript">
  2537. var application_root = __dirname,
  2538. express = require(&quot;express&quot;),
  2539. path = require(&quot;path&quot;),
  2540. mongoose = require('mongoose');
  2541. </programlisting>
  2542. <para>
  2543. Next, create a new Express server.
  2544. <literal>express.createServer()</literal> is a simple way of
  2545. creating an instance of express.HTTPServer, which well be using
  2546. to pass in our routes.
  2547. </para>
  2548. <programlisting language="javascript">
  2549. var app = express.createServer();
  2550. </programlisting>
  2551. <para>
  2552. After this, connect Mongoose up to a database (in our case,
  2553. localhost should suffice). Should you require the ability to pass
  2554. in authentication information, heres a sample containing all of
  2555. the supported URL parameters:
  2556. <literal>mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]</literal>
  2557. </para>
  2558. <programlisting language="javascript">
  2559. mongoose.connect('mongodb://localhost/my_database');
  2560. </programlisting>
  2561. <para>
  2562. A Mongoose model for any Todo item can now be easily defined by
  2563. passing a schema instance to <literal>mongoose.model</literal>. In
  2564. our case the schema covers a Todo items <literal>text</literal>
  2565. content, its <literal>done</literal> state and
  2566. <literal>order</literal> position in the overall Todo list.
  2567. </para>
  2568. <programlisting language="javascript">
  2569. var Todo = mongoose.model('Todo', new mongoose.Schema({
  2570. text: String,
  2571. done: Boolean,
  2572. order: Number
  2573. }));
  2574. </programlisting>
  2575. <para>
  2576. The <literal>configure()</literal> methods allows us to setup what
  2577. we need for the current environment with our Express server. Note
  2578. that lower down in the configuration are two view/view related
  2579. lines. The last one explicitly sets the viewing/templating engine
  2580. to be used as Jade
  2581. <literal>app.set('view engine', 'jade')</literal>. We can avoid
  2582. these if we wish to use plain HTML/JS for our templates instead.
  2583. </para>
  2584. <programlisting language="javascript">
  2585. app.configure(function(){
  2586. // the bodyParser middleware parses JSON request bodies
  2587. app.use(express.bodyParser());
  2588. app.use(express.methodOverride());
  2589. app.use(app.router);
  2590. app.use(express.static(path.join(application_root, &quot;public&quot;)));
  2591. app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
  2592. app.set('views', path.join(application_root, &quot;views&quot;));
  2593. app.set('view engine', 'jade')
  2594. });
  2595. </programlisting>
  2596. <para>
  2597. Should you prefer to switch out Jade for an alternative view
  2598. engine, this can be done fairly trivially. See the section under
  2599. <quote>Templating</quote> here:
  2600. https://github.com/joyent/node/wiki/modules. For example, to
  2601. switch to EJS, you would simply write
  2602. <literal>app.set('view engine', 'ejs')</literal>
  2603. </para>
  2604. <para>
  2605. Express makes use of common HTTP verbs (get, put, post etc.) to
  2606. provide easy to use, expressive routing API based on CRUD (Create,
  2607. Read, Update and Delete). Below for example, we can define what
  2608. happens when the browser requests the root <quote>/</quote>. As a
  2609. trivial route in this application, it doesnt do anything
  2610. particularly exciting, however getters typically read or retrieve
  2611. data.
  2612. </para>
  2613. <programlisting language="javascript">
  2614. app.get('/', function(req, res){
  2615. res.send('Hello World');
  2616. });
  2617. </programlisting>
  2618. <para>
  2619. Onto something a little more useful and in our next route,
  2620. navigating to <quote>/todo</quote> will actually render our Jade
  2621. view <quote>todo.jade</quote>, as seen in the callback. Additional
  2622. configuration values can be passed as the second parameter, such
  2623. as the custom title specified below.
  2624. </para>
  2625. <programlisting language="javascript">
  2626. app.get('/todo', function(req, res){
  2627. res.render('todo', {title: &quot;Our sample application&quot;});
  2628. });
  2629. </programlisting>
  2630. <para>
  2631. Next, we can see the first of our <quote>/api/</quote> routes.
  2632. </para>
  2633. <programlisting language="javascript">
  2634. app.get('/api/todos', function(req, res){
  2635. return Todo.find(function(err, todos) {
  2636. return res.send(todos);
  2637. });
  2638. });
  2639. </programlisting>
  2640. <para>
  2641. The callback to our next route supports querying for todos based
  2642. on a specific ID. The route string itself (once compiled) will be
  2643. converted from <quote>/api/todos/:id</quote> to a regular
  2644. expression. As you might have guessed, this is a hint that routes
  2645. can also be regular expression literals if we wished to do
  2646. something more complex.
  2647. </para>
  2648. <programlisting language="javascript">
  2649. app.get('/api/todos/:id', function(req, res){
  2650. return Todo.findById(req.params.id, function(err, todo) {
  2651. if (!err) {
  2652. return res.send(todo);
  2653. }
  2654. });
  2655. });
  2656. </programlisting>
  2657. <para>
  2658. Similarly, we want to support updating todos based on a specific
  2659. ID as well. The following allows us to query a todo by ID and then
  2660. update the values of its three attributes (text, done, order)
  2661. easily.
  2662. </para>
  2663. <programlisting language="javascript">
  2664. app.put('/api/todos/:id', function(req, res){
  2665. return Todo.findById(req.params.id, function(err, todo) {
  2666. todo.text = req.body.text;
  2667. todo.done = req.body.done;
  2668. todo.order = req.body.order;
  2669. return todo.save(function(err) {
  2670. if (!err) {
  2671. console.log(&quot;updated&quot;);
  2672. }
  2673. return res.send(todo);
  2674. });
  2675. });
  2676. });
  2677. </programlisting>
  2678. <para>
  2679. Weve so far covered requesting todos and updating them, but a
  2680. core part of the application requires us to insert (or add) new
  2681. todos to our data-store. Below we can create new <code>Todo</code>
  2682. models and simply save them.
  2683. </para>
  2684. <programlisting language="javascript">
  2685. app.post('/api/todos', function(req, res){
  2686. var todo;
  2687. todo = new Todo({
  2688. text: req.body.text,
  2689. done: req.body.done,
  2690. order: req.body.order
  2691. });
  2692. todo.save(function(err) {
  2693. if (!err) {
  2694. return console.log(&quot;created&quot;);
  2695. }
  2696. });
  2697. return res.send(todo);
  2698. });
  2699. </programlisting>
  2700. <para>
  2701. We of course also want to support deleting todos (e.g if a todo
  2702. has been <quote>cleared</quote>, it should be deleted). This also
  2703. works based on a specific todo ID.
  2704. </para>
  2705. <programlisting language="javascript">
  2706. app.delete('/api/todos/:id', function(req, res){
  2707. return Todo.findById(req.params.id, function(err, todo) {
  2708. return todo.remove(function(err) {
  2709. if (!err) {
  2710. console.log(&quot;removed&quot;);
  2711. return res.send('')
  2712. }
  2713. });
  2714. });
  2715. });
  2716. </programlisting>
  2717. <para>
  2718. Finally, this last line is to ensure were only listening on the
  2719. port app.js is running.
  2720. </para>
  2721. <programlisting language="javascript">
  2722. app.listen(3000);
  2723. </programlisting>
  2724. <para>
  2725. <emphasis role="strong">script.js - updating our Backbone.js
  2726. app</emphasis>
  2727. </para>
  2728. <para>
  2729. In the <literal>/public/js</literal> folder of options 1 (HTML
  2730. templates) and 2 (Jade) for the practical, youll find a version
  2731. of the Backbone Todo app originally by Jerome Gravel-Niquet. Lets
  2732. pay attention to
  2733. <ulink url="https://github.com/addyosmani/backbone-boilerplates/blob/master/option2/public/js/script.js">script.js</ulink>.
  2734. In order to change the application to work with our new back-end,
  2735. well need to make some very minor changes to this.
  2736. </para>
  2737. <para>
  2738. Reviewing <literal>window.TodoList</literal> (a Backbone
  2739. Collection), youll notice that it has a property called
  2740. <literal>localStorage</literal>, which uses the Backbone
  2741. <ulink url="https://github.com/jeromegn/Backbone.localStorage">localStorage</ulink>
  2742. adapter in order to facilitate storing data using the browsers
  2743. localStorage features.
  2744. </para>
  2745. <programlisting language="javascript">
  2746. window.TodoList = Backbone.Collection.extend({
  2747. // Reference to this collection's model.
  2748. model: Todo,
  2749. // Save all of the todo items under the `&quot;todos&quot;` namespace.
  2750. // Typically, this should be a unique name within your application
  2751. localStorage: new Store(&quot;todos&quot;),
  2752. </programlisting>
  2753. <para>
  2754. In order to switch it over to our RESTful backend, were going to
  2755. make use of the <literal>url</literal> property or function on a
  2756. collection to reference its location on the server. Models inside
  2757. of a collection then use <literal>url</literal> to construct URLs
  2758. of their own. As all of the CRUD for our RESTful API works on the
  2759. base route <quote>/api/todos</quote>, this is the value we set
  2760. <literal>url</literal> to.
  2761. </para>
  2762. <programlisting language="javascript">
  2763. // localStorage: new Store(&quot;todos&quot;),
  2764. url: '/api/todos',
  2765. </programlisting>
  2766. <para>
  2767. This is the only change necessary to our existing Backbone
  2768. application in order to get things working. Pretty easy, right?
  2769. </para>
  2770. <para>
  2771. <emphasis role="strong">todo.jade</emphasis>
  2772. </para>
  2773. <para>
  2774. The Jade templates for our application cover declarative markup
  2775. for both the index (layout.jade) of the application and the main
  2776. Todo container (todo.jade). It also covers the script-tag
  2777. templates used for rendering each new Todo item thats added.
  2778. </para>
  2779. <programlisting language="html">
  2780. // Todo App Interface
  2781. #todoapp
  2782. .title
  2783. h1 Todos
  2784. .content
  2785. #create-todo
  2786. input#new-todo(placeholder=&amp;quot;What needs to be done?&amp;quot;, type=&amp;quot;text&amp;quot;)
  2787. span.ui-tooltip-top(style=&amp;quot;display:none;&amp;quot;) Press Enter to save this task
  2788. #todos
  2789. ul#todo-list
  2790. #todo-stats
  2791. // Templates
  2792. script#item-template(type=&amp;quot;text/template&amp;quot;)
  2793. &amp;lt;div class=&amp;quot;todo &amp;lt;%= done ? 'done' : '' %&amp;gt;&amp;quot;&amp;gt;
  2794. .display
  2795. &amp;lt;input class=&amp;quot;check&amp;quot; type=&amp;quot;checkbox&amp;quot; &amp;lt;%= done ? 'checked=&amp;quot;checked&amp;quot;' : '' %&amp;gt; /&amp;gt;
  2796. .todo-text
  2797. span#todo-destroy
  2798. .edit
  2799. input.todo-input(type=&amp;quot;text&amp;quot;, &amp;quot;value&amp;quot;=&amp;quot;&amp;quot;)
  2800. &amp;lt;/div&amp;gt;
  2801. script#stats-template(type=&amp;quot;text/template&amp;quot;)
  2802. &amp;lt;% if (total) { %&amp;gt;
  2803. span.todo-count
  2804. span.number &amp;lt;%= remaining %&amp;gt;
  2805. span.word &amp;lt;%= remaining == 1 ? 'item' : 'items' %&amp;gt;
  2806. | left.
  2807. &amp;lt;% } %&amp;gt;
  2808. &amp;lt;% if (done) { %&amp;gt;
  2809. span.todo-clear
  2810. a(href=&amp;quot;#&amp;quot;)
  2811. | Clear
  2812. span.number-done &amp;lt;%= done %&amp;gt;
  2813. | completed
  2814. span.word-done &amp;lt;%= done == 1 ? 'item' : 'items' %&amp;gt;
  2815. &amp;lt;% } %&amp;gt;
  2816. </programlisting>
  2817. <para>
  2818. <emphasis role="strong">layout.jade</emphasis>
  2819. </para>
  2820. <programlisting language="html">
  2821. !!! 5
  2822. //if lt IE 6
  2823. &amp;lt;html class=&amp;quot;no-js ie6 oldie&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;
  2824. //if IE 7
  2825. &amp;lt;html class=&amp;quot;no-js ie7 oldie&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;
  2826. //if IE 8
  2827. &amp;lt;html class=&amp;quot;no-js ie8 oldie&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;
  2828. //if gt IE 8
  2829. &amp;lt;!--&amp;gt; &amp;lt;html class=&amp;quot;no-js&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt; &amp;lt;!--
  2830. head
  2831. meta(charset=&amp;quot;utf-8&amp;quot;)
  2832. meta(http-equiv=&amp;quot;X-UA-Compatible&amp;quot;, content=&amp;quot;IE=edge,chrome=1&amp;quot;)
  2833. title=title
  2834. meta(name=&amp;quot;description&amp;quot;, content=&amp;quot;&amp;quot;)
  2835. meta(name=&amp;quot;author&amp;quot;, content=&amp;quot;&amp;quot;)
  2836. meta(name=&amp;quot;viewport&amp;quot;, content=&amp;quot;width=device-width,initial-scale=1&amp;quot;)
  2837. // CSS concatenated and minified via ant build script
  2838. link(rel=&amp;quot;stylesheet&amp;quot;, href=&amp;quot;css/style.css&amp;quot;)
  2839. // end CSS
  2840. script(src=&amp;quot;js/libs/modernizr-2.0.6.min.js&amp;quot;)
  2841. body
  2842. #container
  2843. header
  2844. #main(role=&amp;quot;main&amp;quot;)!=body
  2845. footer
  2846. //! end of #container
  2847. script(src=&amp;quot;//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js&amp;quot;)
  2848. script
  2849. window.jQuery || document.write('&amp;lt;script src=&amp;quot;js/libs/jquery-1.6.2.min.js&amp;quot;&amp;gt;&amp;lt;\\/script&amp;gt;')
  2850. // scripts concatenated and minified via ant build script
  2851. script(src=&amp;quot;js/mylibs/underscore.js&amp;quot;)
  2852. script(src=&amp;quot;js/mylibs/backbone.js&amp;quot;)
  2853. script(defer, src=&amp;quot;js/plugins.js&amp;quot;)
  2854. script(defer, src=&amp;quot;js/script.js&amp;quot;)
  2855. // end scripts
  2856. // Change UA-XXXXX-X to be your site's ID
  2857. script
  2858. window._gaq = [['_setAccount','UAXXXXXXXX1'],['_trackPageview'],['_trackPageLoadTime']];
  2859. Modernizr.load({load: ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js'});
  2860. //if lt IE 7
  2861. script(src=&amp;quot;//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js&amp;quot;)
  2862. script
  2863. window.attachEvent('onload',function(){CFInstall.check({mode:'overlay'})})
  2864. &amp;lt;/html&amp;gt;
  2865. </programlisting>
  2866. <para>
  2867. <emphasis role="strong">static.html</emphasis>
  2868. </para>
  2869. <para>
  2870. Alternatively, a static version of our index which doesnt rely on
  2871. Jade can be put together as follows. See
  2872. <ulink url="https://github.com/addyosmani/backbone-boilerplates/blob/master/option1/public/static.html">here</ulink>
  2873. for the complete file or below for a sample.
  2874. </para>
  2875. <programlisting language="html">
  2876. &amp;lt;div id=&amp;quot;container&amp;quot;&amp;gt;
  2877. &amp;lt;div id=&amp;quot;main&amp;quot; role=&amp;quot;main&amp;quot;&amp;gt;
  2878. &amp;lt;!-- Todo App Interface--&amp;gt;
  2879. &amp;lt;div id=&amp;quot;todoapp&amp;quot;&amp;gt;
  2880. &amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;
  2881. &amp;lt;h1&amp;gt;Todos&amp;lt;/h1&amp;gt;
  2882. &amp;lt;/div&amp;gt;
  2883. &amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;
  2884. &amp;lt;div id=&amp;quot;create-todo&amp;quot;&amp;gt;
  2885. &amp;lt;input id=&amp;quot;new-todo&amp;quot; placeholder=&amp;quot;What needs to be done?&amp;quot; type=
  2886. &amp;quot;text&amp;quot; /&amp;gt;&amp;lt;span style=&amp;quot;display:none;&amp;quot; class=&amp;quot;ui-tooltip-top&amp;quot;&amp;gt;Press Enter to
  2887. save this task&amp;lt;/span&amp;gt;
  2888. &amp;lt;/div&amp;gt;
  2889. &amp;lt;div id=&amp;quot;todos&amp;quot;&amp;gt;
  2890. &amp;lt;ul id=&amp;quot;todo-list&amp;quot;&amp;gt;&amp;lt;/ul&amp;gt;
  2891. &amp;lt;/div&amp;gt;
  2892. &amp;lt;div id=&amp;quot;todo-stats&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  2893. &amp;lt;/div&amp;gt;
  2894. &amp;lt;/div&amp;gt;
  2895. &amp;lt;!-- Templates--&amp;gt;
  2896. &amp;lt;script id=&amp;quot;item-template&amp;quot; type=&amp;quot;text/template&amp;quot;&amp;gt;
  2897. &amp;lt;div class=&amp;quot;todo &amp;lt;%= done ? 'done' : '' %&amp;gt;&amp;quot;&amp;gt;
  2898. &amp;lt;div class=&amp;quot;display&amp;quot;&amp;gt;&amp;lt;input class=&amp;quot;check&amp;quot; type=&amp;quot;checkbox&amp;quot; &amp;lt;%= done ? 'checked=&amp;quot;checked&amp;quot;' : '' %&amp;gt; /&amp;gt;
  2899. &amp;lt;div class=&amp;quot;todo-text&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;span id=&amp;quot;todo-destroy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;edit&amp;quot;&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; value=&amp;quot;&amp;quot; class=&amp;quot;todo-input&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
  2900. &amp;lt;/script&amp;gt;
  2901. &amp;lt;script id=&amp;quot;stats-template&amp;quot; type=&amp;quot;text/template&amp;quot;&amp;gt;
  2902. &amp;lt;% if (total) { %&amp;gt;
  2903. &amp;lt;span class=&amp;quot;todo-count&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;number&amp;quot;&amp;gt;&amp;lt;%= remaining %&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;word&amp;quot;&amp;gt;&amp;lt;%= remaining == 1 ? 'item' : 'items' %&amp;gt;&amp;lt;/span&amp;gt; left.
  2904. &amp;lt;/span&amp;gt;&amp;lt;% } %&amp;gt;
  2905. &amp;lt;% if (done) { %&amp;gt;
  2906. &amp;lt;span class=&amp;quot;todo-clear&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt; Clear
  2907. &amp;lt;span class=&amp;quot;number-done&amp;quot;&amp;gt;&amp;lt;%= done %&amp;gt;&amp;lt;/span&amp;gt; completed
  2908. &amp;lt;span class=&amp;quot;word-done&amp;quot;&amp;gt;&amp;lt;%= done == 1 ? 'item' : 'items' %&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;% } %&amp;gt;
  2909. &amp;lt;/script&amp;gt;
  2910. &amp;lt;/div&amp;gt;
  2911. &amp;lt;/div&amp;gt;
  2912. &amp;lt;!--! end of #container--&amp;gt;
  2913. </programlisting>
  2914. </sect2>
  2915. <sect2 id="practical-setup">
  2916. <title>Practical Setup</title>
  2917. <para>
  2918. Weve now gone through the major points of developing a RESTful
  2919. backend using Node.js, Express and Mongoose. Next, lets make sure
  2920. you can get your environment setup to run the updated Todo app.
  2921. </para>
  2922. <sect3 id="mongodb">
  2923. <title>MongoDB</title>
  2924. <para>
  2925. Once youve downloaded
  2926. <ulink url="http://www.mongodb.org/downloads">MongoDB</ulink>,
  2927. youll need to complete two steps to get it up and running.
  2928. </para>
  2929. <para>
  2930. <emphasis role="strong">Data directories</emphasis>
  2931. </para>
  2932. <para>
  2933. MongoDB stores data in the bin/data/db folder but wont actually
  2934. create this directory for you. Navigate to where youve
  2935. downloaded and extracted MongoDB and run the following from
  2936. terminal:
  2937. </para>
  2938. <programlisting language="html">
  2939. sudo mkdir -p /data/db/
  2940. sudo chown `id -u` /data/db
  2941. </programlisting>
  2942. <para>
  2943. <emphasis role="strong">Running and connecting to your
  2944. server</emphasis>
  2945. </para>
  2946. <para>
  2947. Once this is done, open up two terminal windows.
  2948. </para>
  2949. <para>
  2950. In the first, <literal>cd</literal> to your MongoDB bin
  2951. directory or type in the complete path to it. Youll need to
  2952. start <cpde>mongod`.
  2953. </para>
  2954. <programlisting language="html">
  2955. $ ./bin/mongod
  2956. </programlisting>
  2957. <para>
  2958. Next, in the second terminal, start the
  2959. `mongo</code shell which will connect up to localhost by default.
  2960. ```html
  2961. $ ./bin/mongo
  2962. ```
  2963. That's it!.
  2964. ####Express and Mongoose
  2965. Option 1 (HTML) and Option 2 (Jade) of the practical download both come with an install.sh bash script. This allows you to easily install Express, Mongoose, Jade (and optionally MongoDB if you prefer to) through npm (the node package manager).
  2966. * Make sure you have Node.js installed. If not, you can grab it [here](http://nodejs.org/#download)
  2967. * Next run `$ ./install.sh` at the terminal to install the rest of our dependencies. To see the exact contents of the install.sh file, see below:
  2968. **install.sh**
  2969. ```html
  2970. #!/bin/bash
  2971. npm install express
  2972. npm install mongodb --mongodb:native
  2973. npm install mongoose
  2974. npm install jade
  2975. ```
  2976. * After you've installed all of the dependencies for the stack, we can get to cloning the repo containing our practicals and running them. Start by running the below lines:
  2977. ```html
  2978. git clone git://github.com/addyosmani/backbone-boilerplates.git
  2979. cd option2
  2980. node app.js
  2981. ```
  2982. For option1 (without Jade), simply cd into option1 and run `node app.js` from there.
  2983. Finally, either of the example apps can now be accessed by navigating to:
  2984. * Option 1: `http://localhost:3000/static.html`
  2985. * Option 2: `http://localhost:3000/todo`
  2986. That's it! Whilst there's a lot more than can be done to expand on the concepts covered so far, the base we're reviewed should be enough to get you up and running with this stack if you wish to use it with Backbone.
  2987. #<a name="stack2">Building Backbone.js Apps With Ruby, Sinatra,
  2988. MongoDB and Haml</a>
  2989. </para>
  2990. </sect3>
  2991. </sect2>
  2992. </sect1>
  2993. <sect1 id="introduction-1">
  2994. <title>Introduction</title>
  2995. <para>
  2996. In this chapter were going to explore writing Backbone.js
  2997. applications with a Ruby back-end. To assist with this, were going
  2998. to use <ulink url="http://www.sinatrarb.com/">Sinatra</ulink> - a
  2999. DSL (domain specific language) for rapidly creating web applications
  3000. in Ruby. Similar to the
  3001. <ulink url="https://github.com/addyosmani/backbone-fundamentals/#stack1">section</ulink>
  3002. on writing an application with Node.js, our server-side language
  3003. (Ruby) will be used to power an API whilst Backbone.js will be the
  3004. client consuming it.
  3005. </para>
  3006. </sect1>
  3007. <sect1 id="what-is-sinatra">
  3008. <title>What Is Sinatra?</title>
  3009. <para>
  3010. In the past, youve likely come across or used
  3011. <ulink url="http://rubyonrails.org">Ruby on Rails</ulink> (RoR) - a
  3012. popular web application framework for the Ruby programming language
  3013. that helps organize applications using the MVC pattern. Sinatra is a
  3014. much smaller, more light-weight alternative to it.
  3015. </para>
  3016. <para>
  3017. Whilst a very basic Rails application may require a more strict
  3018. project structure (such as requiring the use of controllers, views
  3019. and routing etc.), Sinatra doesnt require as many of these
  3020. dependencies, sacrificing the helpers needed to connect to
  3021. databases, tools to create forms or any of the other utilities Rails
  3022. comes with out of the box.
  3023. </para>
  3024. <para>
  3025. What Sinatra does have is a
  3026. <emphasis role="strong">minimal</emphasis> set of features most
  3027. useful for tying specific URLs and RESTful HTTP actions to blocks of
  3028. Ruby code and returning this codes output as a response. Sinatra is
  3029. particularly useful for getting projects up and running quickly
  3030. where we dont have a need for the extra pieces RoR provides.
  3031. </para>
  3032. <para>
  3033. For those who are familiar with more Rails, you probably know that
  3034. it requires a separate routes file to define how an application
  3035. should be responding to requests. These are then piped into the
  3036. relevant models and controllers as needed.
  3037. </para>
  3038. <para>
  3039. Sinatra takes a more straight-forward approach, providing us with
  3040. the most simple path to handling routing. By declaring
  3041. <literal>get</literal>,<literal>post</literal>,
  3042. <literal>put</literal> or <literal>delete</literal> actions, we can
  3043. inform Sinatra to add a new route, which we can then have respond to
  3044. requests.
  3045. </para>
  3046. <para>
  3047. The framework is particularly useful for writing APIs, widgets and
  3048. small-scale applications that can power the backend of a
  3049. client-heavy application. As mentioned, we will be using it to power
  3050. our API.
  3051. </para>
  3052. </sect1>
  3053. <sect1 id="getting-started-with-sinatra">
  3054. <title>Getting Started With Sinatra</title>
  3055. <para>
  3056. Lets review how to write and run a very basic Sinatra application.
  3057. As most programming languages and frameworks typically start with
  3058. some variation of <quote>Hello World</quote>, well start with a
  3059. similar example.
  3060. </para>
  3061. <para>
  3062. Note: Before beginning this section, I recommend installing Sinatra
  3063. on your system. A guide to doing this can be found in the
  3064. <link linkend="preq">prerequisites</link> section lower down in the
  3065. article.
  3066. </para>
  3067. <sect2 id="routes">
  3068. <title>Routes</title>
  3069. <para>
  3070. As mentioned, Sinatra allows us to define new routes using HTTP
  3071. actions. Semantically, a route follows quite a simple structure:
  3072. </para>
  3073. <programlisting language="ruby">
  3074. &lt;a HTTP action&gt; &lt;the desired route&gt; do
  3075. # some behaviour
  3076. end
  3077. </programlisting>
  3078. <para>
  3079. A tiny route that outputs a <quote>Hello World</quote>-like
  3080. message when we attempt to <quote>get</quote> the root could thus
  3081. be written as follows:
  3082. </para>
  3083. <programlisting language="ruby">
  3084. require 'sinatra'
  3085. get '/' do
  3086. &quot;Hello World! Is it me you're looking for?&quot;
  3087. end
  3088. </programlisting>
  3089. <para>
  3090. To run this snippet, we can can simply save it to a local .rb
  3091. file and execute it as follows:
  3092. </para>
  3093. <programlisting language="ruby">
  3094. ruby -rubygems example.rb
  3095. </programlisting>
  3096. <para>
  3097. If we now navigated to http://localhost:4567 in our browser we
  3098. could now see the application running successfully.
  3099. </para>
  3100. <para>
  3101. The HTTP verbs we commonly work with when writing RESTful web
  3102. services are: <literal>get</literal>, <literal>post</literal>,
  3103. <literal>delete</literal> and <literal>put</literal>. As we now
  3104. know, all Sinatra routes are basically HTTP actions
  3105. (`<literal>get</literal> etc.) that are paired with a URL-matching
  3106. pattern. We associate a pair of an action and route with code we
  3107. would like sent back to the browser (executed)if the route is
  3108. reached. Sinatra doesn’t enforce much in the way of architectural
  3109. structure, instead relying on simplicity to supporting writing
  3110. powerful APIs.
  3111. </para>
  3112. <para>
  3113. Here’s an example of a skeleton service we could put together
  3114. supporting four common HTTP actions: ruby ``` get
  3115. <quote>/items</quote> do # list all items available end
  3116. </para>
  3117. <para>
  3118. get <quote>/item/:id</quote> do # get a single item end
  3119. </para>
  3120. <para>
  3121. post <quote>/item</quote> do # create a new item end
  3122. </para>
  3123. <para>
  3124. put <quote>/item/:id</quote> do # update an existing item end
  3125. </para>
  3126. <para>
  3127. delete <quote>/item/:id</quote> do # delete an item end ```
  3128. </para>
  3129. <para>
  3130. Sinatra’s routing is both easy for beginners to get started with
  3131. but is also flexible enough for those wishing to define more
  3132. complex routes. As you probably noticed in the above example,
  3133. routes can include named parameters (e.g
  3134. <literal>/item/:id</literal>). We can actually access the content
  3135. of these routes using the <literal>params</literal> hash as
  3136. follows:
  3137. </para>
  3138. <programlisting language="ruby">
  3139. get '/item/:id' do
  3140. # this matches &quot;GET /item/10&quot; and &quot;GET /item/11&quot;
  3141. # params[:id] is &quot;10&quot; or &quot;11&quot;
  3142. &quot;You reached #{params[:id]}&quot;
  3143. end
  3144. </programlisting>
  3145. <para>
  3146. Sinatra also supports route matching via splats, wildcards and
  3147. regular expressions. For more information on this I recommend
  3148. reading the official
  3149. <ulink url="http://www.sinatrarb.com/documentation">docs</ulink>.
  3150. Let’s now take a look at handlers.
  3151. </para>
  3152. <para>
  3153. Sinatra includes convenient handler methods for tasks such as
  3154. redirection, halting and passing.
  3155. </para>
  3156. <sect3 id="redirection">
  3157. <title>Redirection</title>
  3158. <para>
  3159. A simple route supporting redirection which returns a 302
  3160. response can be written as follows:
  3161. </para>
  3162. <programlisting language="ruby">
  3163. get '/items' do
  3164. redirect '/items/welcome'
  3165. end
  3166. </programlisting>
  3167. <para>
  3168. And if we wish to pass additional parameters such as arguments
  3169. we can do so like this: redirect
  3170. <quote>http://site.com/</quote>, <quote>Oops! I think we have a
  3171. problem!</quote>
  3172. </para>
  3173. </sect3>
  3174. <sect3 id="halting">
  3175. <title>Halting</title>
  3176. <para>
  3177. To immediately stop a request (halting) we can use
  3178. <quote>halt</quote>. Heres an example of halting a request where
  3179. we specify the message body:
  3180. </para>
  3181. <para>
  3182. <literal>halt &quot;who goes there!?&quot;</literal>
  3183. </para>
  3184. </sect3>
  3185. <sect3 id="passing">
  3186. <title>Passing</title>
  3187. <para>
  3188. <quote>Passing</quote> is the concept of deferring processing of
  3189. a block to the next matching route. We do this using
  3190. <literal>pass</literal>. In the following example if a parameter
  3191. isnt the username we expect (rick-astley) we simply pass it on:
  3192. </para>
  3193. <programlisting language="ruby">
  3194. get '/members/:username' do
  3195. pass unless params[:username] == 'rick-astley'
  3196. 'Never gonna give you up, never gonna let you down'
  3197. end
  3198. get '/member/*' do
  3199. 'Welcome!'
  3200. end
  3201. </programlisting>
  3202. <para>
  3203. There are also handler methods that can assist with sessions
  3204. (specifically, cookie-based session handling). To use Sinatra’s
  3205. session handling, first enable it in your application with:
  3206. </para>
  3207. <programlisting language="ruby">
  3208. enable :sessions
  3209. </programlisting>
  3210. <para>
  3211. You can then use the session handling capabilities as follows:
  3212. </para>
  3213. <programlisting language="ruby">
  3214. get '/items' do
  3215. session['visitCounter'] ||= 0;
  3216. session['visitCounter'] += 1;
  3217. &quot;This page has been accessed #{session['visitCounter']} times&quot;
  3218. end
  3219. </programlisting>
  3220. <para>
  3221. Note: By default enable:sessions will store all data in cookies.
  3222. If this is not desired, you can not call this and instead use
  3223. some Rack middleware instead. For more on this see
  3224. <ulink url="http://www.sinatrarb.com/intro#Using%20Sessions">here</ulink>.
  3225. </para>
  3226. <para>
  3227. This only touches the surface of what can be done using routes
  3228. and handlers, but is sufficient for us to write the
  3229. Sinatra-powered API service we require in the practical section
  3230. of this chapter.
  3231. </para>
  3232. </sect3>
  3233. </sect2>
  3234. </sect1>
  3235. <sect1 id="templating-and-haml">
  3236. <title>Templating And HAML</title>
  3237. <para>
  3238. Let’s now discuss templating.Out of the box, we can begin using
  3239. templates in our Sinatra applications with ERB. ERB is included with
  3240. Ruby and allows Ruby code to be added to any plain text document for
  3241. the purpose of generating information or flow control. In the
  3242. following example using an ERB template, note that views are by
  3243. default located in the <literal>views</literal> directory of our
  3244. application.
  3245. </para>
  3246. <programlisting language="ruby">
  3247. get '/items' do
  3248. erb :default
  3249. # renders views/default.erb
  3250. end
  3251. </programlisting>
  3252. <para>
  3253. A useful Sinatra convention worth noting is how layouts are handled.
  3254. Layouts automatically search for a views/layout template which is
  3255. rendered before any other views are loaded. With ERB, our
  3256. views/layout.erb file could look as follows:
  3257. </para>
  3258. <programlisting language="html">
  3259. &lt;html&gt;
  3260. &lt;head&gt;&lt;/head&gt;
  3261. &lt;body&gt;
  3262. &lt;%= data %&gt;
  3263. &lt;/body&gt;
  3264. &lt;/html&gt;
  3265. </programlisting>
  3266. <para>
  3267. Haml is a popular alternative to ERB which offers an abstract syntax
  3268. for writing application templates. It has been said to be:
  3269. </para>
  3270. <itemizedlist>
  3271. <listitem>
  3272. <para>
  3273. Straight-forward to learn
  3274. </para>
  3275. </listitem>
  3276. <listitem>
  3277. <para>
  3278. Very easy to read and use for visually expressing a hierarchy of
  3279. DOM elements
  3280. </para>
  3281. </listitem>
  3282. <listitem>
  3283. <para>
  3284. Popular with web designers as it builds on top of CSS syntax
  3285. </para>
  3286. </listitem>
  3287. <listitem>
  3288. <para>
  3289. Well documented with a large community backing it
  3290. </para>
  3291. </listitem>
  3292. <listitem>
  3293. <para>
  3294. Almost as fast as ERB
  3295. </para>
  3296. </listitem>
  3297. </itemizedlist>
  3298. <para>
  3299. For the purpose of comparison, below we can see an ERB template
  3300. compared to it’s Haml equivalent.
  3301. </para>
  3302. <sect2 id="erb">
  3303. <title>ERB</title>
  3304. <programlisting language="html">
  3305. &lt;div class=&quot;todo&quot; id=&quot;content&quot;&gt;
  3306. &lt;h2 class=&quot;entry_title&quot;&gt;&lt;%= h @todo.title %&gt;&lt;/h2&gt;
  3307. &lt;div class=&quot;entry_link&quot;&gt;&lt;%= link_to('link', @todo.link) %&gt;&lt;/div&gt;
  3308. &lt;/div&gt;
  3309. </programlisting>
  3310. </sect2>
  3311. <sect2 id="haml">
  3312. <title>Haml</title>
  3313. <programlisting language="html">
  3314. .todo#content
  3315. %h2.entry_title= @todo.title
  3316. .entry_link= link_to('link', @todo.link)
  3317. </programlisting>
  3318. <para>
  3319. One of the first things we notice is that the Haml snippet looks
  3320. significantly more like CSS than it does traditional markup. It’s
  3321. much easier to read and we no longer need to be concerned with
  3322. divs, spans, closing tags or other semantic rules that usually
  3323. mean more keystrokes. The approach taken to making whitespace a
  3324. part of the syntax also means it can be much easier to compare
  3325. changes between multiple documents (especially if you’re doing a
  3326. diff).
  3327. </para>
  3328. <para>
  3329. In the list of Haml features, we briefly mentioned web designers.
  3330. As developers, we regularly need to communicate and work with
  3331. designers, but we always have to remember that at the end of the
  3332. day, they are not programmers. They’re usually more concerned with
  3333. the look and the feel of an application, but if we want them to
  3334. write mark-up as a part of the templates or skins they create,
  3335. Haml is a simpler option that has worked well for teams at a
  3336. number of companies.
  3337. </para>
  3338. <programlisting language="ruby">
  3339. %h1 This is some h1 text
  3340. %h2 This is some h2 text.
  3341. %p Now we have a line containing a single instance variable: @content
  3342. %p= @content
  3343. %p Embedding Ruby code in the middle of a line can be done using ==.
  3344. %p== Here is an example: #{@foobar}
  3345. %p We can also add attributes using {}
  3346. %p{:style =&gt; &quot;color:green&quot;} We just made this paragraph green!
  3347. %p You'll want to apply classes and ids to your DOM, too.
  3348. %p.foo This has the foo class
  3349. %p.bar This has the bar class
  3350. %p#foobar This has the foobar id
  3351. %p.foo#foobar Or you can combine them!
  3352. %p Nesting can be done like this
  3353. %p
  3354. Or even like this
  3355. </programlisting>
  3356. <para>
  3357. Note: Haml is whitespace sensitive and will not correctly work if
  3358. it isn’t indented by an even number of spaces. This is due to
  3359. whitespace being used for nesting in place of the classic HTML
  3360. markup approach of closing tags.
  3361. </para>
  3362. </sect2>
  3363. </sect1>
  3364. <sect1 id="mongodb-ruby-driver">
  3365. <title>MongoDB Ruby Driver</title>
  3366. <sect2 id="getting-started">
  3367. <title>Getting started</title>
  3368. <para>
  3369. Once the MongoDB Ruby driver is installed, we can begin to use it
  3370. to connect to a Mongo database. To create a connection using
  3371. localhost, we simply specify the driver as a dependency. Assuming
  3372. we’re using the default port we can then connect as follows:
  3373. </para>
  3374. <programlisting language="ruby">
  3375. require 'mongo'
  3376. # where 'learning-mongo' is the name of our database:
  3377. db = Connection.new.db('learning-mongo');
  3378. </programlisting>
  3379. <para>
  3380. We probably also want to place some data into
  3381. <quote>learning-mongo</quote>. It could be as simple as a note, so
  3382. why don’t we go ahead and begin a notes collection?:
  3383. </para>
  3384. <para>
  3385. <literal>ruby notes = db.collection('notes')</literal> Something
  3386. interesting worth noting is that at this point, we haven’t
  3387. actually created the database nor the collection we’re referencing
  3388. above.
  3389. </para>
  3390. <para>
  3391. Neither of these items exist in Mongo (just yet) but as we’re
  3392. working with a new database but they will once we insert some real
  3393. data.
  3394. </para>
  3395. <para>
  3396. A new note could be defined using key/value pairs as follows and
  3397. then inserted into <quote>learning-mongo</quote> using
  3398. <literal>collection.insert()</literal>:
  3399. </para>
  3400. <programlisting language="ruby">
  3401. our_note = { :text =&gt; 'Remember the milk', :remindInterval =&gt; 'weekly'}
  3402. note_id = notes.insert(our_note)
  3403. </programlisting>
  3404. <para>
  3405. What is returned from inserting a note into the notes collection
  3406. is an <literal>ObjectId</literal> reference for the note from
  3407. Mongo. This is useful as we can re-use it to locate the same
  3408. document in our database.
  3409. </para>
  3410. <programlisting language="ruby">
  3411. note = notes.find( :id =&gt; note_id ).first
  3412. </programlisting>
  3413. <para>
  3414. This can also be used in conjunction with Mongo’s
  3415. <literal>collection.update()</literal> method and
  3416. <ulink url="http://www.mongodb.org/display/DOCS/Updating">query</ulink>
  3417. operators (i.e <literal>$set</literal>) to replace fields in an
  3418. existing document.
  3419. </para>
  3420. <para>
  3421. We might update an entire document as follows:
  3422. </para>
  3423. <programlisting language="ruby">
  3424. note = notes.find( :id =&gt; note_id ).first
  3425. note[:text] = 'Remember the bread'
  3426. notes.update({ :_id =&gt; note_id }, note)
  3427. </programlisting>
  3428. <para>
  3429. or using <literal>$set</literal>, update an existing document
  3430. without overwriting the entire object as like this:
  3431. </para>
  3432. <programlisting language="ruby">
  3433. notes.update({ :_id =&gt; note_id }, '$set' =&gt; { :text = &gt; 'Remember the bread' })
  3434. </programlisting>
  3435. <para>
  3436. Useful to know: Almost each MongoDB document has an _id field as
  3437. it’s first attribute. This can normally be of any type, however a
  3438. special BSON datatype is provided for object ids. It’s a 12-byte
  3439. binary value that has a high probability of being unique when
  3440. allocated.
  3441. </para>
  3442. <para>
  3443. Note: Whilst we opted for the MongoDB Ruby Driver for this stack,
  3444. you may also be interested in
  3445. <emphasis role="strong">DataMapper</emphasis> - a solution which
  3446. allows us to use the same API to talk to a number of different
  3447. datastores. This works well for both relational and non-relational
  3448. databases and more information is available on the official
  3449. <ulink url="http://datamapper.org/why.html">project page</ulink>.
  3450. <ulink url="http://sinatra-book.gittr.com/#datamapper">Sinatra:
  3451. The Book</ulink> also contains a brief tutorial on DataMapper for
  3452. anyone interested in exploring it further.
  3453. </para>
  3454. </sect2>
  3455. </sect1>
  3456. <sect1 id="practical-1">
  3457. <title>Practical</title>
  3458. <para>
  3459. We’re going to use Sinatra in a similar manner to how we used
  3460. Express in the last chapter. It will power a RESTful API supporting
  3461. CRUD operations. Together with a MongoDB data store, this will allow
  3462. us to easily persist data (todo items) whilst ensuring they are
  3463. stored in a database. If you’ve read the previous chapter or have
  3464. gone through any of the Todo examples covered so far, you will find
  3465. this surprisingly straight-forward.
  3466. </para>
  3467. <para>
  3468. Remember that the default Todo example included with Backbone.js
  3469. already persists data, although it does this via a localStorage
  3470. adapter. Luckily there aren’t a great deal of changes needed to
  3471. switch over to using our Sinatra-based API. Let’s briefly review the
  3472. code that will be powering the CRUD operations for this sections
  3473. practical, as we go course won’t be starting off with a
  3474. near-complete base for most of our real world applications.
  3475. </para>
  3476. <sect2 id="installing-the-prerequisites">
  3477. <title><a id="preq">Installing The Prerequisites</a></title>
  3478. <sect3 id="ruby">
  3479. <title>Ruby</title>
  3480. <para>
  3481. If using OSX or Linux, Ruby may be one of a number of
  3482. open-source packages that come pre-installed and you can skip
  3483. over to the next paragraph. In case you would like to check if
  3484. check if you have Ruby installed, open up the terminal prompt
  3485. and type:
  3486. </para>
  3487. <para>
  3488. <literal>$ ruby -v</literal>
  3489. </para>
  3490. <para>
  3491. The output of this will either be the version of Ruby installed
  3492. or an error complaining that Ruby wasn’t found.
  3493. </para>
  3494. <para>
  3495. Should you need to install Ruby manually (e.g for an operating
  3496. system such as Windows), you can do so by downloading the latest
  3497. version from http://www.ruby-lang.org/en/downloads/.
  3498. Alternatively, (RVM)[http://beginrescueend.com/rvm/install/]
  3499. (Ruby Version Manager) is a command-line tool that allows you to
  3500. easily install and manage multiple ruby environments with ease.
  3501. </para>
  3502. </sect3>
  3503. <sect3 id="ruby-gems">
  3504. <title>Ruby Gems</title>
  3505. <para>
  3506. Next, we will need to install Ruby Gems. Gems are a standard way
  3507. to package programs or libraries written in Ruby and with Ruby
  3508. Gems it’s possible to install additional dependencies for Ruby
  3509. applications very easily.
  3510. </para>
  3511. <para>
  3512. On OSX, Linux or Windows go to
  3513. <ulink url="http://rubyforge.org/projects/rubygems">http://rubyforge.org/projects/rubygems</ulink>
  3514. and download the latest version of Ruby Gems. Once downloaded,
  3515. open up a terminal, navigate to the folder where this resides
  3516. and enter:
  3517. </para>
  3518. <programlisting>
  3519. $&gt; tar xzvf rubygems.tgz
  3520. $&gt; cd rubygems
  3521. $&gt; sudo ruby setup.rb
  3522. </programlisting>
  3523. <para>
  3524. There will likely be a version number included in your download
  3525. and you should make sure to include this when tying the above.
  3526. Finally, a symlink (symbolic link) to tie everything togther
  3527. should be fun as follows:
  3528. </para>
  3529. <para>
  3530. <literal>$ sudo ln -s /usr/bin/gem1.8.17 /usr/bin/gem</literal>
  3531. </para>
  3532. <para>
  3533. To check that Ruby Gems has been correctly installed, type the
  3534. following into your terminal:
  3535. </para>
  3536. <programlisting>
  3537. $ gem -v
  3538. </programlisting>
  3539. </sect3>
  3540. <sect3 id="sinatra">
  3541. <title>Sinatra</title>
  3542. <para>
  3543. With Ruby Gems setup, we can now easily install Sinatra. For
  3544. Linux or OSX type this in your terminal:
  3545. </para>
  3546. <para>
  3547. <literal>$ sudo gem install sinatra</literal>
  3548. </para>
  3549. <para>
  3550. and if you’re on Windows, enter the following at a command
  3551. prompt:
  3552. </para>
  3553. <para>
  3554. <literal>c:\\ &gt; gem install sinatra</literal>
  3555. </para>
  3556. </sect3>
  3557. <sect3 id="haml-1">
  3558. <title>Haml</title>
  3559. <para>
  3560. As with other DSLs and frameworks, Sinatra supports a wide range
  3561. of different templating engines.
  3562. <ulink url="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html">ERB</ulink>
  3563. is the one most often recommended by the Sinatra camp, however
  3564. as a part of this chapter, we’re going to explore the use of
  3565. <ulink url="http://haml.hamptoncatlin.com/">Haml</ulink> to
  3566. define our application templates.
  3567. </para>
  3568. <para>
  3569. Haml stands for HTML Abstractional Markup Language and is a
  3570. lightweight markup language abstraction that can be used to
  3571. describe HTML without the need to use traditional markup
  3572. language semantics (such as opening and closing tags).
  3573. </para>
  3574. <para>
  3575. Installing Haml can be done in just a line using Ruby Gems as
  3576. follows:
  3577. </para>
  3578. <para>
  3579. <literal>$ gem install haml</literal>
  3580. </para>
  3581. </sect3>
  3582. <sect3 id="mongodb-1">
  3583. <title>MongoDB</title>
  3584. <para>
  3585. If you haven’t already downloaded and installed MongoDB from an
  3586. earlier chapter, please
  3587. <ulink url="http://www.mongodb.org/downloads">do so</ulink> now.
  3588. With Ruby Gems, Mongo can be installed in just one line:
  3589. </para>
  3590. <para>
  3591. <literal>$ gem install mongodb</literal>
  3592. </para>
  3593. <para>
  3594. We now require two further steps to get everything up and
  3595. running.
  3596. </para>
  3597. <sect4 id="data-directories">
  3598. <title>1.Data directories</title>
  3599. <para>
  3600. MongoDB stores data in the bin/data/db folder but won’t
  3601. actually create this directory for you. Navigate to where
  3602. you’ve downloaded and extracted Mongo and run the following
  3603. from terminal:
  3604. </para>
  3605. <programlisting>
  3606. sudo mkdir -p /data/db/
  3607. sudo chown `id -u` /data/db
  3608. </programlisting>
  3609. </sect4>
  3610. <sect4 id="running-and-connecting-to-your-server">
  3611. <title>2.Running and connecting to your server</title>
  3612. <para>
  3613. Once this is done, open up two terminal windows.
  3614. </para>
  3615. <para>
  3616. In the first, cd to your MongoDB bin directory or type in the
  3617. complete path to it. You’ll need to start mongod.
  3618. </para>
  3619. <programlisting>
  3620. $ ./bin/mongod
  3621. </programlisting>
  3622. <para>
  3623. Finally, in the second terminal, start the mongo shell which
  3624. will connect up to localhost by default.
  3625. </para>
  3626. <programlisting>
  3627. $ ./bin/mongo
  3628. </programlisting>
  3629. </sect4>
  3630. </sect3>
  3631. <sect3 id="mongodb-ruby-driver-1">
  3632. <title>MongoDB Ruby Driver</title>
  3633. <para>
  3634. As we’ll be using the
  3635. <ulink url="https://github.com/mongodb/mongo-ruby-driver">MongoDB
  3636. Ruby Driver</ulink>, we’ll also require the following gems:
  3637. </para>
  3638. <para>
  3639. The gem for the driver itself:
  3640. </para>
  3641. <programlisting>
  3642. $ gem install mongo
  3643. </programlisting>
  3644. <para>
  3645. and the driver’s other prerequisite, bson:
  3646. </para>
  3647. <programlisting>
  3648. $ gem install bson_ext
  3649. </programlisting>
  3650. <para>
  3651. This is basically a collection of extensions used to increase
  3652. serialization speed.
  3653. </para>
  3654. <para>
  3655. That’s it for our prerequisites!.
  3656. </para>
  3657. </sect3>
  3658. </sect2>
  3659. <sect2 id="tutorial">
  3660. <title>Tutorial</title>
  3661. <para>
  3662. To get started, let’s get a local copy of the practical
  3663. application working on our system.
  3664. </para>
  3665. <sect3 id="application-files">
  3666. <title>Application Files</title>
  3667. <para>
  3668. Clone
  3669. <ulink url="http://github.com/addyosmani/backbone-fundamentals">this</ulink>
  3670. repository and navigate to
  3671. <literal>/practicals/stacks/option3</literal>. Now run the
  3672. following lines at the terminal:
  3673. </para>
  3674. <programlisting>
  3675. ruby app.rb
  3676. </programlisting>
  3677. <para>
  3678. Finally, navigate to <code>http://localhost:4567/todo</code> to
  3679. see the application running successfully.
  3680. </para>
  3681. <para>
  3682. <emphasis role="strong">Note:</emphasis> The Haml layout files
  3683. for Option 3 can be found in the /views folder.
  3684. </para>
  3685. <para>
  3686. The directory structure for our practical application is as
  3687. follows:
  3688. </para>
  3689. <programlisting>
  3690. --public
  3691. ----css
  3692. ----img
  3693. ----js
  3694. -----script.js
  3695. ----test
  3696. --views
  3697. app.rb
  3698. </programlisting>
  3699. <para>
  3700. The <literal>public</literal> directory contains the scripts and
  3701. stylesheets for our application and uses HTML5 Boilerplate as a
  3702. base. You can find the Models, Views and Collections for this
  3703. section within <literal>public/js/scripts.js</literal> (however,
  3704. this can of course be expanded into sub-directories for each
  3705. component if desired).
  3706. </para>
  3707. <para>
  3708. <literal>scripts.js</literal> contains the following Backbone
  3709. component definitions:
  3710. </para>
  3711. <programlisting>
  3712. --Models
  3713. ----Todo
  3714. --Collections
  3715. ----TodoList
  3716. --Views
  3717. ---TodoView
  3718. ---AppView
  3719. </programlisting>
  3720. <para>
  3721. <literal>app.rb</literal> is the small Sinatra application that
  3722. powers our backend API.
  3723. </para>
  3724. <para>
  3725. Lastly, the <literal>views</literal> directory hosts the Haml
  3726. source files for our application’s index and templates, both of
  3727. which are compiled to standard HTML markup at runtime.
  3728. </para>
  3729. <para>
  3730. These can be viewed along with other note-worthy snippets of
  3731. code from the application below.
  3732. </para>
  3733. </sect3>
  3734. <sect3 id="backbone">
  3735. <title>Backbone</title>
  3736. <sect4 id="views-2">
  3737. <title>Views</title>
  3738. <para>
  3739. In our main application view (AppView), we want to load any
  3740. previously stored Todo items in our Mongo database when the
  3741. view initializes. This is done below with the line
  3742. <literal>Todos.fetch()</literal> in the
  3743. <literal>initialize()</literal> method where we also bind to
  3744. the relevant events on the <literal>Todos</literal> collection
  3745. for when items are added or changed.
  3746. </para>
  3747. <programlisting language="javascript">
  3748. // Our overall **AppView** is the top-level piece of UI.
  3749. var AppView = Backbone.View.extend({
  3750. // Instead of generating a new element, bind to the existing skeleton of
  3751. // the App already present in the HTML.
  3752. el: $(&quot;#todoapp&quot;),
  3753. // Our template for the line of statistics at the bottom of the app.
  3754. statsTemplate: _.template($('#stats-template').html()),
  3755. // Delegated events for creating new items, and clearing completed ones.
  3756. events: {
  3757. &quot;keypress #new-todo&quot;: &quot;createOnEnter&quot;,
  3758. &quot;keyup #new-todo&quot;: &quot;showTooltip&quot;,
  3759. &quot;click .todo-clear a&quot;: &quot;clearCompleted&quot;
  3760. },
  3761. // At initialization
  3762. initialize: function() {
  3763. this.input = this.$(&quot;#new-todo&quot;);
  3764. Todos.on('add', this.addOne, this);
  3765. Todos.on('reset', this.addAll, this);
  3766. Todos.on('all', this.render, this);
  3767. Todos.fetch();
  3768. },
  3769. // Re-rendering the App just means refreshing the statistics -- the rest
  3770. // of the app doesn't change.
  3771. render: function() {
  3772. this.$('#todo-stats').html(this.statsTemplate({
  3773. total: Todos.length,
  3774. done:
  3775. ….
  3776. </programlisting>
  3777. </sect4>
  3778. </sect3>
  3779. <sect3 id="collections-1">
  3780. <title>Collections</title>
  3781. <para>
  3782. In the TodoList collection below, we’ve set the
  3783. <literal>url</literal> property to point to
  3784. <literal>/api/todos</literal> to reference the collection’s
  3785. location on the server. When we attempt to access this from our
  3786. Sinatra-backed API, it should return a list of all the Todo
  3787. items that have been previously stored in Mongo.
  3788. </para>
  3789. <para>
  3790. For the sake of thoroughness, our API will also support
  3791. returning the data for a specific Todo item via
  3792. <literal>/api/todos/itemID</literal>. We’ll take a look at this
  3793. again when writing the Ruby code powering our backend.
  3794. </para>
  3795. <programlisting language="javascript">
  3796. // Todo Collection
  3797. var TodoList = Backbone.Collection.extend({
  3798. // Reference to this collection's model.
  3799. model: Todo,
  3800. // Save all of the todo items under the `&quot;todos&quot;` namespace.
  3801. // localStorage: new Store(&quot;todos&quot;),
  3802. url: '/api/todos',
  3803. // Filter down the list of all todo items that are finished.
  3804. done: function() {
  3805. return this.filter(function(todo){ return todo.get('done'); });
  3806. },
  3807. // Filter down the list to only todo items that are still not finished.
  3808. remaining: function() {
  3809. return this.without.apply(this, this.done());
  3810. },
  3811. // We keep the Todos in sequential order, despite being saved by unordered
  3812. // GUID in the database. This generates the next order number for new items.
  3813. nextOrder: function() {
  3814. if (!this.length) return 1;
  3815. return this.last().get('order') + 1;
  3816. },
  3817. // Todos are sorted by their original insertion order.
  3818. comparator: function(todo) {
  3819. return todo.get('order');
  3820. }
  3821. });
  3822. </programlisting>
  3823. </sect3>
  3824. <sect3 id="model">
  3825. <title>Model</title>
  3826. <para>
  3827. The model for our Todo application remains largely unchanged
  3828. from the versions previously covered in this book. It is however
  3829. worth noting that calling the function
  3830. <literal>model.url()</literal> within the below would return the
  3831. relative URL where a specific Todo item could be located on the
  3832. server.
  3833. </para>
  3834. <programlisting language="javascript">
  3835. // Our basic **Todo** model has `text`, `order`, and `done` attributes.
  3836. var Todo = Backbone.Model.extend({
  3837. idAttribute: &quot;_id&quot;,
  3838. // Default attributes for a todo item.
  3839. defaults: function() {
  3840. return {
  3841. done: false,
  3842. order: Todos.nextOrder()
  3843. };
  3844. },
  3845. // Toggle the `done` state of this todo item.
  3846. toggle: function() {
  3847. this.save({done: !this.get(&quot;done&quot;)});
  3848. }
  3849. });
  3850. </programlisting>
  3851. </sect3>
  3852. <sect3 id="rubysinatra">
  3853. <title>Ruby/Sinatra</title>
  3854. <para>
  3855. Now that we’ve defined our main models, views and collections
  3856. let’s get the CRUD operations required by our Backbone
  3857. application supported in our Sinatra API.
  3858. </para>
  3859. <para>
  3860. We want to make sure that for any operations changing underlying
  3861. data (create, update, delete) that our Mongo data store
  3862. correctly reflects these.
  3863. </para>
  3864. </sect3>
  3865. <sect3 id="app.rb">
  3866. <title>app.rb</title>
  3867. <para>
  3868. For <literal>app.rb</literal>, we first define the dependencies
  3869. required by our application. These include Sinatra, Ruby Gems,
  3870. the MongoDB Ruby driver and the JSON gem.
  3871. </para>
  3872. <programlisting language="ruby">
  3873. require 'rubygems'
  3874. require 'sinatra'
  3875. require 'mongo'
  3876. require 'json'
  3877. </programlisting>
  3878. <para>
  3879. Next, we create a new connection to Mongo, specifying any custom
  3880. configuration desired. If running a multi-threaded application,
  3881. setting the <quote>pool_size</quote> allows us to specify a
  3882. maximum pool size and <quote>timeout</quote> a maximum timeout
  3883. for waiting for old connections to be released to the pool.
  3884. </para>
  3885. <programlisting language="ruby">
  3886. DB = Mongo::Connection.new.db(&quot;mydb&quot;, :pool_size =&gt; 5, :timeout =&gt; 5)
  3887. </programlisting>
  3888. <para>
  3889. Finally we define the routes to be supported by our API. Note
  3890. that in the first two blocks - one for our application root
  3891. (<literal>/</literal>) and the other for our todo items route
  3892. <literal>/todo</literal> - we’re using Haml for template
  3893. rendering.
  3894. </para>
  3895. <programlisting language="ruby">
  3896. class TodoApp &lt; Sinatra::Base
  3897. get '/' do
  3898. haml :index, :attr_wrapper =&gt; '&quot;', :locals =&gt; {:title =&gt; 'hello'}
  3899. end
  3900. get '/todo' do
  3901. haml :todo, :attr_wrapper =&gt; '&quot;', :locals =&gt; {:title =&gt; 'Our Sinatra Todo app'}
  3902. end
  3903. </programlisting>
  3904. <para>
  3905. <literal>haml :index</literal> instructs Sinatra to use the
  3906. <literal>views/index.haml</literal> for the application index,
  3907. whilst `<literal>attr_wrapper</literal> is simply defining the
  3908. values to be used for any local variables defined inside the
  3909. template. This similarly applies Todo items with the template
  3910. `views/todo.haml’.
  3911. </para>
  3912. <para>
  3913. The rest of our routes make use of the <literal>params</literal>
  3914. hash and a number of useful helper methods included with the
  3915. MongoDB Ruby driver. For more details on these, please read the
  3916. comments I’ve made inline below:
  3917. </para>
  3918. <programlisting language="ruby">
  3919. get '/api/:thing' do
  3920. # query a collection :thing, convert the output to an array, map the _id
  3921. # to a string representation of the object's _id and finally output to JSON
  3922. DB.collection(params[:thing]).find.to_a.map{|t| from_bson_id(t)}.to_json
  3923. end
  3924. get '/api/:thing/:id' do
  3925. # get the first document with the id :id in the collection :thing as a single document (rather
  3926. # than a Cursor, the standard output) using find_one(). Our bson utilities assist with
  3927. # ID conversion and the final output returned is also JSON
  3928. from_bson_id(DB.collection(params[:thing]).find_one(to_bson_id(params[:id]))).to_json
  3929. end
  3930. post '/api/:thing' do
  3931. # parse the post body of the content being posted, convert to a string, insert into
  3932. # the collection #thing and return the ObjectId as a string for reference
  3933. oid = DB.collection(params[:thing]).insert(JSON.parse(request.body.read.to_s))
  3934. &quot;{\&quot;_id\&quot;: \&quot;#{oid.to_s}\&quot;}&quot;
  3935. end
  3936. delete '/api/:thing/:id' do
  3937. # remove the item with id :id from the collection :thing, based on the bson
  3938. # representation of the object id
  3939. DB.collection(params[:thing]).remove('_id' =&gt; to_bson_id(params[:id]))
  3940. end
  3941. put '/api/:thing/:id' do
  3942. # collection.update() when used with $set (as covered earlier) allows us to set single values
  3943. # in this case, the put request body is converted to a string, rejecting keys with the name '_id' for security purposes
  3944. DB.collection(params[:thing]).update({'_id' =&gt; to_bson_id(params[:id])}, {'$set' =&gt; JSON.parse(request.body.read.to_s).reject{|k,v| k == '_id'}})
  3945. end
  3946. # utilities for generating/converting MongoDB ObjectIds
  3947. def to_bson_id(id) BSON::ObjectId.from_string(id) end
  3948. def from_bson_id(obj) obj.merge({'_id' =&gt; obj['_id'].to_s}) end
  3949. end
  3950. </programlisting>
  3951. <para>
  3952. That’s it. The above is extremely lean for an entire API, but
  3953. does allow us to read and write data to support the
  3954. functionality required by our client-side application.
  3955. </para>
  3956. <para>
  3957. For more on what MongoDB and the MongoDB Ruby driver are capable
  3958. of, please do feel free to read their documentation for more
  3959. information.
  3960. </para>
  3961. <para>
  3962. If you’re a developer wishing to take this example further, why
  3963. not try to add some additional capabilities to the service:
  3964. </para>
  3965. <itemizedlist>
  3966. <listitem>
  3967. <para>
  3968. Validation: improved validation of data in the API. What
  3969. more could be done to ensure data sanitization?
  3970. </para>
  3971. </listitem>
  3972. <listitem>
  3973. <para>
  3974. Search: search or filter down Todo items based on a set of
  3975. keywords or within a certain date range
  3976. </para>
  3977. </listitem>
  3978. <listitem>
  3979. <para>
  3980. Pagination: only return the Nth number of Todo items or
  3981. items from a start and end-point
  3982. </para>
  3983. </listitem>
  3984. </itemizedlist>
  3985. </sect3>
  3986. <sect3 id="hamltemplates">
  3987. <title>Haml/Templates</title>
  3988. <para>
  3989. Finally, we move on to the Haml files that define our
  3990. application index (layout.haml) and the template for a specific
  3991. Todo item (todo.haml). Both of these are largely
  3992. self-explanatory, but it’s useful to see the differences between
  3993. the Jade approach we reviewed in the last chapter vs. using Haml
  3994. for this implementation.
  3995. </para>
  3996. <para>
  3997. Note: In our Haml snippets below, the forward slash character is
  3998. used to indicate a comment. When this character is placed at the
  3999. beginning of a line, it wraps all of the text after it into a
  4000. HTML comment. e.g
  4001. </para>
  4002. <para>
  4003. <literal>/ These are templates</literal>
  4004. </para>
  4005. <para>
  4006. compiles to:
  4007. </para>
  4008. <para>
  4009. <literal>&lt;!-- These are templates --&gt;</literal>
  4010. </para>
  4011. </sect3>
  4012. <sect3 id="index.haml">
  4013. <title>index.haml</title>
  4014. <programlisting language="html">
  4015. %head
  4016. %meta{'charset' =&gt; 'utf-8'}/
  4017. %title=title
  4018. %meta{'name' =&gt; 'description', 'content' =&gt; ''}/
  4019. %meta{'name' =&gt; 'author', 'content' =&gt; ''}/
  4020. %meta{'name' =&gt; 'viewport', 'content' =&gt; 'width=device-width,initial-scale=1'}/
  4021. / CSS concatenated and minified via ant build script
  4022. %link{'rel' =&gt; 'stylesheet', 'href' =&gt; 'css/style.css'}/
  4023. / end CSS
  4024. %script{'src' =&gt; 'js/libs/modernizr.min.js'}
  4025. %body
  4026. %div#container
  4027. %header
  4028. %div#main
  4029. = yield
  4030. %footer
  4031. /! end of #container
  4032. %script{'src' =&gt; 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'}
  4033. / scripts concatenated and minified via ant build script
  4034. %script{'src' =&gt; 'js/mylibs/underscore.js'}
  4035. %script{'src' =&gt; 'js/mylibs/backbone.js'}
  4036. %script{'defer' =&gt; true, 'src' =&gt; 'js/plugins.js'}
  4037. %script{'defer' =&gt; true, 'src' =&gt; 'js/script.js'}
  4038. / end scripts
  4039. </programlisting>
  4040. </sect3>
  4041. <sect3 id="todo.haml">
  4042. <title>todo.haml</title>
  4043. <programlisting language="html">
  4044. %div#todoapp
  4045. %div.title
  4046. %h1
  4047. Todos
  4048. %div.content
  4049. %div#create-todo
  4050. %input#new-todo{&quot;placeholder&quot; =&gt; &quot;What needs to be done?&quot;, &quot;type&quot; =&gt; &quot;text&quot;}/
  4051. %span.ui-tooltip-top{&quot;style&quot; =&gt; &quot;display:none;&quot;} Press Enter to save this task
  4052. %div#todos
  4053. %ul#todo-list
  4054. %div#todo-stats
  4055. / Templates
  4056. %script#item-template{&quot;type&quot; =&gt; &quot;text/template&quot;}
  4057. &lt;div class=&quot;todo &lt;%= done ? 'done' : '' %&gt;&quot;&gt;
  4058. %div.display
  4059. &lt;input class=&quot;check&quot; type=&quot;checkbox&quot; &lt;%= done ? 'checked=&quot;checked&quot;' : '' %&gt; /&gt;
  4060. %div.todo-text
  4061. %span#todo-destroy
  4062. %div.edit
  4063. %input.todo-input{&quot;type&quot; =&gt; &quot;text&quot;, &quot;value&quot; =&gt;&quot;&quot;}/
  4064. &lt;/div&gt;
  4065. %script#stats-template{&quot;type&quot; =&gt; &quot;text/template&quot;}
  4066. &lt;% if (total) { %&gt;
  4067. %span.todo-count
  4068. %span.number &lt;%= remaining %&gt;
  4069. %span.word &lt;%= remaining == 1 ? 'item' : 'items' %&gt;
  4070. left.
  4071. &lt;% } %&gt;
  4072. &lt;% if (done) { %&gt;
  4073. %span.todo-clear
  4074. %a{&quot;href&quot; =&gt; &quot;#&quot;}
  4075. Clear
  4076. %span.number-done &lt;%= done %&gt;
  4077. completed
  4078. %span.word-done &lt;%= done == 1 ? 'item' : 'items' %&gt;
  4079. &lt;% } %&gt;
  4080. </programlisting>
  4081. </sect3>
  4082. </sect2>
  4083. <sect2 id="conclusions">
  4084. <title>Conclusions</title>
  4085. <para>
  4086. In this chapter, we looked at creating a Backbone application
  4087. backed by an API powered by Ruby, Sinatra, Haml, MongoDB and the
  4088. MongoDB driver. I personally found developing APIs with Sinatra a
  4089. relatively painless experience and one which I felt was on-par
  4090. with the effort required for the Node/Express implementation of
  4091. the same application.
  4092. </para>
  4093. <para>
  4094. This section is by no means the most comprehensive guide on
  4095. building complex apps using all of the items in this particular
  4096. stack. I do however hope it was an introduction sufficient enough
  4097. to help you decide on what stack to try out for your next project.
  4098. </para>
  4099. </sect2>
  4100. <sect2 id="advanced">
  4101. <title># <a name="advanced">Advanced</a></title>
  4102. <para>
  4103. </para>
  4104. </sect2>
  4105. <sect2 id="modular-javascript">
  4106. <title><a name="modularjs">Modular JavaScript</a></title>
  4107. <para>
  4108. When we say an application is modular, we generally mean it’s
  4109. composed of a set of highly decoupled, distinct pieces of
  4110. functionality stored in modules. As you probably know, loose
  4111. coupling facilitates easier maintainability of apps by removing
  4112. dependencies where possible. When this is implemented efficiently,
  4113. its quite easy to see how changes to one part of a system may
  4114. affect another.
  4115. </para>
  4116. <para>
  4117. Unlike some more traditional programming languages however, the
  4118. current iteration of JavaScript (ECMA-262) doesn’t provide
  4119. developers with the means to import such modules of code in a
  4120. clean, organized manner. It’s one of the concerns with
  4121. specifications that haven’t required great thought until more
  4122. recent years where the need for more organized JavaScript
  4123. applications became apparent.
  4124. </para>
  4125. <para>
  4126. Instead, developers at present are left to fall back on variations
  4127. of the module or object literal patterns. With many of these,
  4128. module scripts are strung together in the DOM with namespaces
  4129. being described by a single global object where it’s still
  4130. possible to incur naming collisions in your architecture. There’s
  4131. also no clean way to handle dependency management without some
  4132. manual effort or third party tools.
  4133. </para>
  4134. <para>
  4135. Whilst native solutions to these problems will be arriving in ES
  4136. Harmony, the good news is that writing modular JavaScript has
  4137. never been easier and you can start doing it today.
  4138. </para>
  4139. <para>
  4140. In this next part of the book, we’re going to look at how to use
  4141. AMD modules and RequireJS for cleanly wrapping units of code in
  4142. your application into manageable modules.
  4143. </para>
  4144. </sect2>
  4145. <sect2 id="organizing-modules-with-requirejs-and-amd">
  4146. <title><a name="organizingmodules">Organizing modules with RequireJS
  4147. and AMD</a></title>
  4148. <para>
  4149. In case you haven’t used it before,
  4150. <ulink url="http://requirejs.org">RequireJS</ulink> is a popular
  4151. script loader written by James Burke - a developer who has been
  4152. quite instrumental in helping shape the AMD module format, which
  4153. we’ll discuss more shortly. Some of RequireJS’s capabilities
  4154. include helping to load multiple script files, helping define
  4155. modules with or without dependencies and loading in non-script
  4156. dependencies such as text files.
  4157. </para>
  4158. <para>
  4159. So, why use RequireJS with Backbone? Although Backbone is
  4160. excellent when it comes to providing a sanitary structure to your
  4161. applications, there are a few key areas where some additional help
  4162. could be used:
  4163. </para>
  4164. <orderedlist numeration="arabic">
  4165. <listitem>
  4166. <para>
  4167. Backbone doesn’t endorse a particular approach to
  4168. modular-development. Although this means it’s quite open-ended
  4169. for developers to opt for classical patterns like the
  4170. module-pattern or Object Literals for structuring their apps
  4171. (which both work fine), it also means developers aren’t sure
  4172. of what works best when other concerns come into play, such as
  4173. dependency management.
  4174. </para>
  4175. </listitem>
  4176. </orderedlist>
  4177. <para>
  4178. RequireJS is compatible with the AMD (Asynchronous Module
  4179. Definition) format, a format which was born from a desire to write
  4180. something better than the <quote>write lots of script tags with
  4181. implicit dependencies and manage them manually</quote> approach to
  4182. development. In addition to allowing you to clearly declare
  4183. dependencies, AMD works well in the browser, supports string IDs
  4184. for dependencies, declaring multiple modules in the same file and
  4185. gives you easy-to-use tools to avoid polluting the global
  4186. namespace.
  4187. </para>
  4188. <orderedlist numeration="arabic">
  4189. <listitem override="2">
  4190. <para>
  4191. Let’s discuss dependency management a little more as it can
  4192. actually be quite challenging to get right if you’re doing it
  4193. by hand. When we write modules in JavaScript, we ideally want
  4194. to be able to handle the reuse of code units intelligently and
  4195. sometimes this will mean pulling in other modules at run-time
  4196. whilst at other times you may want to do this dynamically to
  4197. avoid a large pay-load when the user first hits your
  4198. application.
  4199. </para>
  4200. </listitem>
  4201. </orderedlist>
  4202. <para>
  4203. Think about the GMail web-client for a moment. When users
  4204. initially load up the page on their first visit, Google can simply
  4205. hide widgets such as the chat module until a user has indicated
  4206. (by clicking <quote>expand</quote>) that they wish to use it.
  4207. Through dynamic dependency loading, Google could load up the chat
  4208. module only then, rather than forcing all users to load it when
  4209. the page first initializes. This can improve performance and load
  4210. times and can definitely prove useful when building larger
  4211. applications.
  4212. </para>
  4213. <para>
  4214. I’ve previously written
  4215. <ulink url="http://addyosmani.com/writing-modular-js">a detailed
  4216. article</ulink> covering both AMD and other module formats and
  4217. script loaders in case you’d like to explore this topic further.
  4218. The takeaway is that although it’s perfectly fine to develop
  4219. applications without a script loader or clean module format in
  4220. place, it can be of significant benefit to consider using these
  4221. tools in your application development.
  4222. </para>
  4223. <sect3 id="writing-amd-modules-with-requirejs">
  4224. <title>Writing AMD modules with RequireJS</title>
  4225. <para>
  4226. As discussed above, the overall goal for the AMD format is to
  4227. provide a solution for modular JavaScript that developers can
  4228. use today. The two key concepts you need to be aware of when
  4229. using it with a script-loader are a <literal>define()</literal>
  4230. method for facilitating module definition and a
  4231. <literal>require()</literal> method for handling dependency
  4232. loading. <literal>define()</literal> is used to define named or
  4233. unnamed modules based on the proposal using the following
  4234. signature:
  4235. </para>
  4236. <programlisting language="javascript">
  4237. define(
  4238. module_id /*optional*/,
  4239. [dependencies] /*optional*/,
  4240. definition function /*function for instantiating the module or object*/
  4241. );
  4242. </programlisting>
  4243. <para>
  4244. As you can tell by the inline comments, the
  4245. <literal>module_id</literal> is an optional argument which is
  4246. typically only required when non-AMD concatenation tools are
  4247. being used (there may be some other edge cases where it’s useful
  4248. too). When this argument is left out, we call the module
  4249. <quote>anonymous</quote>. When working with anonymous modules,
  4250. the idea of a module’s identity is DRY, making it trivial to
  4251. avoid duplication of filenames and code.
  4252. </para>
  4253. <para>
  4254. Back to the define signature, the dependencies argument
  4255. represents an array of dependencies which are required by the
  4256. module you are defining and the third argument
  4257. (<quote>definition function</quote>) is a function that’s
  4258. executed to instantiate your module. A barebone module
  4259. (compatible with RequireJS) could be defined using
  4260. <literal>define()</literal> as follows:
  4261. </para>
  4262. <programlisting language="javascript">
  4263. // A module ID has been omitted here to make the module anonymous
  4264. define(['foo', 'bar'],
  4265. // module definition function
  4266. // dependencies (foo and bar) are mapped to function parameters
  4267. function ( foo, bar ) {
  4268. // return a value that defines the module export
  4269. // (i.e the functionality we want to expose for consumption)
  4270. // create your module here
  4271. var myModule = {
  4272. doStuff:function(){
  4273. console.log('Yay! Stuff');
  4274. }
  4275. }
  4276. return myModule;
  4277. });
  4278. </programlisting>
  4279. <sect4 id="alternate-syntax">
  4280. <title>Alternate syntax</title>
  4281. <para>
  4282. There is also a
  4283. <ulink url="http://requirejs.org/docs/whyamd.html#sugar">sugared
  4284. version</ulink> of <literal>define()</literal> available that
  4285. allows you to declare your dependencies as local variables
  4286. using <literal>require()</literal>. This will feel familiar to
  4287. anyone who’s used node, and can be easier to add or remove
  4288. dependencies. Here is the previous snippet using the alternate
  4289. syntax:
  4290. </para>
  4291. <programlisting language="javascript">
  4292. // A module ID has been omitted here to make the module anonymous
  4293. define(function(require){
  4294. // module definition function
  4295. // dependencies (foo and bar) are defined as local vars
  4296. var foo = require('foo'),
  4297. bar = require('bar');
  4298. // return a value that defines the module export
  4299. // (i.e the functionality we want to expose for consumption)
  4300. // create your module here
  4301. var myModule = {
  4302. doStuff:function(){
  4303. console.log('Yay! Stuff');
  4304. }
  4305. }
  4306. return myModule;
  4307. });
  4308. </programlisting>
  4309. <para>
  4310. The <literal>require()</literal> method is typically used to
  4311. load code in a top-level JavaScript file or within a module
  4312. should you wish to dynamically fetch dependencies. An example
  4313. of its usage is:
  4314. </para>
  4315. <programlisting language="javascript">
  4316. // Consider 'foo' and 'bar' are two external modules
  4317. // In this example, the 'exports' from the two modules loaded are passed as
  4318. // function arguments to the callback (foo and bar)
  4319. // so that they can similarly be accessed
  4320. require(['foo', 'bar'], function ( foo, bar ) {
  4321. // rest of your code here
  4322. foo.doSomething();
  4323. });
  4324. </programlisting>
  4325. <para>
  4326. <emphasis role="strong">Wrapping modules, views and other
  4327. components with AMD</emphasis>
  4328. </para>
  4329. <para>
  4330. Now that we’ve taken a look at how to define AMD modules,
  4331. let’s review how to go about wrapping components like views
  4332. and collections so that they can also be easily loaded as
  4333. dependencies for any parts of your application that require
  4334. them. At it’s simplest, a Backbone model may just require
  4335. Backbone and Underscore.js. These are considered it’s
  4336. dependencies and so, to write an AMD model module, we would
  4337. simply do this:
  4338. </para>
  4339. <programlisting language="javascript">
  4340. define(['underscore', 'backbone'], function(_, Backbone) {
  4341. var myModel = Backbone.Model.extend({
  4342. // Default attributes
  4343. defaults: {
  4344. content: &quot;hello world&quot;,
  4345. },
  4346. // A dummy initialization method
  4347. initialize: function() {
  4348. },
  4349. clear: function() {
  4350. this.destroy();
  4351. this.view.remove();
  4352. }
  4353. });
  4354. return myModel;
  4355. });
  4356. </programlisting>
  4357. <para>
  4358. Note how we alias Underscore.js’s instance to
  4359. <literal>_</literal> and Backbone to just
  4360. <literal>Backbone</literal>, making it very trivial to convert
  4361. non-AMD code over to using this module format. For a view
  4362. which might require other dependencies such as jQuery, this
  4363. can similarly be done as follows:
  4364. </para>
  4365. <programlisting language="javascript">
  4366. define([
  4367. 'jquery',
  4368. 'underscore',
  4369. 'backbone',
  4370. 'collections/mycollection',
  4371. 'views/myview'
  4372. ], function($, _, Backbone, myCollection, myView){
  4373. var AppView = Backbone.View.extend({
  4374. ...
  4375. </programlisting>
  4376. <para>
  4377. Aliasing to the dollar-sign (<literal>$</literal>), once again
  4378. makes it very easy to encapsulate any part of an application
  4379. you wish using AMD.
  4380. </para>
  4381. </sect4>
  4382. </sect3>
  4383. </sect2>
  4384. <sect2 id="keeping-your-templates-external-using-requirejs-and-the-text-plugin">
  4385. <title><a name="externaltemplates">Keeping Your Templates External
  4386. Using RequireJS And The Text Plugin</a></title>
  4387. <para>
  4388. Moving your [Underscore/Mustache/Handlebars] templates to external
  4389. files is actually quite straight-forward. As this application
  4390. makes use of RequireJS, I’ll discuss how to implement external
  4391. templates using this specific script loader.
  4392. </para>
  4393. <para>
  4394. RequireJS has a special plugin called text.js which is used to
  4395. load in text file dependencies. To use the text plugin, simply
  4396. follow these simple steps:
  4397. </para>
  4398. <orderedlist numeration="arabic">
  4399. <listitem>
  4400. <para>
  4401. Download the plugin from
  4402. http://requirejs.org/docs/download.html#text and place it in
  4403. either the same directory as your application’s main JS file
  4404. or a suitable sub-directory.
  4405. </para>
  4406. </listitem>
  4407. <listitem>
  4408. <para>
  4409. Next, include the text.js plugin in your initial RequireJS
  4410. configuration options. In the code snippet below, we assume
  4411. that RequireJS is being included in our page prior to this
  4412. code snippet being executed. Any of the other scripts being
  4413. loaded are just there for the sake of example.
  4414. </para>
  4415. </listitem>
  4416. </orderedlist>
  4417. <programlisting language="javascript">
  4418. require.config( {
  4419. paths: {
  4420. 'backbone': 'libs/AMDbackbone-0.5.3',
  4421. 'underscore': 'libs/underscore-1.2.2',
  4422. 'text': 'libs/require/text',
  4423. 'jquery': 'libs/jQuery-1.7.1',
  4424. 'json2': 'libs/json2',
  4425. 'datepicker': 'libs/jQuery.ui.datepicker',
  4426. 'datepickermobile': 'libs/jquery.ui.datepicker.mobile',
  4427. 'jquerymobile': 'libs/jquery.mobile-1.0'
  4428. },
  4429. baseUrl: 'app'
  4430. } );
  4431. </programlisting>
  4432. <orderedlist numeration="arabic">
  4433. <listitem override="3">
  4434. <para>
  4435. When the <literal>text!</literal> prefix is used for a
  4436. dependency, RequireJS will automatically load the text plugin
  4437. and treat the dependency as a text resource. A typical example
  4438. of this in action may look like..
  4439. </para>
  4440. </listitem>
  4441. </orderedlist>
  4442. <programlisting language="javascript">
  4443. require(['js/app', 'text!templates/mainView.html'],
  4444. function(app, mainView){
  4445. // the contents of the mainView file will be
  4446. // loaded into mainView for usage.
  4447. }
  4448. );
  4449. </programlisting>
  4450. <orderedlist numeration="arabic">
  4451. <listitem override="4">
  4452. <para>
  4453. Finally we can use the text resource that’s been loaded for
  4454. templating purposes. You’re probably used to storing your HTML
  4455. templates inline using a script with a specific identifier.
  4456. </para>
  4457. </listitem>
  4458. </orderedlist>
  4459. <para>
  4460. With Underscore.js’s micro-templating (and jQuery) this would
  4461. typically be:
  4462. </para>
  4463. <para>
  4464. HTML:
  4465. </para>
  4466. <programlisting>
  4467. &lt;script type=&quot;text/template&quot; id=&quot;mainViewTemplate&quot;&gt;
  4468. &lt;% _.each( person, function( person_item ){ %&gt;
  4469. &lt;li&gt;&lt;%= person_item.get(&quot;name&quot;) %&gt;&lt;/li&gt;
  4470. &lt;% }); %&gt;
  4471. &lt;/script&gt;
  4472. </programlisting>
  4473. <para>
  4474. JS:
  4475. </para>
  4476. <programlisting language="javascript">
  4477. var compiled_template = _.template( $('#mainViewTemplate').html() );
  4478. </programlisting>
  4479. <para>
  4480. With RequireJS and the text plugin however, it’s as simple as
  4481. saving your template into an external text file (say,
  4482. <literal>mainView.html</literal>) and doing the following:
  4483. </para>
  4484. <programlisting language="javascript">
  4485. require(['js/app', 'text!templates/mainView.html'],
  4486. function(app, mainView){
  4487. var compiled_template = _.template( mainView );
  4488. }
  4489. );
  4490. </programlisting>
  4491. <para>
  4492. That’s it!. You can then go applying your template to a view in
  4493. Backbone doing something like:
  4494. </para>
  4495. <programlisting language="javascript">
  4496. collection.someview.el.html( compiled_template( { results: collection.models } ) );
  4497. </programlisting>
  4498. <para>
  4499. All templating solutions will have their own custom methods for
  4500. handling template compilation, but if you understand the above,
  4501. substituting Underscore’s micro-templating for any other solution
  4502. should be fairly trivial.
  4503. </para>
  4504. <para>
  4505. <emphasis role="strong">Note:</emphasis> You may also be
  4506. interested in looking at
  4507. <ulink url="https://github.com/ZeeAgency/requirejs-tpl">Require.js
  4508. tpl</ulink>. It’s an AMD-compatible version of the Underscore
  4509. templating system that also includes support for optimization
  4510. (pre-compiled templates) which can lead to better performance and
  4511. no evals. I have yet to use it myself, but it comes as a
  4512. recommended resource.
  4513. </para>
  4514. </sect2>
  4515. <sect2 id="optimizing-backbone-apps-for-production-with-the-requirejs-optimizer">
  4516. <title><a name="optimizingrequirejs">Optimizing Backbone apps for
  4517. production with the RequireJS Optimizer</a></title>
  4518. <para>
  4519. As experienced developers may know, an essential final step when
  4520. writing both small and large JavaScript web applications is the
  4521. build process. The majority of non-trivial apps are likely to
  4522. consist of more than one or two scripts and so optimizing,
  4523. minimizing and concatenating your scripts prior to pushing them to
  4524. production will require your users to download a reduced number
  4525. (if not just one) script file.
  4526. </para>
  4527. <para>
  4528. Note: If you haven’t looked at build processes before and this is
  4529. your first time hearing about them, you might find
  4530. <ulink url="http://addyosmani.com/blog/client-side-build-process/">my
  4531. post and screencast on this topic</ulink> useful.
  4532. </para>
  4533. <para>
  4534. With some other structural JavaScript frameworks, my
  4535. recommendation would normally be to implicitly use YUI Compressor
  4536. or Google’s closure compiler tools, but we have a slightly more
  4537. elegant method available, when it comes to Backbone if you’re
  4538. using RequireJS. RequireJS has a command line optimization tool
  4539. called r.js which has a number of capabilities, including:
  4540. </para>
  4541. <itemizedlist>
  4542. <listitem>
  4543. <para>
  4544. Concatenating specific scripts and minifying them using
  4545. external tools such as UglifyJS (which is used by default) or
  4546. Google’s Closure Compiler for optimal browser delivery, whilst
  4547. preserving the ability to dynamically load modules
  4548. </para>
  4549. </listitem>
  4550. <listitem>
  4551. <para>
  4552. Optimizing CSS and stylesheets by inlining CSS files imported
  4553. using @import, stripping out comments etc.
  4554. </para>
  4555. </listitem>
  4556. <listitem>
  4557. <para>
  4558. The ability to run AMD projects in both Node and Rhino (more
  4559. on this later)
  4560. </para>
  4561. </listitem>
  4562. </itemizedlist>
  4563. <para>
  4564. You’ll notice that I mentioned the word <quote>specific</quote> in
  4565. the first bullet point. The RequireJS optimizer only concatenates
  4566. module scripts that have been specified in arrays of string
  4567. literals passed to top-level (i.e non-local) require and define
  4568. calls. As clarified by the
  4569. <ulink url="http://requirejs.org/docs/optimization.html">optimizer
  4570. docs</ulink> this means that Backbone modules defined like this:
  4571. </para>
  4572. <programlisting language="javascript">
  4573. define(['jquery','backbone','underscore', 'collections/sample','views/test'],
  4574. function($,Backbone, _, Sample, Test){
  4575. //...
  4576. });
  4577. </programlisting>
  4578. <para>
  4579. will combine fine, however inline dependencies such as:
  4580. </para>
  4581. <programlisting language="javascript">
  4582. var models = someCondition ? ['models/ab','models/ac'] : ['models/ba','models/bc'];
  4583. </programlisting>
  4584. <para>
  4585. will be ignored. This is by design as it ensures that dynamic
  4586. dependency/module loading can still take place even after
  4587. optimization.
  4588. </para>
  4589. <para>
  4590. Although the RequireJS optimizer works fine in both Node and Java
  4591. environments, it’s strongly recommended to run it under Node as it
  4592. executes significantly faster there. In my experience, it’s a
  4593. piece of cake to get setup with either environment, so go for
  4594. whichever you feel most comfortable with.
  4595. </para>
  4596. <para>
  4597. To get started with r.js, grab it from the
  4598. <ulink url="http://requirejs.org/docs/download.html#rjs">RequireJS
  4599. download page</ulink> or
  4600. <ulink url="http://requirejs.org/docs/optimization.html#download">through
  4601. NPM</ulink>. Now, the RequireJS optimizer works absolutely fine
  4602. for single script and CSS files, but for most cases you’ll want to
  4603. actually optimize an entire Backbone project. You
  4604. <emphasis>could</emphasis> do this completely from the
  4605. command-line, but a cleaner option is using build profiles.
  4606. </para>
  4607. <para>
  4608. Below is an example of a build file taken from the modular jQuery
  4609. Mobile app referenced later in this book. A
  4610. <emphasis role="strong">build profile</emphasis> (commonly named
  4611. <literal>app.build.js</literal>) informs RequireJS to copy all of
  4612. the content of <literal>appDir</literal> to a directory defined by
  4613. <literal>dir</literal> (in this case
  4614. <literal>../release</literal>). This will apply all of the
  4615. necessary optimizations inside the release folder. The
  4616. <literal>baseUrl</literal> is used to resolve the paths for your
  4617. modules. It should ideally be relative to
  4618. <literal>appDir</literal>.
  4619. </para>
  4620. <para>
  4621. Near the bottom of this sample file, you’ll see an array called
  4622. <literal>modules</literal>. This is where you specify the module
  4623. names you wish to have optimized. In this case we’re optimizing
  4624. the main application called <quote>app</quote>, which maps to
  4625. <literal>appDir/app.js</literal>. If we had set the
  4626. <literal>baseUrl</literal> to <quote>scripts</quote>, it would be
  4627. mapped to <literal>appDir/scripts/app.js</literal>.
  4628. </para>
  4629. <programlisting language="javascript">
  4630. ({
  4631. appDir: &quot;./&quot;,
  4632. baseUrl: &quot;./&quot;,
  4633. dir: &quot;../release&quot;,
  4634. paths: {
  4635. 'backbone': 'libs/AMDbackbone-0.5.3',
  4636. 'underscore': 'libs/underscore-1.2.2',
  4637. 'jquery': 'libs/jQuery-1.7.1',
  4638. 'json2': 'libs/json2',
  4639. 'datepicker': 'libs/jQuery.ui.datepicker',
  4640. 'datepickermobile': 'libs/jquery.ui.datepicker.mobile',
  4641. 'jquerymobile': 'libs/jquery.mobile-1.0'
  4642. },
  4643. optimize: &quot;uglify&quot;,
  4644. modules: [
  4645. {
  4646. name: &quot;app&quot;,
  4647. exclude: [
  4648. // If you prefer not to include certain libs exclude them here
  4649. ]
  4650. }
  4651. ]
  4652. })
  4653. </programlisting>
  4654. <para>
  4655. The way the build system in r.js works is that it traverses app.js
  4656. (whatever modules you’ve passed) and resolved dependencies,
  4657. concatenating them into the final <literal>release</literal>(dir)
  4658. folder. CSS is treated the same way.
  4659. </para>
  4660. <para>
  4661. The build profile is usually placed inside the
  4662. <quote>scripts</quote> or <quote>js</quote> directory of your
  4663. project. As per the docs, this file can however exist anywhere you
  4664. wish, but you’ll need to edit the contents of your build profile
  4665. accordingly.
  4666. </para>
  4667. <para>
  4668. Finally, to run the build, execute the following command once
  4669. inside your <literal>appDir</literal> or
  4670. <literal>appDir/scripts</literal> directory:
  4671. </para>
  4672. <programlisting language="javascript">
  4673. node ../../r.js -o app.build.js
  4674. </programlisting>
  4675. <para>
  4676. That’s it. As long as you have UglifyJS/Closure tools setup
  4677. correctly, r.js should be able to easily optimize your entire
  4678. Backbone project in just a few key-strokes. If you would like to
  4679. learn more about build profiles, James Burke has a
  4680. <ulink url="https://github.com/jrburke/r.js/blob/master/build/example.build.js">heavily
  4681. commented sample file</ulink> with all the possible options
  4682. available.
  4683. </para>
  4684. </sect2>
  4685. <sect2 id="practical-building-a-modular-backbone-app-with-amd-requirejs">
  4686. <title><a name="practicalrequirejs">Practical: Building a modular
  4687. Backbone app with AMD &amp; RequireJS</a></title>
  4688. <para>
  4689. In this chapter, we’ll look at our first practical Backbone &amp;
  4690. RequireJS project - how to build a modular Todo application. The
  4691. application will allow us to add new todos, edit new todos and
  4692. clear todo items that have been marked as completed. For a more
  4693. advanced practical, see the section on mobile Backbone
  4694. development.
  4695. </para>
  4696. <para>
  4697. The complete code for the application can can be found in the
  4698. <literal>practicals/modular-todo-app</literal> folder of this repo
  4699. (thanks to Thomas Davis and Jérôme Gravel-Niquet). Alternatively
  4700. grab a copy of my side-project
  4701. <ulink url="https://github.com/addyosmani/todomvc">TodoMVC</ulink>
  4702. which contains the sources to both AMD and non-AMD versions.
  4703. </para>
  4704. <para>
  4705. <emphasis role="strong">Note:</emphasis> Thomas may be covering a
  4706. practical on this exercise in more detail on
  4707. <ulink url="http://backbonetutorials.com">backbonetutorials.com</ulink>
  4708. at some point soon, but for this section I’ll be covering what I
  4709. consider the core concepts.
  4710. </para>
  4711. <sect3 id="overview">
  4712. <title>Overview</title>
  4713. <para>
  4714. Writing a <quote>modular</quote> Backbone application can be a
  4715. straight-forward process. There are however, some key conceptual
  4716. differences to be aware of if opting to use AMD as your module
  4717. format of choice:
  4718. </para>
  4719. <itemizedlist>
  4720. <listitem>
  4721. <para>
  4722. As AMD isn’t a standard native to JavaScript or the browser,
  4723. it’s necessary to use a script loader (such as RequireJS or
  4724. curl.js) in order to support defining components and modules
  4725. using this module format. As we’ve already reviewed, there
  4726. are a number of advantages to using the AMD as well as
  4727. RequireJS to assist here.
  4728. </para>
  4729. </listitem>
  4730. <listitem>
  4731. <para>
  4732. Models, views, controllers and routers need to be
  4733. encapsulated <emphasis>using</emphasis> the AMD-format. This
  4734. allows each component of our Backbone application to cleanly
  4735. manage dependencies (e.g collections required by a view) in
  4736. the same way that AMD allows non-Backbone modules to.
  4737. </para>
  4738. </listitem>
  4739. <listitem>
  4740. <para>
  4741. Non-Backbone components/modules (such as utilities or
  4742. application helpers) can also be encapsulated using AMD. I
  4743. encourage you to try developing these modules in such a way
  4744. that they can both be used and tested independent of your
  4745. Backbone code as this will increase their ability to be
  4746. re-used elsewhere.
  4747. </para>
  4748. </listitem>
  4749. </itemizedlist>
  4750. <para>
  4751. Now that we’ve reviewed the basics, let’s take a look at
  4752. developing our application. For reference, the structure of our
  4753. app is as follows:
  4754. </para>
  4755. <programlisting>
  4756. index.html
  4757. ...js/
  4758. main.js
  4759. .../models
  4760. todo.js
  4761. .../views
  4762. app.js
  4763. todos.js
  4764. .../collections
  4765. todos.js
  4766. .../templates
  4767. stats.html
  4768. todos.html
  4769. ../libs
  4770. .../backbone
  4771. .../jquery
  4772. .../underscore
  4773. .../require
  4774. require.js
  4775. text.js
  4776. ...css/
  4777. </programlisting>
  4778. </sect3>
  4779. <sect3 id="markup">
  4780. <title>Markup</title>
  4781. <para>
  4782. The markup for the application is relatively simple and consists
  4783. of three primary parts: an input section for entering new todo
  4784. items (<literal>create-todo</literal>), a list section to
  4785. display existing items (which can also be edited in-place)
  4786. (<literal>todo-list</literal>) and finally a section summarizing
  4787. how many items are left to be completed
  4788. (<literal>todo-stats</literal>).
  4789. </para>
  4790. <programlisting>
  4791. &lt;div id=&quot;todoapp&quot;&gt;
  4792. &lt;div class=&quot;content&quot;&gt;
  4793. &lt;div id=&quot;create-todo&quot;&gt;
  4794. &lt;input id=&quot;new-todo&quot; placeholder=&quot;What needs to be done?&quot; type=&quot;text&quot; /&gt;
  4795. &lt;span class=&quot;ui-tooltip-top&quot;&gt;Press Enter to save this task&lt;/span&gt;
  4796. &lt;/div&gt;
  4797. &lt;div id=&quot;todos&quot;&gt;
  4798. &lt;ul id=&quot;todo-list&quot;&gt;&lt;/ul&gt;
  4799. &lt;/div&gt;
  4800. &lt;div id=&quot;todo-stats&quot;&gt;&lt;/div&gt;
  4801. &lt;/div&gt;
  4802. &lt;/div&gt;
  4803. </programlisting>
  4804. <para>
  4805. The rest of the tutorial will now focus on the JavaScript side
  4806. of the practical.
  4807. </para>
  4808. </sect3>
  4809. <sect3 id="configuration-options">
  4810. <title>Configuration options</title>
  4811. <para>
  4812. If you’ve read the earlier chapter on AMD, you may have noticed
  4813. that explicitly needing to define each dependency a Backbone
  4814. module (view, collection or other module) may require with it
  4815. can get a little tedious. This can however be improved.
  4816. </para>
  4817. <para>
  4818. In order to simplify referencing common paths the modules in our
  4819. application may use, we use a RequireJS
  4820. <ulink url="http://requirejs.org/docs/api.html#config">configuration
  4821. object</ulink>, which is typically defined as a top-level script
  4822. file. Configuration objects have a number of useful
  4823. capabilities, the most useful being mode name-mapping. Name-maps
  4824. are basically a key:value pair, where the key defines the alias
  4825. you wish to use for a path and the value represents the true
  4826. location of the path.
  4827. </para>
  4828. <para>
  4829. In the code-sample below, you can see some typical examples of
  4830. common name-maps which include: <literal>backbone</literal>,
  4831. <literal>underscore</literal>, <literal>jquery</literal> and
  4832. depending on your choice, the RequireJS <literal>text</literal>
  4833. plugin, which assists with loading text assets like templates.
  4834. </para>
  4835. <para>
  4836. <emphasis role="strong">main.js</emphasis>
  4837. </para>
  4838. <programlisting language="javascript">
  4839. require.config({
  4840. baseUrl:'../',
  4841. paths: {
  4842. jquery: 'libs/jquery/jquery-min',
  4843. underscore: 'libs/underscore/underscore-min',
  4844. backbone: 'libs/backbone/backbone-optamd3-min',
  4845. text: 'libs/require/text'
  4846. }
  4847. });
  4848. require(['views/app'], function(AppView){
  4849. var app_view = new AppView;
  4850. });
  4851. </programlisting>
  4852. <para>
  4853. The <literal>require()</literal> at the end of our main.js file
  4854. is simply there so we can load and instantiation the primary
  4855. view for our application (<literal>views/app.js</literal>).
  4856. You’ll commonly see both this and the configuration object
  4857. included the most top-level script file for a project.
  4858. </para>
  4859. <para>
  4860. In addition to offering name-mapping, the configuration object
  4861. can be used to define additional properties such as
  4862. <literal>waitSeconds</literal> - the number of seconds to wait
  4863. before script loading times out and <literal>locale</literal>,
  4864. should you wish to load up i18n bundles for custom languages.
  4865. The <literal>baseUrl</literal> is simply the path to use for
  4866. module lookups.
  4867. </para>
  4868. <para>
  4869. For more information on configuration objects, please feel free
  4870. to check out the excellent guide to them in the
  4871. <ulink url="http://requirejs.org/docs/api.html#config">RequireJS
  4872. docs</ulink>.
  4873. </para>
  4874. </sect3>
  4875. <sect3 id="modularizing-our-models-views-and-collections">
  4876. <title>Modularizing our models, views and collections</title>
  4877. <para>
  4878. Before we dive into AMD-wrapped versions of our Backbone
  4879. components, let’s review a sample of a non-AMD view. The
  4880. following view listens for changes to its model (a Todo item)
  4881. and re-renders if a user edits the value of the item.
  4882. </para>
  4883. <programlisting language="javascript">
  4884. var TodoView = Backbone.View.extend({
  4885. //... is a list tag.
  4886. tagName: &quot;li&quot;,
  4887. // Cache the template function for a single item.
  4888. template: _.template($('#item-template').html()),
  4889. // The DOM events specific to an item.
  4890. events: {
  4891. &quot;click .check&quot; : &quot;toggleDone&quot;,
  4892. &quot;dblclick div.todo-content&quot; : &quot;edit&quot;,
  4893. &quot;click span.todo-destroy&quot; : &quot;clear&quot;,
  4894. &quot;keypress .todo-input&quot; : &quot;updateOnEnter&quot;
  4895. },
  4896. // The TodoView listens for changes to its model, re-rendering. Since there's
  4897. // a one-to-one correspondence between a **Todo** and a **TodoView** in this
  4898. // app, we set a direct reference on the model for convenience.
  4899. initialize: function() {
  4900. this.model.bind('change', this.render, this);
  4901. this.model.view = this;
  4902. },
  4903. ...
  4904. </programlisting>
  4905. <para>
  4906. Note how for templating the common practice of referencing a
  4907. script by an ID (or other selector) and obtaining its value is
  4908. used. This of course requires that the template being accessed
  4909. is implicitly defined in our markup. The following is the
  4910. <quote>embedded</quote> version of our template being referenced
  4911. above:
  4912. </para>
  4913. <programlisting>
  4914. &lt;script type=&quot;text/template&quot; id=&quot;item-template&quot;&gt;
  4915. &lt;div class=&quot;todo &lt;%= done ? 'done' : '' %&gt;&quot;&gt;
  4916. &lt;div class=&quot;display&quot;&gt;
  4917. &lt;input class=&quot;check&quot; type=&quot;checkbox&quot; &lt;%= done ? 'checked=&quot;checked&quot;' : '' %&gt; /&gt;
  4918. &lt;div class=&quot;todo-content&quot;&gt;&lt;/div&gt;
  4919. &lt;span class=&quot;todo-destroy&quot;&gt;&lt;/span&gt;
  4920. &lt;/div&gt;
  4921. &lt;div class=&quot;edit&quot;&gt;
  4922. &lt;input class=&quot;todo-input&quot; type=&quot;text&quot; value=&quot;&quot; /&gt;
  4923. &lt;/div&gt;
  4924. &lt;/div&gt;
  4925. &lt;/script&gt;
  4926. </programlisting>
  4927. <para>
  4928. Whilst there is nothing wrong with the template itself, once we
  4929. begin to develop larger applications requiring multiple
  4930. templates, including them all in our markup on page-load can
  4931. quickly become both unmanageable and come with performance
  4932. costs. We’ll look at solving this problem in a minute.
  4933. </para>
  4934. <para>
  4935. Let’s now take a look at the AMD-version of our view. As
  4936. discussed earlier, the <quote>module</quote> is wrapped using
  4937. AMD’s <literal>define()</literal> which allows us to specify the
  4938. dependencies our view requires. Using the mapped paths to
  4939. <quote>jquery</quote> etc. simplifies referencing common
  4940. dependencies and instances of dependencies are themselves mapped
  4941. to local variables that we can access (e.g <quote>jquery</quote>
  4942. is mapped to <literal>$</literal>).
  4943. </para>
  4944. <para>
  4945. <emphasis role="strong">views/todos.js</emphasis>
  4946. </para>
  4947. <programlisting language="javascript">
  4948. define([
  4949. 'jquery',
  4950. 'underscore',
  4951. 'backbone',
  4952. 'text!templates/todos.html'
  4953. ], function($, _, Backbone, todosTemplate){
  4954. var TodoView = Backbone.View.extend({
  4955. //... is a list tag.
  4956. tagName: &quot;li&quot;,
  4957. // Cache the template function for a single item.
  4958. template: _.template(todosTemplate),
  4959. // The DOM events specific to an item.
  4960. events: {
  4961. &quot;click .check&quot; : &quot;toggleDone&quot;,
  4962. &quot;dblclick div.todo-content&quot; : &quot;edit&quot;,
  4963. &quot;click span.todo-destroy&quot; : &quot;clear&quot;,
  4964. &quot;keypress .todo-input&quot; : &quot;updateOnEnter&quot;
  4965. },
  4966. // The TodoView listens for changes to its model, re-rendering. Since there's
  4967. // a one-to-one correspondence between a **Todo** and a **TodoView** in this
  4968. // app, we set a direct reference on the model for convenience.
  4969. initialize: function() {
  4970. this.model.bind('change', this.render, this);
  4971. this.model.view = this;
  4972. },
  4973. // Re-render the contents of the todo item.
  4974. render: function() {
  4975. $(this.el).html(this.template(this.model.toJSON()));
  4976. this.setContent();
  4977. return this;
  4978. },
  4979. // Use `jQuery.text` to set the contents of the todo item.
  4980. setContent: function() {
  4981. var content = this.model.get('content');
  4982. this.$('.todo-content').text(content);
  4983. this.input = this.$('.todo-input');
  4984. this.input.bind('blur', this.close);
  4985. this.input.val(content);
  4986. },
  4987. ...
  4988. </programlisting>
  4989. <para>
  4990. From a maintenance perspective, there’s nothing logically
  4991. different in this version of our view, except for how we
  4992. approach templating.
  4993. </para>
  4994. <para>
  4995. Using the RequireJS text plugin (the dependency marked
  4996. <literal>text</literal>), we can actually store all of the
  4997. contents for the template we looked at earlier in an external
  4998. file (todos.html).
  4999. </para>
  5000. <para>
  5001. <emphasis role="strong">templates/todos.html</emphasis>
  5002. </para>
  5003. <programlisting language="html">
  5004. &lt;div class=&quot;todo &lt;%= done ? 'done' : '' %&gt;&quot;&gt;
  5005. &lt;div class=&quot;display&quot;&gt;
  5006. &lt;input class=&quot;check&quot; type=&quot;checkbox&quot; &lt;%= done ? 'checked=&quot;checked&quot;' : '' %&gt; /&gt;
  5007. &lt;div class=&quot;todo-content&quot;&gt;&lt;/div&gt;
  5008. &lt;span class=&quot;todo-destroy&quot;&gt;&lt;/span&gt;
  5009. &lt;/div&gt;
  5010. &lt;div class=&quot;edit&quot;&gt;
  5011. &lt;input class=&quot;todo-input&quot; type=&quot;text&quot; value=&quot;&quot; /&gt;
  5012. &lt;/div&gt;
  5013. &lt;/div&gt;
  5014. </programlisting>
  5015. <para>
  5016. There’s no longer a need to be concerned with IDs for the
  5017. template as we can map it’s contents to a local variable (in
  5018. this case <literal>todosTemplate</literal>). We then simply pass
  5019. this to the Underscore.js templating function
  5020. <literal>_.template()</literal> the same way we normally would
  5021. have the value of our template script.
  5022. </para>
  5023. <para>
  5024. Next, let’s look at how to define models as dependencies which
  5025. can be pulled into collections. Here’s an AMD-compatible model
  5026. module, which has two default values: a
  5027. <literal>content</literal> attribute for the content of a Todo
  5028. item and a boolean <literal>done</literal> state, allowing us to
  5029. trigger whether the item has been completed or not.
  5030. </para>
  5031. <para>
  5032. <emphasis role="strong">models/todo.js</emphasis>
  5033. </para>
  5034. <programlisting language="javascript">
  5035. define(['underscore', 'backbone'], function(_, Backbone) {
  5036. var TodoModel = Backbone.Model.extend({
  5037. // Default attributes for the todo.
  5038. defaults: {
  5039. // Ensure that each todo created has `content`.
  5040. content: &quot;empty todo...&quot;,
  5041. done: false
  5042. },
  5043. initialize: function() {
  5044. },
  5045. // Toggle the `done` state of this todo item.
  5046. toggle: function() {
  5047. this.save({done: !this.get(&quot;done&quot;)});
  5048. },
  5049. // Remove this Todo from *localStorage* and delete its view.
  5050. clear: function() {
  5051. this.destroy();
  5052. this.view.remove();
  5053. }
  5054. });
  5055. return TodoModel;
  5056. });
  5057. </programlisting>
  5058. <para>
  5059. As per other types of dependencies, we can easily map our model
  5060. module to a local variable (in this case
  5061. <literal>Todo</literal>) so it can be referenced as the model to
  5062. use for our <literal>TodosCollection</literal>. This collection
  5063. also supports a simple <literal>done()</literal> filter for
  5064. narrowing down Todo items that have been completed and a
  5065. <literal>remaining()</literal> filter for those that are still
  5066. outstanding.
  5067. </para>
  5068. <para>
  5069. <emphasis role="strong">collections/todos.js</emphasis>
  5070. </para>
  5071. <programlisting language="javascript">
  5072. define([
  5073. 'underscore',
  5074. 'backbone',
  5075. 'libs/backbone/localstorage',
  5076. 'models/todo'
  5077. ], function(_, Backbone, Store, Todo){
  5078. var TodosCollection = Backbone.Collection.extend({
  5079. // Reference to this collection's model.
  5080. model: Todo,
  5081. // Save all of the todo items under the `&quot;todos&quot;` namespace.
  5082. localStorage: new Store(&quot;todos&quot;),
  5083. // Filter down the list of all todo items that are finished.
  5084. done: function() {
  5085. return this.filter(function(todo){ return todo.get('done'); });
  5086. },
  5087. // Filter down the list to only todo items that are still not finished.
  5088. remaining: function() {
  5089. return this.without.apply(this, this.done());
  5090. },
  5091. ...
  5092. </programlisting>
  5093. <para>
  5094. In addition to allowing users to add new Todo items from views
  5095. (which we then insert as models in a collection), we ideally
  5096. also want to be able to display how many items have been
  5097. completed and how many are remaining. We’ve already defined
  5098. filters that can provide us this information in the above
  5099. collection, so let’s use them in our main application view.
  5100. </para>
  5101. <para>
  5102. <emphasis role="strong">views/app.js</emphasis>
  5103. </para>
  5104. <programlisting language="javascript">
  5105. define([
  5106. 'jquery',
  5107. 'underscore',
  5108. 'backbone',
  5109. 'collections/todos',
  5110. 'views/todos',
  5111. 'text!templates/stats.html'
  5112. ], function($, _, Backbone, Todos, TodoView, statsTemplate){
  5113. var AppView = Backbone.View.extend({
  5114. // Instead of generating a new element, bind to the existing skeleton of
  5115. // the App already present in the HTML.
  5116. el: $(&quot;#todoapp&quot;),
  5117. // Our template for the line of statistics at the bottom of the app.
  5118. statsTemplate: _.template(statsTemplate),
  5119. // ...events, initialize() etc. can be seen in the complete file
  5120. // Re-rendering the App just means refreshing the statistics -- the rest
  5121. // of the app doesn't change.
  5122. render: function() {
  5123. var done = Todos.done().length;
  5124. this.$('#todo-stats').html(this.statsTemplate({
  5125. total: Todos.length,
  5126. done: Todos.done().length,
  5127. remaining: Todos.remaining().length
  5128. }));
  5129. },
  5130. ...
  5131. </programlisting>
  5132. <para>
  5133. Above, we map the second template for this project,
  5134. <literal>templates/stats.html</literal> to
  5135. <literal>statsTemplate</literal> which is used for rendering the
  5136. overall <literal>done</literal> and <literal>remaining</literal>
  5137. states. This works by simply passing our template the length of
  5138. our overall Todos collection (<literal>Todos.length</literal> -
  5139. the number of Todo items created so far) and similarly the
  5140. length (counts) for items that have been completed
  5141. (<literal>Todos.done().length</literal>) or are remaining
  5142. (<literal>Todos.remaining().length</literal>).
  5143. </para>
  5144. <para>
  5145. The contents of our <literal>statsTemplate</literal> can be seen
  5146. below. It’s nothing too complicated, but does use ternary
  5147. conditions to evaluate whether we should state there’s <quote>1
  5148. item</quote> or <quote>2 item<i>s</i></quote> in a particular
  5149. state.
  5150. </para>
  5151. <programlisting>
  5152. &lt;% if (total) { %&gt;
  5153. &lt;span class=&quot;todo-count&quot;&gt;
  5154. &lt;span class=&quot;number&quot;&gt;&lt;%= remaining %&gt;&lt;/span&gt;
  5155. &lt;span class=&quot;word&quot;&gt;&lt;%= remaining == 1 ? 'item' : 'items' %&gt;&lt;/span&gt; left.
  5156. &lt;/span&gt;
  5157. &lt;% } %&gt;
  5158. &lt;% if (done) { %&gt;
  5159. &lt;span class=&quot;todo-clear&quot;&gt;
  5160. &lt;a href=&quot;#&quot;&gt;
  5161. Clear &lt;span class=&quot;number-done&quot;&gt;&lt;%= done %&gt;&lt;/span&gt;
  5162. completed &lt;span class=&quot;word-done&quot;&gt;&lt;%= done == 1 ? 'item' : 'items' %&gt;&lt;/span&gt;
  5163. &lt;/a&gt;
  5164. &lt;/span&gt;
  5165. &lt;% } %&gt;
  5166. </programlisting>
  5167. <para>
  5168. The rest of the source for the Todo app mainly consists of code
  5169. for handling user and application events, but that rounds up
  5170. most of the core concepts for this practical.
  5171. </para>
  5172. <para>
  5173. To see how everything ties together, feel free to grab the
  5174. source by cloning this repo or browse it
  5175. <ulink url="https://github.com/addyosmani/backbone-fundamentals/tree/master/practicals/modular-todo-app">online</ulink>
  5176. to learn more. I hope you find it helpful!.
  5177. </para>
  5178. <para>
  5179. <emphasis role="strong">Note:</emphasis> While this first
  5180. practical doesn’t use a build profile as outlined in the chapter
  5181. on using the RequireJS optimizer, we will be using one in the
  5182. section on building mobile Backbone applications.
  5183. </para>
  5184. </sect3>
  5185. </sect2>
  5186. <sect2 id="decoupling-backbone-with-the-mediator-and-facade-patterns">
  5187. <title><a name="decouplingbackbone">Decoupling Backbone with the
  5188. Mediator and Facade patterns</a></title>
  5189. <para>
  5190. In this section we’ll discuss applying some of the concepts I
  5191. cover in my article on
  5192. <ulink url="http://addyosmani.com/largescalejavascript">Large-scale
  5193. JavaScript Application development</ulink> to Backbone.
  5194. </para>
  5195. <sect3 id="summary-1">
  5196. <title>Summary</title>
  5197. <para>
  5198. At a high-level, one architecture that works for such
  5199. applications is something which is:
  5200. </para>
  5201. <itemizedlist>
  5202. <listitem>
  5203. <para>
  5204. <emphasis role="strong">Highly decoupled</emphasis>:
  5205. encouraging modules to only publish and subscribe to events
  5206. of interest rather than directly communicating with each
  5207. other. This helps us to build applications who’s units of
  5208. code aren’t highly tied (coupled) together and can thus be
  5209. reused more easily.
  5210. </para>
  5211. </listitem>
  5212. <listitem>
  5213. <para>
  5214. <emphasis role="strong">Supports module-level
  5215. security</emphasis>: whereby modules are only able to
  5216. execute behavior they’ve been permitted to. Application
  5217. security is an area which is often overlooked in JavaScript
  5218. applications, but can be quite easily implemented in a
  5219. flexible manner.
  5220. </para>
  5221. </listitem>
  5222. <listitem>
  5223. <para>
  5224. <emphasis role="strong">Supports failover</emphasis>:
  5225. allowing an application continuing to function even if
  5226. particular modules fail. The typical example I give of this
  5227. is the GMail chat widget. Imagine being able to build
  5228. applications in a way that if one widget on the page fails
  5229. (e.g chat), the rest of your application (mail) can continue
  5230. to function without being affected.
  5231. </para>
  5232. </listitem>
  5233. </itemizedlist>
  5234. <para>
  5235. This is an architecture which has been implemented by a number
  5236. of different companies in the past, including Yahoo! (for their
  5237. modularized homepage - which Nicholas Zakas has
  5238. <ulink url="http://www.youtube.com/watch?v=vXjVFPosQHw">spoken</ulink>
  5239. about) and AOL for some of our upcoming projects.
  5240. </para>
  5241. <para>
  5242. The three design patterns that make this architecture possible
  5243. are the:
  5244. </para>
  5245. <itemizedlist>
  5246. <listitem>
  5247. <para>
  5248. <emphasis role="strong">Module pattern</emphasis>: used for
  5249. encapsulating unique blocks of code, where functions and
  5250. variables can be kept either public or private.
  5251. (<quote>private</quote> in the simulation of privacy sense,
  5252. as of course don’t have true privacy in JavaScript)
  5253. </para>
  5254. </listitem>
  5255. <listitem>
  5256. <para>
  5257. <emphasis role="strong">Mediator pattern</emphasis>: used
  5258. when the communication between modules may be complex, but
  5259. is still well defined. If it appears a system may have too
  5260. many relationships between modules in your code, it may be
  5261. time to have a central point of control, which is where the
  5262. pattern fits in.
  5263. </para>
  5264. </listitem>
  5265. <listitem>
  5266. <para>
  5267. <emphasis role="strong">Facade pattern</emphasis>: used for
  5268. providing a convenient higher-level interface to a larger
  5269. body of code, hiding its true underlying complexity
  5270. </para>
  5271. </listitem>
  5272. </itemizedlist>
  5273. <para>
  5274. Their specific roles in this architecture can be found below.
  5275. </para>
  5276. <itemizedlist>
  5277. <listitem>
  5278. <para>
  5279. <emphasis role="strong">Modules</emphasis>: There are almost
  5280. two concepts of what defines a module. As AMD is being used
  5281. as a module wrapper, technically each model, view and
  5282. collection can be considered a module. We then have the
  5283. concept of modules being distinct blocks of code outside of
  5284. just MVC/MV*. For the latter, these types of
  5285. <quote>modules</quote> are primarily concerned with
  5286. broadcasting and subscribing to events of interest rather
  5287. than directly communicating with each other.They are made
  5288. possible through the Mediator pattern.
  5289. </para>
  5290. </listitem>
  5291. <listitem>
  5292. <para>
  5293. <emphasis role="strong">Mediator</emphasis>: The mediator
  5294. has a varying role depending on just how you wish to
  5295. implement it. In my article, I mention using it as a module
  5296. manager with the ability to start and stop modules at will,
  5297. however when it comes to Backbone, I feel that simplifying
  5298. it down to the role of a central <quote>controller</quote>
  5299. that provides pub/sub capabilities should suffice. One can
  5300. of course go all out in terms of building a module system
  5301. that supports module starting, stopping, pausing etc,
  5302. however the scope of this is outside of this chapter.
  5303. </para>
  5304. </listitem>
  5305. <listitem>
  5306. <para>
  5307. <emphasis role="strong">Facade</emphasis>: This acts as a
  5308. secure middle-layer that both abstracts an application core
  5309. (Mediator) and relays messages from the modules back to the
  5310. Mediator so they don’t touch it directly. The Facade also
  5311. performs the duty of application security guard; it checks
  5312. event notifications from modules against a configuration
  5313. (permissions.js, which we will look at later) to ensure
  5314. requests from modules are only processed if they are
  5315. permitted to execute the behavior passed.
  5316. </para>
  5317. </listitem>
  5318. </itemizedlist>
  5319. <para>
  5320. For ease of reference, I sometimes refer to these three patterns
  5321. grouped together as Aura (a word that means subtle, luminous
  5322. light).
  5323. </para>
  5324. </sect3>
  5325. <sect3 id="practical-2">
  5326. <title>Practical</title>
  5327. <para>
  5328. For the practical section of this chapter, we’ll be extending
  5329. the well-known Backbone Todo application using the three
  5330. patterns mentioned above. The complete code for this section can
  5331. be found here: https://github.com/addyosmani/backbone-aura and
  5332. should ideally be run on at minimum, a local HTTP server.
  5333. </para>
  5334. <para>
  5335. The application is broken down into AMD modules that cover
  5336. everything from Backbone models through to application-level
  5337. modules. The views publish events of interest to the rest of the
  5338. application and modules can then subscribe to these event
  5339. notifications.
  5340. </para>
  5341. <para>
  5342. All subscriptions from modules go through a facade (or sandbox).
  5343. What this does is check against the subscriber name and the
  5344. <quote>channel/notification</quote> it’s attempting to subscribe
  5345. to. If a channel <emphasis>doesn’t</emphasis> have permissions
  5346. to be subscribed to (something established through
  5347. permissions.js), the subscription isn’t permitted.
  5348. </para>
  5349. <para>
  5350. <emphasis role="strong">Mediator</emphasis>
  5351. </para>
  5352. <para>
  5353. Found in <literal>aura/mediator.js</literal>
  5354. </para>
  5355. <para>
  5356. Below is a very simple AMD-wrapped implementation of the
  5357. mediator pattern, based on prior work by Ryan Florence. It
  5358. accepts as it’s input an object, to which it attaches
  5359. <literal>publish()</literal> and <literal>subscribe()</literal>
  5360. methods. In a larger application, the mediator can contain
  5361. additional utilities, such as handlers for initializing,
  5362. starting and stopping modules, but for demonstration purposes,
  5363. these two methods should work fine for our needs.
  5364. </para>
  5365. <programlisting language="javascript">
  5366. define([], function(obj){
  5367. var channels = {};
  5368. if (!obj) obj = {};
  5369. obj.subscribe = function (channel, subscription) {
  5370. if (!channels[channel]) channels[channel] = [];
  5371. channels[channel].push(subscription);
  5372. };
  5373. obj.publish = function (channel) {
  5374. if (!channels[channel]) return;
  5375. var args = [].slice.call(arguments, 1);
  5376. for (var i = 0, l = channels[channel].length; i &lt; l; i++) {
  5377. channels[channel][i].apply(this, args);
  5378. }
  5379. };
  5380. return obj;
  5381. });
  5382. </programlisting>
  5383. <para>
  5384. <emphasis role="strong">Facade</emphasis>
  5385. </para>
  5386. <para>
  5387. Found in <literal>aura/facade.js</literal>
  5388. </para>
  5389. <para>
  5390. Next, we have an implementation of the facade pattern. Now the
  5391. classical facade pattern applied to JavaScript would probably
  5392. look a little like this:
  5393. </para>
  5394. <programlisting language="javascript">
  5395. var module = (function() {
  5396. var _private = {
  5397. i:5,
  5398. get : function() {
  5399. console.log('current value:' + this.i);
  5400. },
  5401. set : function( val ) {
  5402. this.i = val;
  5403. },
  5404. run : function() {
  5405. console.log('running');
  5406. },
  5407. jump: function(){
  5408. console.log('jumping');
  5409. }
  5410. };
  5411. return {
  5412. facade : function( args ) {
  5413. _private.set(args.val);
  5414. _private.get();
  5415. if ( args.run ) {
  5416. _private.run();
  5417. }
  5418. }
  5419. }
  5420. }());
  5421. module.facade({run: true, val:10});
  5422. //outputs current value: 10, running
  5423. </programlisting>
  5424. <para>
  5425. It’s effectively a variation of the module pattern, where
  5426. instead of simply returning an interface of supported methods,
  5427. your API can completely hide the true implementation powering
  5428. it, returning something simpler. This allows the logic being
  5429. performed in the background to be as complex as necessary,
  5430. whilst all the end-user experiences is a simplified API they
  5431. pass options to (note how in our case, a single method
  5432. abstraction is exposed). This is a beautiful way of providing
  5433. APIs that can be easily consumed.
  5434. </para>
  5435. <para>
  5436. That said, to keep things simple, our implementation of an
  5437. AMD-compatible facade will act a little more like a proxy.
  5438. Modules will communicate directly through the facade to access
  5439. the mediator’s <literal>publish()</literal> and
  5440. <literal>subscribe()</literal> methods, however, they won’t as
  5441. such touch the mediator directly.This enables the facade to
  5442. provide application-level validation of any subscriptions and
  5443. publications made.
  5444. </para>
  5445. <para>
  5446. It also allows us to implement a simple, but flexible,
  5447. permissions checker (as seen below) which will validate
  5448. subscriptions made against a permissions configuration to see
  5449. whether it’s permitted or not.
  5450. </para>
  5451. <programlisting language="javascript">
  5452. define([ &quot;../aura/mediator&quot; , &quot;../aura/permissions&quot; ], function (mediator, permissions) {
  5453. var facade = facade || {};
  5454. facade.subscribe = function(subscriber, channel, callback){
  5455. // Note: Handling permissions/security is optional here
  5456. // The permissions check can be removed
  5457. // to just use the mediator directly.
  5458. if(permissions.validate(subscriber, channel)){
  5459. mediator.subscribe( channel, callback );
  5460. }
  5461. }
  5462. facade.publish = function(channel){
  5463. mediator.publish( channel );
  5464. }
  5465. return facade;
  5466. });
  5467. </programlisting>
  5468. <para>
  5469. <emphasis role="strong">Permissions</emphasis>
  5470. </para>
  5471. <para>
  5472. Found in <literal>aura/permissions.js</literal>
  5473. </para>
  5474. <para>
  5475. In our simple permissions configuration, we support checking
  5476. against subscription requests to establish whether they are
  5477. allowed to clear. This enforces a flexible security layer for
  5478. the application.
  5479. </para>
  5480. <para>
  5481. To visually see how this works, consider changing say,
  5482. permissions -&gt; renderDone -&gt; todoCounter to be false. This
  5483. will completely disable the application from from rendering or
  5484. displaying the counts component for Todo items left (because
  5485. they aren’t allowed to subscribe to that event notification).
  5486. The rest of the Todo app can still however be used without
  5487. issue.
  5488. </para>
  5489. <para>
  5490. It’s a very dumbed down example of the potential for application
  5491. security, but imagine how powerful this might be in a large app
  5492. with a significant number of visual widgets.
  5493. </para>
  5494. <programlisting language="javascript">
  5495. define([], function () {
  5496. // Permissions
  5497. // A permissions structure can support checking
  5498. // against subscriptions prior to allowing them
  5499. // to clear. This enforces a flexible security
  5500. // layer for your application.
  5501. var permissions = {
  5502. newContentAvailable: {
  5503. contentUpdater:true
  5504. },
  5505. endContentEditing:{
  5506. todoSaver:true
  5507. },
  5508. beginContentEditing:{
  5509. editFocus:true
  5510. },
  5511. addingNewTodo:{
  5512. todoTooltip:true
  5513. },
  5514. clearContent:{
  5515. garbageCollector:true
  5516. },
  5517. renderDone:{
  5518. todoCounter:true //switch to false to see what happens :)
  5519. },
  5520. destroyContent:{
  5521. todoRemover:true
  5522. },
  5523. createWhenEntered:{
  5524. keyboardManager:true
  5525. }
  5526. };
  5527. permissions.validate = function(subscriber, channel){
  5528. var test = permissions[channel][subscriber];
  5529. return test===undefined? false: test;
  5530. };
  5531. return permissions;
  5532. });
  5533. </programlisting>
  5534. <para>
  5535. <emphasis role="strong">Subscribers</emphasis>
  5536. </para>
  5537. <para>
  5538. Found in <literal>subscribers.js</literal>
  5539. </para>
  5540. <para>
  5541. Subscriber <quote>modules</quote> communicate through the facade
  5542. back to the mediator and perform actions when a notification
  5543. event of a particular name is published.
  5544. </para>
  5545. <para>
  5546. For example, when a user enters in a new piece of text for a
  5547. Todo item and hits <quote>enter</quote> the application
  5548. publishes a notification saying two things: a) a new Todo item
  5549. is available and b) the text content of the new item is X. It’s
  5550. then left up to the rest of the application to do with this
  5551. information whatever it wishes.
  5552. </para>
  5553. <para>
  5554. In order to update your Backbone application to primarily use
  5555. pub/sub, a lot of the work you may end up doing will be moving
  5556. logic coupled inside of specific views to modules outside of it
  5557. which are reactionary.
  5558. </para>
  5559. <para>
  5560. Take the <literal>todoSaver</literal> for example - it’s
  5561. responsibility is saving new Todo items to models once the a
  5562. <literal>notificationName</literal> called
  5563. <quote>newContentAvailable</quote> has fired. If you take a look
  5564. at the permissions structure in the last code sample, you’ll
  5565. notice that <quote>newContentAvailable</quote> is present there.
  5566. If I wanted to prevent subscribers from being able to subscribe
  5567. to this notification, I simply set it to a boolean value of
  5568. <literal>false</literal>.
  5569. </para>
  5570. <para>
  5571. Again, this is a massive oversimplification of how advanced your
  5572. permissions structures could get, but it’s certainly one way of
  5573. controlling what parts of your application can or can’t be
  5574. accessed by specific modules at any time.
  5575. </para>
  5576. <programlisting language="javascript">
  5577. define([&quot;jquery&quot;, &quot;underscore&quot;, &quot;aura/facade&quot;],
  5578. function ($, _, facade) {
  5579. // Subscription 'modules' for our views. These take the
  5580. // the form facade.subscribe( subscriberName, notificationName , callBack )
  5581. // Update view with latest todo content
  5582. // Subscribes to: newContentAvailable
  5583. facade.subscribe('contentUpdater', 'newContentAvailable', function (context) {
  5584. var content = context.model.get('content');
  5585. context.$('.todo-content').text(content);
  5586. context.input = context.$('.todo-input');
  5587. context.input.bind('blur', context.close);
  5588. context.input.val(content);
  5589. });
  5590. // Save models when a user has finishes editing
  5591. // Subscribes to: endContentEditing
  5592. facade.subscribe('todoSaver','endContentEditing', function (context) {
  5593. try {
  5594. context.model.save({
  5595. content: context.input.val()
  5596. });
  5597. $(context.el).removeClass(&quot;editing&quot;);
  5598. } catch (e) {
  5599. //console.log(e);
  5600. }
  5601. });
  5602. // Delete a todo when the user no longer needs it
  5603. // Subscribes to: destroyContent
  5604. facade.subscribe('todoRemover','destroyContent', function (context) {
  5605. try {
  5606. context.model.clear();
  5607. } catch (e) {
  5608. //console.log(e);
  5609. }
  5610. });
  5611. // When a user is adding a new entry, display a tooltip
  5612. // Subscribes to: addingNewTodo
  5613. facade.subscribe('todoTooltip','addingNewTodo', function (context, todo) {
  5614. var tooltip = context.$(&quot;.ui-tooltip-top&quot;);
  5615. var val = context.input.val();
  5616. tooltip.fadeOut();
  5617. if (context.tooltipTimeout) clearTimeout(context.tooltipTimeout);
  5618. if (val == '' || val == context.input.attr('placeholder')) return;
  5619. var show = function () {
  5620. tooltip.show().fadeIn();
  5621. };
  5622. context.tooltipTimeout = _.delay(show, 1000);
  5623. });
  5624. // Update editing UI on switching mode to editing content
  5625. // Subscribes to: beginContentEditing
  5626. facade.subscribe('editFocus','beginContentEditing', function (context) {
  5627. $(context.el).addClass(&quot;editing&quot;);
  5628. context.input.focus();
  5629. });
  5630. // Create a new todo entry
  5631. // Subscribes to: createWhenEntered
  5632. facade.subscribe('keyboardManager','createWhenEntered', function (context, e, todos) {
  5633. if (e.keyCode != 13) return;
  5634. todos.create(context.newAttributes());
  5635. context.input.val('');
  5636. });
  5637. // A Todo and remaining entry counter
  5638. // Subscribes to: renderDone
  5639. facade.subscribe('todoCounter','renderDone', function (context, Todos) {
  5640. var done = Todos.done().length;
  5641. context.$('#todo-stats').html(context.statsTemplate({
  5642. total: Todos.length,
  5643. done: Todos.done().length,
  5644. remaining: Todos.remaining().length
  5645. }));
  5646. });
  5647. // Clear all completed todos when clearContent is dispatched
  5648. // Subscribes to: clearContent
  5649. facade.subscribe('garbageCollector','clearContent', function (Todos) {
  5650. _.each(Todos.done(), function (todo) {
  5651. todo.clear();
  5652. });
  5653. });
  5654. });
  5655. </programlisting>
  5656. <para>
  5657. That’s it for this section. If you’ve been intrigued by some of
  5658. the concepts covered, I encourage you to consider taking a look
  5659. at my
  5660. <ulink url="http://addyosmani.com/blog/large-scale-javascript-application-architecture/">slides</ulink>
  5661. on Large-scale JS from the jQuery Summit or my longer post on
  5662. the topic
  5663. <ulink url="http://addyosmani.com/largescalejavascript">here</ulink>
  5664. for more information.
  5665. </para>
  5666. </sect3>
  5667. </sect2>
  5668. <sect2 id="paginating-backbone.js-requests-collections">
  5669. <title><a name="pagination">Paginating Backbone.js Requests &amp;
  5670. Collections</a></title>
  5671. <para>
  5672. Pagination is a ubiquitous problem we often find ourselves needing
  5673. to solve on the web. Perhaps most predominantly when working with
  5674. back-end APIs and JavaScript-heavy clients which consume them.
  5675. </para>
  5676. <para>
  5677. On this topic, we’re going to go through a set of **pagination
  5678. components ** I wrote for Backbone.js, which should hopefully come
  5679. in useful if you’re working on applications which need to tackle
  5680. this problem. They’re part of an extension called
  5681. <ulink url="http://github.com/addyosmani/backbone.paginator">Backbone.Paginator</ulink>.
  5682. </para>
  5683. <para>
  5684. When working with a structural framework like Backbone.js, the
  5685. three types of pagination we are most likely to run into are:
  5686. </para>
  5687. <para>
  5688. **Requests to a service layer (API) **- e.g query for results
  5689. containing the term <quote>Brendan</quote> - if 5,000 results are
  5690. available only display 20 results per page (leaving us with 250
  5691. possible result pages that can be navigated to).
  5692. </para>
  5693. <para>
  5694. This problem actually has quite a great deal more to it, such as
  5695. maintaining persistence of other URL parameters (e.g sort, query,
  5696. order) which can change based on a user’s search configuration in
  5697. a UI. One also had to think of a clean way of hooking views up to
  5698. this pagination so you can easily navigate between pages (e.g
  5699. First, Last, Next, Previous, 1,2,3), manage the number of results
  5700. displayed per page and so on.
  5701. </para>
  5702. <para>
  5703. <emphasis role="strong">Further client-side pagination of data
  5704. returned -</emphasis> e.g we’ve been returned a JSON esponse
  5705. containing 100 results. Rather than displaying all 100 to the
  5706. user, we only display 20 of these results within a navigatable UI
  5707. in the browser.
  5708. </para>
  5709. <para>
  5710. Similar to the request problem, client-pagination has its own
  5711. challenges like navigation once again (Next, Previous, 1,2,3),
  5712. sorting, order, switching the number of results to display per
  5713. page and so on.
  5714. </para>
  5715. <para>
  5716. <emphasis role="strong">Infinite results</emphasis> - with
  5717. services such as Facebook, the concept of numeric pagination is
  5718. instead replaced with a <quote>Load More</quote> or <quote>View
  5719. More</quote> button. Triggering this normally fetches the next
  5720. <quote>page</quote> of N results but rather than replacing the
  5721. previous set of results loaded entirely, we simply append to them
  5722. instead.
  5723. </para>
  5724. <para>
  5725. A request pager which simply appends results in a view rather than
  5726. replacing on each new fetch is effectively an
  5727. <quote>infinite</quote> pager.
  5728. </para>
  5729. <para>
  5730. <emphasis role="strong">Let’s now take a look at exactly what
  5731. we’re getting out of the box:</emphasis>
  5732. </para>
  5733. <para>
  5734. <emphasis><ulink url="http://addyosmani.github.com/backbone.paginator/">Backbone.Paginator</ulink>
  5735. is a set of opinionated components for paginating collections of
  5736. data using Backbone.js. It aims to provide both solutions for
  5737. assisting with pagination of requests to a server (e.g an API) as
  5738. well as pagination of single-loads of data, where we may wish to
  5739. further paginate a collection of N results into M pages within a
  5740. view.</emphasis>
  5741. </para>
  5742. </sect2>
  5743. <sect2 id="paginators-pieces">
  5744. <title>Paginator’s pieces</title>
  5745. <para>
  5746. Backbone.Paginator supports two main pagination components:
  5747. </para>
  5748. <itemizedlist>
  5749. <listitem>
  5750. <para>
  5751. <emphasis role="strong">Backbone.Paginator.requestPager</emphasis>:
  5752. For pagination of requests between a client and a server-side
  5753. API
  5754. </para>
  5755. </listitem>
  5756. <listitem>
  5757. <para>
  5758. <emphasis role="strong">Backbone.Paginator.clientPager</emphasis>:
  5759. For pagination of data returned from a server which you would
  5760. like to further paginate within the UI (e.g 60 results are
  5761. returned, paginate into 3 pages of 20)
  5762. </para>
  5763. </listitem>
  5764. </itemizedlist>
  5765. </sect2>
  5766. <sect2 id="downloads-and-source-code">
  5767. <title>Downloads And Source Code</title>
  5768. <para>
  5769. You can either download the raw source code for the project, fork
  5770. the repository or use one of these links:
  5771. </para>
  5772. <itemizedlist>
  5773. <listitem>
  5774. <para>
  5775. Production:
  5776. <ulink url="https://raw.github.com/addyosmani/backbone.baginator/master/dist/backbone.paginator.min.js">production</ulink>
  5777. </para>
  5778. </listitem>
  5779. <listitem>
  5780. <para>
  5781. Development:
  5782. <ulink url="https://raw.github.com/addyosmani/backbone.baginator/master/dist/backbone.paginator.js">development
  5783. version</ulink>
  5784. </para>
  5785. </listitem>
  5786. <listitem>
  5787. <para>
  5788. Examples + Source :
  5789. <ulink url="https://github.com/addyosmani/backbone.paginator/zipball/v0.153">zipball</ulink>
  5790. </para>
  5791. </listitem>
  5792. <listitem>
  5793. <para>
  5794. <ulink url="">Repository</ulink>http://github.com/addyosmani/backbone.paginator)
  5795. </para>
  5796. </listitem>
  5797. </itemizedlist>
  5798. </sect2>
  5799. <sect2 id="live-examples">
  5800. <title>Live Examples</title>
  5801. <para>
  5802. Live previews of both pagination components using the Netflix API
  5803. can be found below. Download the tarball or fork the repository to
  5804. experiment with these examples further.
  5805. </para>
  5806. <para>
  5807. Demo 1:
  5808. <ulink url="http://addyosmani.github.com/backbone.paginator/examples/netflix-request-paging/index.html">Backbone.Paginator.requestPager()</ulink>
  5809. </para>
  5810. <img alt="" class="aligncenter size-large wp-image-4578" height="451" src="img/requestPager.png" style="margin-left:-17px;" width="600" />
  5811. <para>
  5812. Demo 2:
  5813. <ulink url="http://addyosmani.github.com/backbone.paginator/examples/netflix-client-paging/index.html">Backbone.Paginator.clientPager()</ulink>
  5814. </para>
  5815. <img alt="" class="aligncenter size-full wp-image-4579" height="462" src="img/clientPager.png" width="600" />
  5816. <para>
  5817. Demo 3:
  5818. <ulink url="http://addyosmani.github.com/backbone.paginator/examples/netflix-infinite-paging/index.html">Infinite
  5819. Pagination (Backbone.Paginator.requestPager())</ulink>
  5820. </para>
  5821. <img alt="" class="aligncenter size-large wp-image-4580" height="451" src="img/infinitepager.png" width="600" />
  5822. </sect2>
  5823. <sect2 id="paginator.requestpager">
  5824. <title>Paginator.requestPager</title>
  5825. <para>
  5826. In this section we’re going to walkthrough actually using the
  5827. requestPager.
  5828. </para>
  5829. <sect3 id="create-a-new-paginated-collection">
  5830. <title>1. Create a new Paginated collection</title>
  5831. <para>
  5832. First, we define a new Paginated collection using
  5833. <literal>Backbone.Paginator.requestPager()</literal> as follows:
  5834. </para>
  5835. <pre class="javascript" name="code">var PaginatedCollection = Backbone.Paginator.requestPager.extend({
  5836. </pre>
  5837. </sect3>
  5838. <sect3 id="set-the-model-and-base-url-for-the-collection-as-normal">
  5839. <title>2: Set the model and base URL for the collection as
  5840. normal</title>
  5841. <para>
  5842. Within our collection, we then (as normal) specify the model to
  5843. be used with this collection followed by the URL (or base URL)
  5844. for the service providing our data (e.g the Netflix API).
  5845. </para>
  5846. <programlisting language="javascript">
  5847. model: model,
  5848. url: 'http://odata.netflix.com/v2/Catalog/Titles?&amp;',
  5849. </programlisting>
  5850. </sect3>
  5851. <sect3 id="map-the-attributes-supported-by-your-api-url">
  5852. <title>3. Map the attributes supported by your API (URL)</title>
  5853. <para>
  5854. Next, we’re going to map the request (URL) parameters supported
  5855. by your API or backend data service back to attributes that are
  5856. internally used by Backbone.Paginator.
  5857. </para>
  5858. <para>
  5859. For example: the NetFlix API refers to it’s parameter for
  5860. stating how many results to skip ahead by as
  5861. <literal>$skip</literal> and it’s number of items to return per
  5862. page as <literal>$top</literal> (amongst others). We determine
  5863. these by looking at a sample URL pointing at the service:
  5864. </para>
  5865. <programlisting language="javascript">
  5866. http://odata.netflix.com/v2/Catalog/Titles?&amp;callback=callback&amp;$top=30&amp;$skip=30&amp;orderBy=ReleaseYear&amp;$inlinecount=allpages&amp;$format=json&amp;$callback=callback&amp;$filter=substringof%28%27the%27,%20Name%29%20eq%20true&amp;_=1332702202090
  5867. </programlisting>
  5868. <para>
  5869. We then simply map these parameters to the relevant Paginator
  5870. equivalents shown on the left hand side of the next snippets to
  5871. get everything working:
  5872. </para>
  5873. <programlisting language="javascript">
  5874. // @param-name for the query field in the
  5875. // request (e.g query/keywords/search)
  5876. queryAttribute: '$filter',
  5877. // @param-name for number of items to return per request/page
  5878. perPageAttribute: '$top',
  5879. // @param-name for how many results the request should skip ahead to
  5880. skipAttribute: '$skip',
  5881. // @param-name for the direction to sort in
  5882. sortAttribute: '$sort',
  5883. // @param-name for field to sort by
  5884. orderAttribute: '$orderBy',
  5885. // @param-name for the format of the request
  5886. formatAttribute: '$format',
  5887. // @param-name for a custom attribute
  5888. customAttribute1: '$inlinecount',
  5889. // @param-name for another custom attribute
  5890. customAttribute2: '$callback',
  5891. </programlisting>
  5892. <para>
  5893. <emphasis role="strong">Note</emphasis>: you can define support
  5894. for new custom attributes in Backbone.Paginator if needed (e.g
  5895. customAttribute1) for those that may be unique to your service.
  5896. </para>
  5897. </sect3>
  5898. <sect3 id="configure-the-default-pagination-query-and-sort-details-for-the-paginator">
  5899. <title>4. Configure the default pagination, query and sort details
  5900. for the paginator</title>
  5901. <para>
  5902. Now, let’s configure the default values in our collection for
  5903. these parameters so that as a user navigates through the
  5904. paginated UI, requests are able to continue querying with the
  5905. correct field to sort on, the right number of items to return
  5906. per request etc.
  5907. </para>
  5908. <para>
  5909. e.g: If we want to request the:
  5910. </para>
  5911. <itemizedlist>
  5912. <listitem>
  5913. <para>
  5914. 1st page of results
  5915. </para>
  5916. </listitem>
  5917. <listitem>
  5918. <para>
  5919. for the search query <quote>superman</quote>
  5920. </para>
  5921. </listitem>
  5922. <listitem>
  5923. <para>
  5924. in JSON format
  5925. </para>
  5926. </listitem>
  5927. <listitem>
  5928. <para>
  5929. sorted by release year
  5930. </para>
  5931. </listitem>
  5932. <listitem>
  5933. <para>
  5934. in ascending order
  5935. </para>
  5936. </listitem>
  5937. <listitem>
  5938. <para>
  5939. where only 30 results are returned per request
  5940. </para>
  5941. </listitem>
  5942. </itemizedlist>
  5943. <para>
  5944. This would look as follows:
  5945. </para>
  5946. <programlisting language="javascript">
  5947. // current page to query from the service
  5948. page: 5,
  5949. // The lowest page index your API allows to be accessed
  5950. firstPage: 0, //some begin with 1
  5951. // how many results to query from the service (i.e how many to return
  5952. // per request)
  5953. perPage: 30,
  5954. // maximum number of pages that can be queried from
  5955. // the server (only here as a default in case your
  5956. // service doesn't return the total pages available)
  5957. totalPages: 10,
  5958. // what field should the results be sorted on?
  5959. sortField: 'ReleaseYear',
  5960. // what direction should the results be sorted in?
  5961. sortDirection: 'asc',
  5962. // what would you like to query (search) from the service?
  5963. // as Netflix reqires additional parameters around the query
  5964. // we simply fill these around our search term
  5965. query: &quot;substringof('&quot; + escape('the') + &quot;',Name)&quot;,
  5966. // what format would you like to request results in?
  5967. format: 'json',
  5968. // what other custom parameters for the request do
  5969. // you require
  5970. // for your application?
  5971. customParam1: 'allpages',
  5972. customParam2: 'callback',
  5973. </programlisting>
  5974. <para>
  5975. As the particular API we’re using requires
  5976. <literal>callback</literal> and <literal>allpages</literal>
  5977. parameters to also be passed, we simply define the values for
  5978. these as custom parameters which can be mapped back to
  5979. requestPager as needed.
  5980. </para>
  5981. </sect3>
  5982. <sect3 id="finally-configure-collection.parse-and-were-done">
  5983. <title>5. Finally, configure Collection.parse() and we’re
  5984. done</title>
  5985. <para>
  5986. The last thing we need to do is configure our collection’s
  5987. <literal>parse()</literal> method. We want to ensure we’re
  5988. returning the correct part of our JSON response containing the
  5989. data our collection will be populated with, which below is
  5990. <literal>response.d.results</literal> (for the Netflix API).
  5991. </para>
  5992. <para>
  5993. You might also notice that we’re setting
  5994. <literal>this.totalPages</literal> to the total page count
  5995. returned by the API. This allows us to define the maximum number
  5996. of (result) pages available for the current/last request so that
  5997. we can clearly display this in the UI. It also allows us to
  5998. infuence whether clicking say, a <quote>next</quote> button
  5999. should proceed with a request or not.
  6000. </para>
  6001. <programlisting language="javascript">
  6002. parse: function (response) {
  6003. // Be sure to change this based on how your results
  6004. // are structured (e.g d.results is Netflix specific)
  6005. var tags = response.d.results;
  6006. //Normally this.totalPages would equal response.d.__count
  6007. //but as this particular NetFlix request only returns a
  6008. //total count of items for the search, we divide.
  6009. this.totalPages = Math.floor(response.d.__count / this.perPage);
  6010. return tags;
  6011. }
  6012. });
  6013. });
  6014. </programlisting>
  6015. </sect3>
  6016. <sect3 id="convenience-methods">
  6017. <title>Convenience methods:</title>
  6018. <para>
  6019. For your convenience, the following methods are made available
  6020. for use in your views to interact with the
  6021. <literal>requestPager</literal>:
  6022. </para>
  6023. <itemizedlist>
  6024. <listitem>
  6025. <para>
  6026. <emphasis role="strong">Collection.goTo(n)</emphasis> - go
  6027. to a specific page
  6028. </para>
  6029. </listitem>
  6030. <listitem>
  6031. <para>
  6032. <emphasis role="strong">Collection.requestNextPage()</emphasis>
  6033. - go to the next page
  6034. </para>
  6035. </listitem>
  6036. <listitem>
  6037. <para>
  6038. <emphasis role="strong">Collection.requestPreviousPage()</emphasis>
  6039. - go to the previous page
  6040. </para>
  6041. </listitem>
  6042. <listitem>
  6043. <para>
  6044. <emphasis role="strong">Collection.howManyPer(n)</emphasis>
  6045. - set the number of items to display per page
  6046. </para>
  6047. </listitem>
  6048. </itemizedlist>
  6049. </sect3>
  6050. </sect2>
  6051. <sect2 id="paginator.clientpager">
  6052. <title>Paginator.clientPager</title>
  6053. <para>
  6054. The <literal>clientPager</literal> works similar to the
  6055. <literal>requestPager</literal>, except that our configuration
  6056. values influence the pagination of data already returned at a
  6057. UI-level. Whilst not shown (yet) there is also a lot more UI logic
  6058. that ties in with the <literal>clientPager</literal>. An example
  6059. of this can be seen in ‘views/clientPagination.js.
  6060. </para>
  6061. <sect3 id="create-a-new-paginated-collection-with-a-model-and-url">
  6062. <title>1. Create a new paginated collection with a model and
  6063. URL</title>
  6064. <para>
  6065. As with <literal>requestPager</literal>, let’s first create a
  6066. new Paginated <literal>Backbone.Paginator.clientPager</literal>
  6067. collection, with a model and base URL:
  6068. </para>
  6069. <programlisting language="javascript">
  6070. var PaginatedCollection = Backbone.Paginator.clientPager.extend({
  6071. model: model,
  6072. url: 'http://odata.netflix.com/v2/Catalog/Titles?&amp;',
  6073. </programlisting>
  6074. </sect3>
  6075. <sect3 id="map-the-attributes-supported-by-your-api-url-1">
  6076. <title>2. Map the attributes supported by your API (URL)</title>
  6077. <para>
  6078. We’re similarly going to map request parameter names for your
  6079. API to those supported in the paginator:
  6080. </para>
  6081. <programlisting language="javascript">
  6082. perPageAttribute: '$top',
  6083. skipAttribute: '$skip',
  6084. orderAttribute: '$orderBy',
  6085. customAttribute1: '$inlinecount',
  6086. queryAttribute: '$filter',
  6087. formatAttribute: '$format',
  6088. customAttribute2: '$callback',
  6089. </programlisting>
  6090. </sect3>
  6091. <sect3 id="configure-how-to-paginate-data-at-a-ui-level">
  6092. <title>3. Configure how to paginate data at a UI-level</title>
  6093. <para>
  6094. We then get to configuration for the paginated data in the UI.
  6095. <literal>perPage</literal> specifies how many results to return
  6096. from the server whilst <literal>displayPerPage</literal>
  6097. configures how many of the items in returned results to display
  6098. per <quote>page</quote> in the UI. e.g If we request 100 results
  6099. and only display 20 per page, we have 5 sub-pages of results
  6100. that can be navigated through in the UI.
  6101. </para>
  6102. <programlisting language="javascript">
  6103. // M: how many results to query from the service
  6104. perPage: 40,
  6105. // N: how many results to display per 'page' within the UI
  6106. // Effectively M/N = the number of pages the data will be split into.
  6107. displayPerPage: 20,
  6108. </programlisting>
  6109. </sect3>
  6110. <sect3 id="configure-the-rest-of-the-request-parameter-default-values">
  6111. <title>4. Configure the rest of the request parameter default
  6112. values</title>
  6113. <para>
  6114. We can then configure default values for the rest of our request
  6115. parameters:
  6116. </para>
  6117. <programlisting language="javascript">
  6118. // current page to query from the service
  6119. page: 1,
  6120. // a default. This should be overridden in the collection's parse()
  6121. // sort direction
  6122. sortDirection: 'asc',
  6123. // sort field
  6124. sortField: 'ReleaseYear',
  6125. //or year(Instant/AvailableFrom)
  6126. // query
  6127. query: &quot;substringof('&quot; + escape('the') + &quot;',Name)&quot;,
  6128. // request format
  6129. format: 'json',
  6130. // custom parameters for the request that may be specific to your
  6131. // application
  6132. customParam1: 'allpages',
  6133. customParam2: 'callback',
  6134. </programlisting>
  6135. </sect3>
  6136. <sect3 id="finally-configure-collection.parse-and-were-done-1">
  6137. <title>5. Finally, configure Collection.parse() and we’re
  6138. done</title>
  6139. <para>
  6140. And finally we have our <literal>parse()</literal> method, which
  6141. in this case isn’t concerned with the total number of result
  6142. pages available on the server as we have our own total count of
  6143. pages for the paginated data in the UI.
  6144. </para>
  6145. <programlisting language="javascript">
  6146. parse: function (response) {
  6147. var tags = response.d.results;
  6148. return tags;
  6149. }
  6150. });
  6151. </programlisting>
  6152. </sect3>
  6153. <sect3 id="convenience-methods-1">
  6154. <title>Convenience methods:</title>
  6155. <para>
  6156. As mentioned, your views can hook into a number of convenience
  6157. methods to navigate around UI-paginated data. For
  6158. <literal>clientPager</literal> these include:
  6159. </para>
  6160. <itemizedlist>
  6161. <listitem>
  6162. <para>
  6163. <emphasis role="strong">Collection.goTo(n)</emphasis> - go
  6164. to a specific page
  6165. </para>
  6166. </listitem>
  6167. <listitem>
  6168. <para>
  6169. <emphasis role="strong">Collection.previousPage()</emphasis>
  6170. - go to the previous page
  6171. </para>
  6172. </listitem>
  6173. <listitem>
  6174. <para>
  6175. <emphasis role="strong">Collection.nextPage()</emphasis> -
  6176. go to the next page
  6177. </para>
  6178. </listitem>
  6179. <listitem>
  6180. <para>
  6181. <emphasis role="strong">Collection.howManyPer(n)</emphasis>
  6182. - set how many items to display per page
  6183. </para>
  6184. </listitem>
  6185. <listitem>
  6186. <para>
  6187. <emphasis role="strong">Collection.pager(sortBy,
  6188. sortDirection)</emphasis> - update sort on the current view
  6189. </para>
  6190. </listitem>
  6191. </itemizedlist>
  6192. </sect3>
  6193. </sect2>
  6194. <sect2 id="viewstemplates">
  6195. <title>Views/Templates</title>
  6196. <para>
  6197. Although the collection layer is perhaps the most important part
  6198. of Backbone.Paginator, it would be of little use without views
  6199. interacting with it. The project zipball comes with three complete
  6200. examples of using the components with the Netflix API, but here’s
  6201. a sample view and template from the
  6202. <literal>requestPager()</literal> example for those interested in
  6203. learning more:
  6204. </para>
  6205. <para>
  6206. First, we have a view for a pagination bar in our UI that allows
  6207. us to navigate around our paginated collection:
  6208. </para>
  6209. <programlisting language="javascript">
  6210. (function ( views ) {
  6211. views.PaginatedView = Backbone.View.extend({
  6212. events: {
  6213. 'click a.servernext': 'nextResultPage',
  6214. 'click a.serverprevious': 'previousResultPage',
  6215. 'click a.orderUpdate': 'updateSortBy',
  6216. 'click a.serverlast': 'gotoLast',
  6217. 'click a.page': 'gotoPage',
  6218. 'click a.serverfirst': 'gotoFirst',
  6219. 'click a.serverpage': 'gotoPage',
  6220. 'click .serverhowmany a': 'changeCount'
  6221. },
  6222. tagName: 'aside',
  6223. template: _.template($('#tmpServerPagination').html()),
  6224. initialize: function () {
  6225. this.collection.on('reset', this.render, this);
  6226. this.collection.on('change', this.render, this);
  6227. this.$el.appendTo('#pagination');
  6228. },
  6229. render: function () {
  6230. var html = this.template(this.collection.info());
  6231. this.$el.html(html);
  6232. },
  6233. updateSortBy: function (e) {
  6234. e.preventDefault();
  6235. var currentSort = $('#sortByField').val();
  6236. this.collection.updateOrder(currentSort);
  6237. },
  6238. nextResultPage: function (e) {
  6239. e.preventDefault();
  6240. this.collection.requestNextPage();
  6241. },
  6242. previousResultPage: function (e) {
  6243. e.preventDefault();
  6244. this.collection.requestPreviousPage();
  6245. },
  6246. gotoFirst: function (e) {
  6247. e.preventDefault();
  6248. this.collection.goTo(this.collection.information.firstPage);
  6249. },
  6250. gotoLast: function (e) {
  6251. e.preventDefault();
  6252. this.collection.goTo(this.collection.information.lastPage);
  6253. },
  6254. gotoPage: function (e) {
  6255. e.preventDefault();
  6256. var page = $(e.target).text();
  6257. this.collection.goTo(page);
  6258. },
  6259. changeCount: function (e) {
  6260. e.preventDefault();
  6261. var per = $(e.target).text();
  6262. this.collection.howManyPer(per);
  6263. }
  6264. });
  6265. })( app.views );
  6266. </programlisting>
  6267. <para>
  6268. which we use with a template like this to generate the necessary
  6269. pagination links (more are shown in the full example):
  6270. </para>
  6271. <programlisting language="html">
  6272. &lt;span class=&quot;divider&quot;&gt;/&lt;/span&gt;
  6273. &lt;% if (page &gt; firstPage) { %&gt;
  6274. &lt;a href=&quot;#&quot; class=&quot;serverprevious&quot;&gt;Previous&lt;/a&gt;
  6275. &lt;% }else{ %&gt;
  6276. &lt;span&gt;Previous&lt;/span&gt;
  6277. &lt;% }%&gt;
  6278. &lt;% if (page &lt; totalPages) { %&gt;
  6279. &lt;a href=&quot;#&quot; class=&quot;servernext&quot;&gt;Next&lt;/a&gt;
  6280. &lt;% } %&gt;
  6281. &lt;% if (firstPage != page) { %&gt;
  6282. &lt;a href=&quot;#&quot; class=&quot;serverfirst&quot;&gt;First&lt;/a&gt;
  6283. &lt;% } %&gt;
  6284. &lt;% if (lastPage != page) { %&gt;
  6285. &lt;a href=&quot;#&quot; class=&quot;serverlast&quot;&gt;Last&lt;/a&gt;
  6286. &lt;% } %&gt;
  6287. &lt;span class=&quot;divider&quot;&gt;/&lt;/span&gt;
  6288. &lt;span class=&quot;cell serverhowmany&quot;&gt;
  6289. Show
  6290. &lt;a href=&quot;#&quot; class=&quot;selected&quot;&gt;3&lt;/a&gt;
  6291. |
  6292. &lt;a href=&quot;#&quot; class=&quot;&quot;&gt;9&lt;/a&gt;
  6293. |
  6294. &lt;a href=&quot;#&quot; class=&quot;&quot;&gt;12&lt;/a&gt;
  6295. per page
  6296. &lt;/span&gt;
  6297. &lt;span class=&quot;divider&quot;&gt;/&lt;/span&gt;
  6298. &lt;span class=&quot;cell first records&quot;&gt;
  6299. Page: &lt;span class=&quot;current&quot;&gt;&lt;%= page %&gt;&lt;/span&gt;
  6300. of
  6301. &lt;span class=&quot;total&quot;&gt;&lt;%= totalPages %&gt;&lt;/span&gt;
  6302. shown
  6303. &lt;/span&gt;
  6304. &lt;span class=&quot;divider&quot;&gt;/&lt;/span&gt;
  6305. &lt;span class=&quot;cell sort&quot;&gt;
  6306. &lt;a href=&quot;#&quot; class=&quot;orderUpdate btn small&quot;&gt;Sort by:&lt;/a&gt;
  6307. &lt;/span&gt;
  6308. &lt;select id=&quot;sortByField&quot;&gt;
  6309. &lt;option value=&quot;cid&quot;&gt;Select a field to sort on&lt;/option&gt;
  6310. &lt;option value=&quot;ReleaseYear&quot;&gt;Release year&lt;/option&gt;
  6311. &lt;option value=&quot;ShortName&quot;&gt;Alphabetical&lt;/option&gt;
  6312. &lt;/select&gt;
  6313. &lt;/span&gt;
  6314. </programlisting>
  6315. </sect2>
  6316. <sect2 id="license">
  6317. <title>License</title>
  6318. <para>
  6319. Copyright © 2012 Addy Osmani. Licensed under the MIT license.
  6320. </para>
  6321. </sect2>
  6322. <sect2 id="backbone-jquery-mobile">
  6323. <title>Backbone &amp; jQuery Mobile</title>
  6324. <sect3 id="resolving-the-routing-conflicts">
  6325. <title>Resolving the routing conflicts</title>
  6326. <para>
  6327. The first major hurdle developers typically run into when
  6328. building Backbone applications with jQuery Mobile is that both
  6329. frameworks have their own opinions about how to handle
  6330. application navigation.
  6331. </para>
  6332. <para>
  6333. Backbone’s routers offer an explicit way to define custom
  6334. navigation routes through <literal>Backbone.Router</literal>,
  6335. whilst jQuery Mobile encourages the use of URL hash fragments to
  6336. reference separate <quote>pages</quote> or views in the same
  6337. document. jQuery Mobile also supports automatically pulling in
  6338. external content for links through XHR calls meaning that there
  6339. can be quite a lot of inter-framework confusion about what a
  6340. link pointing at <quote>#photo/id</quote> should actually be
  6341. doing.
  6342. </para>
  6343. <para>
  6344. Some of the solutions that have been previously proposed to
  6345. work-around this problem included manually patching Backbone or
  6346. jQuery Mobile. I discourage opting for these techniques as it
  6347. becomes necessary to manually patch your framework builds when
  6348. new releases get made upstream.
  6349. </para>
  6350. <para>
  6351. There’s also
  6352. <ulink url="https://github.com/azicchetti/jquerymobile-router">jQueryMobile
  6353. router</ulink>, which tries to solve this problem differently,
  6354. however I think my proposed solution is both simpler and allows
  6355. both frameworks to cohabit quite peacefully without the need to
  6356. extend either. What we’re after is a way to prevent one
  6357. framework from listening to hash changes so that we can fully
  6358. rely on the other (e.g. <literal>Backbone.Router</literal>) to
  6359. handle this for us exclusively.
  6360. </para>
  6361. <para>
  6362. Using jQuery Mobile this can be done by setting:
  6363. </para>
  6364. <programlisting language="javascript">
  6365. $.mobile.hashListeningEnabled = false;
  6366. </programlisting>
  6367. <para>
  6368. prior to initializing any of your other code.
  6369. </para>
  6370. <para>
  6371. I discovered this method looking through some jQuery Mobile
  6372. commits that didn’t make their way into the official docs, but
  6373. am happy to see that they are now covered here
  6374. http://jquerymobile.com/test/docs/api/globalconfig.html in more
  6375. detail.
  6376. </para>
  6377. <para>
  6378. The next question that arises is, if we’re preventing jQuery
  6379. Mobile from listening to URL hash changes, how can we still get
  6380. the benefit of being able to navigate to other sections in a
  6381. document using the built-in transitions and effects supported?
  6382. Good question. This can now be solve by simply calling
  6383. <literal>$.mobile.changePage()</literal> as follows:
  6384. </para>
  6385. <programlisting language="javascript">
  6386. var url = '#about',
  6387. effect = 'slideup',
  6388. reverse = false,
  6389. changeHash = false;
  6390. $.mobile.changePage( url , { transition: effect}, reverse, changeHash );
  6391. </programlisting>
  6392. <para>
  6393. In the above sample, <literal>url</literal> can refer to a URL
  6394. or a hash identifier to navigate to, <literal>effect</literal>
  6395. is simply the transition effect to animate the page in with and
  6396. the final two parameters decide the direction for the transition
  6397. (<literal>reverse</literal>) and whether or not the hash in the
  6398. address bar should be updated (<literal>changeHash</literal>).
  6399. With respect to the latter, I typically set this to false to
  6400. avoid managing two sources for hash updates, but feel free to
  6401. set this to true if you’re comfortable doing so.
  6402. </para>
  6403. <para>
  6404. <emphasis role="strong">Note:</emphasis> For some parallel work
  6405. being done to explore how well the jQuery Mobile Router plugin
  6406. works with Backbone, you may be interested in checking out
  6407. https://github.com/Filirom1/jquery-mobile-backbone-requirejs.
  6408. </para>
  6409. </sect3>
  6410. <sect3 id="practical-a-backbone-requirejsamd-app-with-jquery-mobile">
  6411. <title>Practical: A Backbone, RequireJS/AMD app with jQuery
  6412. Mobile</title>
  6413. <para>
  6414. <emphasis role="strong">Note:</emphasis> The code for this
  6415. practical can be found in
  6416. <literal>practicals/modular-mobile-app</literal>.
  6417. </para>
  6418. </sect3>
  6419. <sect3 id="getting-started-1">
  6420. <title>Getting started</title>
  6421. <para>
  6422. Once you feel comfortable with the
  6423. <ulink url="http://msdn.microsoft.com/en-us/scriptjunkie/hh377172.aspx">Backbone
  6424. fundamentals</ulink> and you’ve put together a rough wireframe
  6425. of the app you may wish to build, start to think about your
  6426. application architecture. Ideally, you’ll want to logically
  6427. separate concerns so that it’s as easy as possible to maintain
  6428. the app in the future.
  6429. </para>
  6430. <para>
  6431. <emphasis role="strong">Namespacing</emphasis>
  6432. </para>
  6433. <para>
  6434. For this application, I opted for the nested namespacing
  6435. pattern. Implemented correctly, this enables you to clearly
  6436. identify if items being referenced in your app are views, other
  6437. modules and so on. This initial structure is a sane place to
  6438. also include application defaults (unless you prefer maintaining
  6439. those in a separate file).
  6440. </para>
  6441. <programlisting language="javascript">
  6442. window.mobileSearch = window.mobileSearch || {
  6443. views: {
  6444. appview: new AppView
  6445. },
  6446. routers:{
  6447. workspace:new Workspace()
  6448. },
  6449. utils: utils,
  6450. defaults:{
  6451. resultsPerPage: 16,
  6452. safeSearch: 2,
  6453. maxDate:'',
  6454. minDate:'01/01/1970'
  6455. }
  6456. }
  6457. </programlisting>
  6458. <para>
  6459. <emphasis role="strong">Models</emphasis>
  6460. </para>
  6461. <para>
  6462. In the Flickly application, there are at least two unique types
  6463. of data that need to be modeled - search results and individual
  6464. photos, both of which contain additional meta-data like photo
  6465. titles. If you simplify this down, search results are actually
  6466. groups of photos in their own right, so the application only
  6467. requires:
  6468. </para>
  6469. <itemizedlist>
  6470. <listitem>
  6471. <para>
  6472. A single model (a photo or <quote>result</quote> entry)
  6473. </para>
  6474. </listitem>
  6475. <listitem>
  6476. <para>
  6477. A result collection (containing a group of result entries)
  6478. for search results
  6479. </para>
  6480. </listitem>
  6481. <listitem>
  6482. <para>
  6483. A photo collection (containing one or more result entries)
  6484. for individual photos or photos with more than one image
  6485. </para>
  6486. </listitem>
  6487. </itemizedlist>
  6488. <para>
  6489. <emphasis role="strong">Views</emphasis>
  6490. </para>
  6491. <para>
  6492. The views we’ll need include an application view, a search
  6493. results view and a photo view. Static views or pages of the
  6494. single-page application which do not require a dynamic element
  6495. to them (e.g an <quote>about</quote> page) can be easily coded
  6496. up in your document’s markup, independent of Backbone.
  6497. </para>
  6498. <para>
  6499. <emphasis role="strong">Routers</emphasis>
  6500. </para>
  6501. <para>
  6502. A number of possible routes need to be taken into consideration:
  6503. </para>
  6504. <itemizedlist>
  6505. <listitem>
  6506. <para>
  6507. Basic search queries <literal>#search/kiwis</literal>
  6508. </para>
  6509. </listitem>
  6510. <listitem>
  6511. <para>
  6512. Search queries with additional parameters (e.g sort,
  6513. pagination) <literal>#search/kiwis/srelevance/p7</literal>
  6514. </para>
  6515. </listitem>
  6516. <listitem>
  6517. <para>
  6518. Queries for specific photos <literal>#photo/93839</literal>
  6519. </para>
  6520. </listitem>
  6521. <listitem>
  6522. <para>
  6523. A default route (no parameters passed)
  6524. </para>
  6525. </listitem>
  6526. </itemizedlist>
  6527. <para>
  6528. This tutorial will be expanded shortly to fully cover the demo
  6529. application. In the mean time, please see the practicals folder
  6530. for the completed application that demonstrates the router
  6531. resolution discussed earlier between Backbone and jQuery Mobile.
  6532. </para>
  6533. </sect3>
  6534. <sect3 id="jquery-mobile-going-beyond-mobile-application-development">
  6535. <title>jQuery Mobile: Going beyond mobile application
  6536. development</title>
  6537. <para>
  6538. The majority of jQM apps I’ve seen in production have been
  6539. developed for the purpose of providing an optimal experience to
  6540. users on mobile devices. Given that the framework was developed
  6541. for this purpose, there’s nothing fundamentally wrong with this,
  6542. but many developers forget that jQM is a UI framework not
  6543. dissimilar to jQuery UI. It’s using the widget factory and is
  6544. capable of being used for a lot more than we give it credit for.
  6545. </para>
  6546. <para>
  6547. If you open up Flickly in a desktop browser, you’ll get an image
  6548. search UI that’s modeled on Google.com, however, review the
  6549. components (buttons, text inputs, tabs) on the page for a
  6550. moment. The desktop UI doesn’t look anything like a mobile
  6551. application yet I’m still using jQM for theming mobile
  6552. components; the tabs, date-picker, sliders - everything in the
  6553. desktop UI is re-using what jQM would be providing users on
  6554. mobile devices. Thanks to some media queries, the desktop UI can
  6555. make optimal use of whitespace, expanding component blocks out
  6556. and providing alternative layouts whilst still making use of jQM
  6557. as a component framework.
  6558. </para>
  6559. <para>
  6560. The benefit of this is that I don’t need to go pulling in jQuery
  6561. UI separately to be able to take advantage of these features.
  6562. Thanks to the recent ThemeRoller my components can look pretty
  6563. much exactly how I would like them to and users of the app can
  6564. get a jQM UI for lower-resolutions and a jQM-ish UI for
  6565. everything else.
  6566. </para>
  6567. <para>
  6568. The takeaway here is just to remember that if you’re not
  6569. (already) going through the hassle of conditional script/style
  6570. loading based on screen-resolution (using matchMedia.js etc),
  6571. there are simpler approaches that can be taken to cross-device
  6572. component theming.
  6573. </para>
  6574. </sect3>
  6575. </sect2>
  6576. <sect2 id="unit-testing">
  6577. <title># <a name="testing">Unit Testing</a></title>
  6578. <para>
  6579. </para>
  6580. </sect2>
  6581. </sect1>
  6582. <sect1 id="unit-testing-backbone-applications-with-jasmine">
  6583. <title><a name="unittestingjasmine">Unit Testing Backbone Applications
  6584. With Jasmine</a></title>
  6585. <sect2 id="introduction-2">
  6586. <title>Introduction</title>
  6587. <para>
  6588. One definition of unit testing is the process of taking the
  6589. smallest piece of testable code in an application, isolating it
  6590. from the remainder of your codebase and determining if it behaves
  6591. exactly as expected. In this section, we’ll be taking a look at
  6592. how to unit test Backbone applications using a popular JavaScript
  6593. testing framework called
  6594. <ulink url="http://pivotal.github.com/jasmine/">Jasmine</ulink>
  6595. from Pivotal Labs.
  6596. </para>
  6597. <para>
  6598. For an application to be considered <quote>well</quote>-tested,
  6599. distinct functionality should ideally have its own separate unit
  6600. tests where it’s tested against the different conditions you
  6601. expect it to work under. All tests must pass before functionality
  6602. is considered <quote>complete</quote>. This allows developers to
  6603. both modify a unit of code and it’s dependencies with a level of
  6604. confidence about whether these changes have caused any breakage.
  6605. </para>
  6606. <para>
  6607. As a basic example of unit testing is where a developer may wish
  6608. to assert whether passing specific values through to a sum
  6609. function results in the correct output being returned. For an
  6610. example more relevant to this book, we may wish to assert whether
  6611. a user adding a new Todo item to a list correctly adds a Model of
  6612. a specific type to a Todos Collection.
  6613. </para>
  6614. <para>
  6615. When building modern web-applications, it’s typically considered
  6616. best-practice to include automated unit testing as a part of your
  6617. development process. Whilst we’ll be focusing on Jasmine as a
  6618. solution for this, there are a number of other alternatives worth
  6619. considering, including QUnit.
  6620. </para>
  6621. </sect2>
  6622. <sect2 id="jasmine">
  6623. <title>Jasmine</title>
  6624. <para>
  6625. Jasmine describes itself as a behavior-driven development (BDD)
  6626. framework for testing JavaScript code. Before we jump into how the
  6627. framework works, it’s useful to understand exactly what
  6628. <ulink url="http://en.wikipedia.org/wiki/Behavior_Driven_Development">BDD</ulink>
  6629. is.
  6630. </para>
  6631. <para>
  6632. BDD is a second-generation testing approach first described by
  6633. <ulink url="http://dannorth.net/introducing-bdd/">Dan
  6634. North</ulink> (the authority on BDD) which attempts to test the
  6635. behavior of software. It’s considered second-generation as it came
  6636. out of merging ideas from Domain driven design (DDD) and lean
  6637. software development, helping teams to deliver high quality
  6638. software by answering many of the more confusing questions early
  6639. on in the agile process. Such questions commonly include those
  6640. concerning documentation and testing.
  6641. </para>
  6642. <para>
  6643. If you were to read a book on BDD, it’s likely to also be
  6644. described as being <quote>outside-in and pull-based</quote>. The
  6645. reason for this is that it borrows the idea of of pulling features
  6646. from Lean manufacturing which effectively ensures that the right
  6647. software solutions are being written by a) focusing on expected
  6648. outputs of the system and b) ensuring these outputs are achieved.
  6649. </para>
  6650. <para>
  6651. BDD recognizes that there are usually multiple stakeholders in a
  6652. project and not a single amorphous user of the system. These
  6653. different groups will be affected by the software being written in
  6654. differing ways and will have a varying opinion of what quality in
  6655. the system means to them. It’s for this reason that it’s important
  6656. to understand who the software will be bringing value you and
  6657. exactly what in it will be valuable to them.
  6658. </para>
  6659. <para>
  6660. Finally, BDD relies on automation. Once you’ve defined the quality
  6661. expected, your team will likely want to check on the functionality
  6662. of the solution being built regularly and compare it to the
  6663. results they expect. In order to facilitate this efficiently, the
  6664. process has to be automated. BDD relies heavily on the automation
  6665. of specification-testing and Jasmine is a tool which can assist
  6666. with this.
  6667. </para>
  6668. <para>
  6669. BDD helps both developers and non-technical stakeholders:
  6670. </para>
  6671. <itemizedlist>
  6672. <listitem>
  6673. <para>
  6674. Better understand and represent the models of the problems
  6675. being solved
  6676. </para>
  6677. </listitem>
  6678. <listitem>
  6679. <para>
  6680. Explain supported tests cases in a language that
  6681. non-developers can read
  6682. </para>
  6683. </listitem>
  6684. <listitem>
  6685. <para>
  6686. Focus on minimizing translation of the technical code being
  6687. written and the domain language spoken by the business
  6688. </para>
  6689. </listitem>
  6690. </itemizedlist>
  6691. <para>
  6692. What this means is that developers should be able to show Jasmine
  6693. unit tests to a project stakeholder and (at a high level, thanks
  6694. to a common vocabulary being used) they’ll ideally be able to
  6695. understand what the code supports.
  6696. </para>
  6697. <para>
  6698. Developers often implement BDD in unison with another testing
  6699. paradigm known as
  6700. <ulink url="http://en.wikipedia.org/wiki/Test-driven_development">TDD</ulink>
  6701. (test-driven development). The main idea behind TDD is:
  6702. </para>
  6703. <itemizedlist>
  6704. <listitem>
  6705. <para>
  6706. Write unit tests which describe the functionality you would
  6707. like your code to support
  6708. </para>
  6709. </listitem>
  6710. <listitem>
  6711. <para>
  6712. Watch these tests fail (as the code to support them hasn’t yet
  6713. been written)
  6714. </para>
  6715. </listitem>
  6716. <listitem>
  6717. <para>
  6718. Write code to make the tests pass
  6719. </para>
  6720. </listitem>
  6721. <listitem>
  6722. <para>
  6723. Rinse, repeat and refactor
  6724. </para>
  6725. </listitem>
  6726. </itemizedlist>
  6727. <para>
  6728. In this chapter we’re going to use both BDD (with TDD) to write
  6729. unit tests for a Backbone application.
  6730. </para>
  6731. <para>
  6732. <emphasis role="strong"><emphasis>Note:</emphasis></emphasis> I’ve
  6733. seen a lot of developers also opt for writing tests to validate
  6734. behavior of their code after having written it. While this is
  6735. fine, note that it can come with pitfalls such as only testing for
  6736. behavior your code currently supports, rather than behavior the
  6737. problem needs to be supported.
  6738. </para>
  6739. </sect2>
  6740. <sect2 id="suites-specs-spies">
  6741. <title>Suites, Specs &amp; Spies</title>
  6742. <para>
  6743. When using Jasmine, you’ll be writing suites and specifications
  6744. (specs). Suites basically describe scenarios whilst specs describe
  6745. what can be done in these scenarios.
  6746. </para>
  6747. <para>
  6748. Each spec is a JavaScript function, described with a call to
  6749. `<literal>it()</literal> using a description string and a
  6750. function. The description should describe the behaviour the
  6751. particular unit of code should exhibit and keeping in mind BDD, it
  6752. should ideally be meaningful. Heres an example of a basic spec:
  6753. </para>
  6754. <programlisting language="javascript">
  6755. it('should be incrementing in value', function(){
  6756. var counter = 0;
  6757. counter++;
  6758. });
  6759. </programlisting>
  6760. <para>
  6761. On its own, a spec isnt particularly useful until expectations
  6762. are set about the behavior of the code. Expectations in specs are
  6763. defined using the <literal>expect()</literal> function and an
  6764. <ulink url="https://github.com/pivotal/jasmine/wiki/Matchers">expectation
  6765. matcher</ulink> (e.g toEqual(), toBeTruthy(), toContain()). A
  6766. revised example using an expectation matcher would look like:
  6767. </para>
  6768. <programlisting language="javascript">
  6769. it('should be incrementing in value', function(){
  6770. var counter = 0;
  6771. counter++;
  6772. expect(counter).toEqual(1);
  6773. });
  6774. </programlisting>
  6775. <para>
  6776. The above code passes our behavioral expectation as
  6777. `<literal>counter</literal> equals 1. Notice how easy this was to
  6778. read the expectation on the last line (you probably grokked it
  6779. without any explanation).
  6780. </para>
  6781. <para>
  6782. Specs are grouped into suites which we describe using Jasmine’s
  6783. <literal>describe()</literal> function, again passing a string as
  6784. a description and a function. The name/description for your suite
  6785. is typically that of the component or module you’re testing.
  6786. </para>
  6787. <para>
  6788. Jasmine will use it as the group name when it reports the results
  6789. of the specs you’ve asked it to run. A simple suite containing our
  6790. sample spec could look like:
  6791. </para>
  6792. <programlisting language="javascript">
  6793. describe('Stats', function(){
  6794. it('can increment a number', function(){
  6795. ...
  6796. });
  6797. it('can subtract a number', function(){
  6798. ...
  6799. });
  6800. });
  6801. </programlisting>
  6802. <para>
  6803. Suites also share a functional scope and so it’s possible to
  6804. declare variables and functions inside a describe block which are
  6805. accessible within specs:
  6806. </para>
  6807. <programlisting language="javascript">
  6808. describe('Stats', function(){
  6809. var counter = 1;
  6810. it('can increment a number', function(){
  6811. // the counter was = 1
  6812. counter = counter + 1;
  6813. expect(counter).toEqual(2);
  6814. });
  6815. it('can subtract a number', function(){
  6816. // the counter was = 2
  6817. counter = counter - 1;
  6818. expect(counter).toEqual(1);
  6819. });
  6820. });
  6821. </programlisting>
  6822. <para>
  6823. <emphasis role="strong"><emphasis>Note:</emphasis></emphasis>
  6824. Suites are executed in the order in which they are described,
  6825. which can be useful to know if you would prefer to see test
  6826. results for specific parts of your application reported first.
  6827. </para>
  6828. <para>
  6829. Jasmine also supports <emphasis role="strong">spies</emphasis> - a
  6830. way to mock, spy and fake behavior in our unit tests. Spies
  6831. replace the function they’re spying on, allowing us to simulate
  6832. behavior we would like to mock (i.e test free of the actual
  6833. implementation).
  6834. </para>
  6835. <para>
  6836. In the below example, we’re spying on the
  6837. <literal>setComplete</literal> method of a dummy Todo function to
  6838. test that arguments can be passed to it as expected.
  6839. </para>
  6840. <programlisting language="javascript">
  6841. var Todo = function(){
  6842. };
  6843. Todo.prototype.setComplete = function (arg){
  6844. return arg;
  6845. }
  6846. describe('a simple spy', function(){
  6847. it('should spy on an instance method of a Todo', function(){
  6848. var myTodo = new Todo();
  6849. spyOn(myTodo, 'setComplete');
  6850. myTodo.setComplete('foo bar');
  6851. expect(myTodo.setComplete).toHaveBeenCalledWith('foo bar');
  6852. var myTodo2 = new Todo();
  6853. spyOn(myTodo2, 'setComplete');
  6854. expect(myTodo2.setComplete).not.toHaveBeenCalled();
  6855. });
  6856. });
  6857. </programlisting>
  6858. <para>
  6859. What you’re more likely to use spies for is testing
  6860. <ulink url="http://en.wikipedia.org/wiki/Asynchronous_communication">asynchronous</ulink>
  6861. behavior in your application such as AJAX requests. Jasmine
  6862. supports:
  6863. </para>
  6864. <itemizedlist>
  6865. <listitem>
  6866. <para>
  6867. Writing tests which can mock AJAX requests using spies. This
  6868. allows us to test code which runs before an AJAX request and
  6869. right after. It’s also possible to mock/fake responses the
  6870. server can return and the benefit of this type of testing is
  6871. that it’s faster as no real calls are being made to a server
  6872. </para>
  6873. </listitem>
  6874. <listitem>
  6875. <para>
  6876. Asynchronous tests which don’t rely on spies
  6877. </para>
  6878. </listitem>
  6879. </itemizedlist>
  6880. <para>
  6881. For the first kind of test, it’s possible to both fake an AJAX
  6882. request and verify that the request was both calling the correct
  6883. URL and executed a callback where one was provided.
  6884. </para>
  6885. <programlisting language="javascript">
  6886. it(&quot;the callback should be executed on success&quot;, function () {
  6887. spyOn($, &quot;ajax&quot;).andCallFake(function(options) {
  6888. options.success();
  6889. });
  6890. var callback = jasmine.createSpy();
  6891. getTodo(15, callback);
  6892. expect($.ajax.mostRecentCall.args[0][&quot;url&quot;]).toEqual(&quot;/todos/15&quot;);
  6893. expect(callback).toHaveBeenCalled();
  6894. });
  6895. function getTodo(id, callback) {
  6896. $.ajax({
  6897. type: &quot;GET&quot;,
  6898. url: &quot;/todos/&quot; + id,
  6899. dataType: &quot;json&quot;,
  6900. success: callback
  6901. });
  6902. }
  6903. </programlisting>
  6904. <para>
  6905. If you feel lost having seen matchers like
  6906. <literal>andCallFake()</literal> and
  6907. <literal>toHaveBeenCalled()</literal>, don’t worry. All of these
  6908. are Spy-specific matchers and are documented on the Jasmine
  6909. <ulink url="https://github.com/pivotal/jasmine/wiki/Spies">wiki</ulink>.
  6910. </para>
  6911. <para>
  6912. For the second type of test (asynchronous tests), we can take the
  6913. above further by taking advantage of three other methods Jasmine
  6914. supports:
  6915. </para>
  6916. <itemizedlist>
  6917. <listitem>
  6918. <para>
  6919. runs(function) - a block which runs as if it was directly
  6920. called
  6921. </para>
  6922. </listitem>
  6923. <listitem>
  6924. <para>
  6925. waits(timeout) - a native timeout before the next block is run
  6926. </para>
  6927. </listitem>
  6928. <listitem>
  6929. <para>
  6930. waitsFor(function, optional message, optional timeout) - a way
  6931. to pause specs until some other work has completed. Jasmine
  6932. waits until the supplied function returns true here before it
  6933. moves on to the next block.
  6934. </para>
  6935. </listitem>
  6936. </itemizedlist>
  6937. <programlisting language="javascript">
  6938. it(&quot;should make an actual AJAX request to a server&quot;, function () {
  6939. var callback = jasmine.createSpy();
  6940. getTodo(16, callback);
  6941. waitsFor(function() {
  6942. return callback.callCount &gt; 0;
  6943. });
  6944. runs(function() {
  6945. expect(callback).toHaveBeenCalled();
  6946. });
  6947. });
  6948. function getTodo(id, callback) {
  6949. $.ajax({
  6950. type: &quot;GET&quot;,
  6951. url: &quot;todos.json&quot;,
  6952. dataType: &quot;json&quot;,
  6953. success: callback
  6954. });
  6955. }
  6956. </programlisting>
  6957. <para>
  6958. <emphasis role="strong"><emphasis>Note:</emphasis></emphasis> It’s
  6959. useful to remember that when making real requests to a web server
  6960. in your unit tests, this has the potential to massively slow down
  6961. the speed at which tests run (due to many factors including server
  6962. latency). As this also introduces an external dependency that can
  6963. (and should) be minimized in your unit testing, it is strongly
  6964. recommended that you opt for spies to remove the need for a web
  6965. server to be used here.
  6966. </para>
  6967. </sect2>
  6968. <sect2 id="beforeeach-and-aftereach">
  6969. <title>beforeEach and afterEach()</title>
  6970. <para>
  6971. Jasmine also supports specifying code that can be run before each
  6972. (<literal>beforeEach()</literal>) and after each
  6973. (<literal>afterEach</literal>) test. This is useful for enforcing
  6974. consistent conditions (such as resetting variables that may be
  6975. required by specs). In the following example,
  6976. <literal>beforeEach()</literal> is used to create a new sample
  6977. Todo model specs can use for testing attributes.
  6978. </para>
  6979. <programlisting language="javascript">
  6980. beforeEach(function(){
  6981. this.todo = new Backbone.Model({
  6982. text: &quot;Buy some more groceries&quot;,
  6983. done: false
  6984. });
  6985. });
  6986. it(&quot;should contain a text value if not the default value&quot;, function(){
  6987. expect(this.todo.get('text')).toEqual(&quot;Buy some more groceries&quot;);
  6988. });
  6989. </programlisting>
  6990. <para>
  6991. Each nested <literal>describe()</literal> in your tests can have
  6992. their own <literal>beforeEach()</literal> and
  6993. <literal>afterEach()</literal> methods which support including
  6994. setup and teardown methods relevant to a particular suite. We’ll
  6995. be using <literal>beforeEach()</literal> in practice a little
  6996. later.
  6997. </para>
  6998. </sect2>
  6999. <sect2 id="shared-scope">
  7000. <title>Shared scope</title>
  7001. <para>
  7002. In the previous section you may have noticed that we initially
  7003. declared a variable <literal>this.todo</literal> in our
  7004. <literal>beforeEach()</literal> call and were then able to
  7005. continue using this in <literal>afterEach()</literal>. This is
  7006. thanks to a powerful feature of Jasmine known as shared functional
  7007. scope. Shared scope allows <literal>this</literal> properties to
  7008. be common to all blocks (including <literal>runs()</literal>), but
  7009. not declared variables (i.e <literal>var</literal>s).
  7010. </para>
  7011. </sect2>
  7012. <sect2 id="getting-setup">
  7013. <title>Getting setup</title>
  7014. <para>
  7015. Now that we’ve reviewed some fundamentals, let’s go through
  7016. downloading Jasmine and getting everything setup to write tests.
  7017. </para>
  7018. <para>
  7019. A standalone release of Jasmine can be
  7020. <ulink url="http://pivotal.github.com/jasmine/download.html">downloaded</ulink>
  7021. from the official release page.
  7022. </para>
  7023. <para>
  7024. You’ll need a file called SpecRunner.html in addition to the
  7025. release. It can be downloaded from
  7026. https://github.com/pivotal/jasmine/tree/master/lib/jasmine-core/example
  7027. or as part of a download of the complete Jasmine
  7028. <ulink url="https://github.com/pivotal/jasmine/zipball/master">repo</ulink>.Alternatively,
  7029. you can <literal>git clone</literal> the main Jasmine repository
  7030. from https://github.com/pivotal/jasmine.git.
  7031. </para>
  7032. <para>
  7033. Let’s review
  7034. <ulink url="https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/SpecRunner.html">SpecRunner.html</ulink>:
  7035. </para>
  7036. <para>
  7037. It first includes both Jasmine and the necessary CSS required for
  7038. reporting:
  7039. </para>
  7040. <programlisting>
  7041. &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;lib/jasmine-1.1.0.rc1/jasmine.css&quot;/&gt;
  7042. &lt;script type=&quot;text/javascript&quot; src=&quot;lib/jasmine-1.1.0.rc1/jasmine.js&quot;&gt;&lt;/script&gt;
  7043. &lt;script type=&quot;text/javascript&quot; src=&quot;lib/jasmine-1.1.0.rc1/jasmine-html.js&quot;&gt;&lt;/script&gt;
  7044. </programlisting>
  7045. <para>
  7046. Next, some sample tests are included:
  7047. </para>
  7048. <programlisting>
  7049. &lt;script type=&quot;text/javascript&quot; src=&quot;spec/SpecHelper.js&quot;&gt;&lt;/script&gt;
  7050. &lt;script type=&quot;text/javascript&quot; src=&quot;spec/PlayerSpec.js&quot;&gt;&lt;/script&gt;
  7051. </programlisting>
  7052. <para>
  7053. And finally the sources being tested:
  7054. </para>
  7055. <programlisting>
  7056. &lt;script type=&quot;text/javascript&quot; src=&quot;src/Player.js&quot;&gt;&lt;/script&gt;
  7057. &lt;script type=&quot;text/javascript&quot; src=&quot;src/Song.js&quot;&gt;&lt;/script&gt;
  7058. </programlisting>
  7059. <para>
  7060. <emphasis role="strong"><emphasis>Note:</emphasis></emphasis>
  7061. Below this section of SpecRunner is code responsible for running
  7062. the actual tests. Given that we won’t be covering modifying this
  7063. code, I’m going to skip reviewing it. I do however encourage you
  7064. to take a look through
  7065. <ulink url="https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/spec/PlayerSpec.js">PlayerSpec.js</ulink>
  7066. and
  7067. <ulink url="https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/spec/SpecHelper.js">SpecHelper.js</ulink>.
  7068. They’re a useful basic example to go through how a minimal set of
  7069. tests might work.
  7070. </para>
  7071. </sect2>
  7072. <sect2 id="tdd-with-backbone">
  7073. <title>TDD With Backbone</title>
  7074. <para>
  7075. When developing applications with Backbone, it can be necessary to
  7076. test both individual modules of code as well as modules, views,
  7077. collections and routers. Taking a TDD approach to testing, let’s
  7078. review some specs for testing these Backbone components using the
  7079. popular Backbone
  7080. <ulink url="https://github.com/addyosmani/todomvc/tree/master/todo-example/backbone">Todo</ulink>
  7081. application. For this section we will be using a modified version
  7082. of Larry Myers Backbone Koans project, which can be found in the
  7083. <literal>practicals\jasmine-koans</literal> folder.
  7084. </para>
  7085. </sect2>
  7086. <sect2 id="models-2">
  7087. <title><a name="testing-jasmine-models">Models</a></title>
  7088. <para>
  7089. The complexity of Backbone models can vary greatly depending on
  7090. what your application is trying to achieve. In the following
  7091. example, we’re going to test default values, attributes, state
  7092. changes and validation rules.
  7093. </para>
  7094. <para>
  7095. First, we begin our suite for model testing using
  7096. <literal>describe()</literal>:
  7097. </para>
  7098. <programlisting language="javascript">
  7099. describe('Tests for Todo', function() {
  7100. </programlisting>
  7101. <para>
  7102. Models should ideally have default values for attributes. This
  7103. helps ensure that when creating instances without a value set for
  7104. any specific attribute, a default one (e.g <quote></quote>) is
  7105. used instead. The idea here is to allow your application to
  7106. interact with models without any unexpected behavior.
  7107. </para>
  7108. <para>
  7109. In the following spec, we create a new Todo without any attributes
  7110. passed then check to find out what the value of the
  7111. <literal>text</literal> attribute is. As no value has been set, we
  7112. expect a default value of `<literal>&quot;&quot;</literal> to be
  7113. returned.
  7114. </para>
  7115. <programlisting language="javascript">
  7116. it('Can be created with default values for its attributes.', function() {
  7117. var todo = new Todo();
  7118. expect(todo.get('text')).toBe(&quot;&quot;);
  7119. });
  7120. </programlisting>
  7121. <para>
  7122. If testing this spec before your models have been written, youll
  7123. incur a failing test, as expected. Whats required for the spec to
  7124. pass is a default value for the attribute <literal>text</literal>.
  7125. We can implement this default value with some other useful
  7126. defaults (which well be using shortly) in our Todo model as
  7127. follows:
  7128. </para>
  7129. <programlisting language="javascript">
  7130. window.Todo = Backbone.Model.extend({
  7131. defaults: function() {
  7132. return {
  7133. text: &quot;&quot;,
  7134. done: false,
  7135. order: 0
  7136. };
  7137. }
  7138. </programlisting>
  7139. <para>
  7140. Next, we want to test that our model will pass attributes that are
  7141. set such that retrieving the value of these attributes after
  7142. initialization will be what we expect. Notice that here, in
  7143. addition to testing for an expected value for
  7144. <literal>text</literal>, were also testing the other default
  7145. values are what we expect them to be.
  7146. </para>
  7147. <programlisting language="javascript">
  7148. it('Will set passed attributes on the model instance when created.', function() {
  7149. var todo = new Todo({ text: 'Get oil change for car.' });
  7150. // what are the values expected here for each of the
  7151. // attributes in our Todo?
  7152. expect(todo.get('text')).toBe(&quot;Get oil change for car.&quot;);
  7153. expect(todo.get('done')).toBe(false);
  7154. expect(todo.get('order')).toBe(0);
  7155. });
  7156. </programlisting>
  7157. <para>
  7158. Backbone models support a model.change() event which is triggered
  7159. when the state of a model changes. In the following example, by
  7160. <quote>state</quote> Im referring to the value of a Todo models
  7161. attributes. The reason changes of state are important to test are
  7162. that there may be state-dependent events in your application e.g
  7163. you may wish to display a confirmation view once a Todo model has
  7164. been updated.
  7165. </para>
  7166. <programlisting language="javascript">
  7167. it('Fires a custom event when the state changes.', function() {
  7168. var spy = jasmine.createSpy('-change event callback-');
  7169. var todo = new Todo();
  7170. // how do we monitor changes of state?
  7171. todo.bind('change', spy);
  7172. // what would you need to do to force a change of state?
  7173. todo.set({ text: 'Get oil change for car.' });
  7174. expect(spy).toHaveBeenCalled();
  7175. });
  7176. </programlisting>
  7177. <para>
  7178. Its common to include validation logic in your models to ensure
  7179. both the input passed from users (and other modules) in the
  7180. application are <quote>valid</quote>. A Todo app may wish to
  7181. validate the text input supplied in case it contains rude words.
  7182. Similarly if were storing the <literal>done</literal> state of a
  7183. Todo item using booleans, we need to validate that truthy/falsy
  7184. values are passed and not just any arbitrary string.
  7185. </para>
  7186. <para>
  7187. In the following spec, we take advantage of the fact that
  7188. validations which fail model.validate() trigger an
  7189. <quote>error</quote> event. This allows us to test if validations
  7190. are correctly failing when invalid input is supplied.
  7191. </para>
  7192. <para>
  7193. We create an errorCallback spy using Jasmines built in
  7194. <literal>createSpy()</literal> method which allows us to spy on
  7195. the error event as follows:
  7196. </para>
  7197. <programlisting language="javascript">
  7198. it('Can contain custom validation rules, and will trigger an error event on failed validation.', function() {
  7199. var errorCallback = jasmine.createSpy('-error event callback-');
  7200. var todo = new Todo();
  7201. todo.bind('error', errorCallback);
  7202. // What would you need to set on the todo properties to
  7203. // cause validation to fail?
  7204. todo.set({done:'a non-integer value'});
  7205. var errorArgs = errorCallback.mostRecentCall.args;
  7206. expect(errorArgs).toBeDefined();
  7207. expect(errorArgs[0]).toBe(todo);
  7208. expect(errorArgs[1]).toBe('Todo.done must be a boolean value.');
  7209. });
  7210. </programlisting>
  7211. <para>
  7212. The code to make the above failing test support validation is
  7213. relatively simple. In our model, we override the validate() method
  7214. (as recommended in the Backbone docs), checking to make sure a
  7215. model both has a <quote>done</quote> property and is a valid
  7216. boolean before allowing it to pass.
  7217. </para>
  7218. <programlisting language="javascript">
  7219. validate: function(attrs) {
  7220. if (attrs.hasOwnProperty('done') &amp;&amp; !_.isBoolean(attrs.done)) {
  7221. return 'Todo.done must be a boolean value.';
  7222. }
  7223. }
  7224. </programlisting>
  7225. <para>
  7226. If you would like to review the final code for our Todo model, you
  7227. can find it below:
  7228. </para>
  7229. <programlisting language="javascript">
  7230. var NAUGHTY_WORDS = /crap|poop|hell|frogs/gi;
  7231. function sanitize(str) {
  7232. return str.replace(NAUGHTY_WORDS, 'rainbows');
  7233. }
  7234. window.Todo = Backbone.Model.extend({
  7235. defaults: function() {
  7236. return {
  7237. text: '',
  7238. done: false,
  7239. order: 0
  7240. };
  7241. },
  7242. initialize: function() {
  7243. this.set({text: sanitize(this.get('text'))}, {silent: true});
  7244. },
  7245. validate: function(attrs) {
  7246. if (attrs.hasOwnProperty('done') &amp;&amp; !_.isBoolean(attrs.done)) {
  7247. return 'Todo.done must be a boolean value.';
  7248. }
  7249. },
  7250. toggle: function() {
  7251. this.save({done: !this.get(&quot;done&quot;)});
  7252. }
  7253. });
  7254. </programlisting>
  7255. </sect2>
  7256. <sect2 id="collections-2">
  7257. <title><a name="testing-jasmine-collections">Collections</a></title>
  7258. <para>
  7259. We now need to define specs to tests a Backbone collection of Todo
  7260. models (a TodoList). Collections are responsible for a number of
  7261. list tasks including managing order and filtering.
  7262. </para>
  7263. <para>
  7264. A few specific specs that come to mind when working with
  7265. collections are:
  7266. </para>
  7267. <itemizedlist>
  7268. <listitem>
  7269. <para>
  7270. Making sure we can add new Todo models as both objects and
  7271. arrays
  7272. </para>
  7273. </listitem>
  7274. <listitem>
  7275. <para>
  7276. Attribute testing to make sure attributes such as the base URL
  7277. of the collection are values we expect
  7278. </para>
  7279. </listitem>
  7280. <listitem>
  7281. <para>
  7282. Purposefully adding items with a status of
  7283. <literal>done:true</literal> and checking against how many
  7284. items the collection thinks have been completed vs. those that
  7285. are remaining
  7286. </para>
  7287. </listitem>
  7288. </itemizedlist>
  7289. <para>
  7290. In this section were going to cover the first two of these with
  7291. the third left as an extended exercise I recommend trying out.
  7292. </para>
  7293. <para>
  7294. Testing Todo models can be added to a collection as objects or
  7295. arrays is relatively trivial. First, we initialize a new TodoList
  7296. collection and check to make sure its length (i.e the number of
  7297. Todo models it contains) is 0. Next, we add new Todos, both as
  7298. objects and arrays, checking the length property of the collection
  7299. at each stage to ensure the overall count is what we expect:
  7300. </para>
  7301. <programlisting language="javascript">
  7302. describe('Tests for TodoList', function() {
  7303. it('Can add Model instances as objects and arrays.', function() {
  7304. var todos = new TodoList();
  7305. expect(todos.length).toBe(0);
  7306. todos.add({ text: 'Clean the kitchen' });
  7307. // how many todos have been added so far?
  7308. expect(todos.length).toBe(1);
  7309. todos.add([
  7310. { text: 'Do the laundry', done: true },
  7311. { text: 'Go to the gym'}
  7312. ]);
  7313. // how many are there in total now?
  7314. expect(todos.length).toBe(3);
  7315. });
  7316. ...
  7317. </programlisting>
  7318. <para>
  7319. Similar to model attributes, its also quite straight-forward to
  7320. test attributes in collections. Here we have a spec that ensures
  7321. the collection.url (i.e the url reference to the collections
  7322. location on the server) is what we expect it to be:
  7323. </para>
  7324. <programlisting language="javascript">
  7325. it('Can have a url property to define the basic url structure for all contained models.', function() {
  7326. var todos = new TodoList();
  7327. // what has been specified as the url base in our model?
  7328. expect(todos.url).toBe('/todos/');
  7329. });
  7330. </programlisting>
  7331. <para>
  7332. For the third spec, its useful to remember that the
  7333. implementation for our collection will have methods for filtering
  7334. how many Todo items are done and how many are remaining - we can
  7335. call these <literal>done()</literal> and
  7336. <literal>remaining()</literal>. Consider writing a spec which
  7337. creates a new collection and adds one new model that has a preset
  7338. <literal>done</literal> state of <literal>true</literal> and two
  7339. others that have the default <literal>done</literal> state of
  7340. <literal>false</literal>. Testing the length of whats returned
  7341. using <literal>done()</literal> and <literal>remaining()</literal>
  7342. should allow us to know whether the state management in our
  7343. application is working or needs a little tweaking.
  7344. </para>
  7345. <para>
  7346. The final implementation for our TodoList collection can be found
  7347. below:
  7348. </para>
  7349. <programlisting language="javascript">
  7350. window.TodoList = Backbone.Collection.extend({
  7351. model: Todo,
  7352. url: '/todos/',
  7353. done: function() {
  7354. return this.filter(function(todo) { return todo.get('done'); });
  7355. },
  7356. remaining: function() {
  7357. return this.without.apply(this, this.done());
  7358. },
  7359. nextOrder: function() {
  7360. if (!this.length) {
  7361. return 1;
  7362. }
  7363. return this.last().get('order') + 1;
  7364. },
  7365. comparator: function(todo) {
  7366. return todo.get('order');
  7367. }
  7368. });
  7369. </programlisting>
  7370. </sect2>
  7371. <sect2 id="views-3">
  7372. <title><a name="testing-jasmine-views">Views</a></title>
  7373. <para>
  7374. Before we take a look at testing Backbone views, lets briefly
  7375. review a jQuery plugin that can assist with writing Jasmine specs
  7376. for them.
  7377. </para>
  7378. <para>
  7379. <emphasis role="strong">The Jasmine jQuery Plugin</emphasis>
  7380. </para>
  7381. <para>
  7382. As we know our Todo application will be using jQuery for DOM
  7383. manipulation, theres a useful jQuery plugin called
  7384. <ulink url="https://github.com/velesin/jasmine-jquery">jasmine-jquery</ulink>
  7385. we can use to help simplify BDD testing rendered elements that our
  7386. views may produce.
  7387. </para>
  7388. <para>
  7389. The plugin provides a number of additional Jasmine
  7390. <ulink url="https://github.com/pivotal/jasmine/wiki/Matchers">matchers</ulink>
  7391. to help test jQuery wrapped sets such as:
  7392. </para>
  7393. <itemizedlist>
  7394. <listitem>
  7395. <para>
  7396. <literal>toBe(jQuerySelector)</literal> e.g
  7397. <literal>expect($('&lt;div id=&quot;some-id&quot;&gt;&lt;/div&gt;')).toBe('div#some-id')</literal>
  7398. </para>
  7399. </listitem>
  7400. <listitem>
  7401. <para>
  7402. <literal>toBeChecked()</literal> e.g
  7403. <literal>expect($('&lt;input type=&quot;checkbox&quot; checked=&quot;checked&quot;/&gt;')).toBeChecked()</literal>
  7404. </para>
  7405. </listitem>
  7406. <listitem>
  7407. <para>
  7408. <literal>toBeSelected()</literal> e.g
  7409. <literal>expect($('&lt;option selected=&quot;selected&quot;&gt;&lt;/option&gt;')).toBeSelected()</literal>
  7410. </para>
  7411. </listitem>
  7412. </itemizedlist>
  7413. <para>
  7414. and <ulink url="https://github.com/velesin/jasmine-jquery">many
  7415. others</ulink>. The complete list of matchers supported can be
  7416. found on the project homepage. Its useful to know that similar to
  7417. the standard Jasmine matchers, the custom matchers above can be
  7418. inverted using the .not prefix (i.e
  7419. <literal>expect(x).not.toBe(y)</literal>):
  7420. </para>
  7421. <programlisting language="javascript">
  7422. expect($('&lt;div&gt;I am an example&lt;/div&gt;')).not.toHaveText(/other/)
  7423. </programlisting>
  7424. <para>
  7425. jasmine-jquery also includes a fixtures model, allowing us to load
  7426. in arbitrary HTML content we may wish to use in our tests.
  7427. Fixtures can be used as follows:
  7428. </para>
  7429. <para>
  7430. Include some HTML in an external fixtures file:
  7431. </para>
  7432. <para>
  7433. some.fixture.html:
  7434. <literal>&lt;div id=&quot;sample-fixture&quot;&gt;some HTML content&lt;/div&gt;</literal>
  7435. </para>
  7436. <para>
  7437. Next, inside our actual test we would load it as follows:
  7438. </para>
  7439. <programlisting language="javascript">
  7440. loadFixtures('some.fixture.html')
  7441. $('some-fixture').myTestedPlugin();
  7442. expect($('#some-fixture')).to&lt;the rest of your matcher would go here&gt;
  7443. </programlisting>
  7444. <para>
  7445. The jasmine-jquery plugin is by default setup to load fixtures
  7446. from a specific directory: spec/javascripts/fixtures. If you wish
  7447. to configure this path you can do so by initially setting
  7448. <literal>jasmine.getFixtures().fixturesPath = 'your custom path'</literal>.
  7449. </para>
  7450. <para>
  7451. Finally, jasmine-jquery includes support for spying on jQuery
  7452. events without the need for any extra plumbing work. This can be
  7453. done using the <literal>spyOnEvent()</literal> and
  7454. <literal>assert(eventName).toHaveBeenTriggered(selector)</literal>
  7455. functions. An example of usage may look as follows:
  7456. </para>
  7457. <programlisting language="javascript">
  7458. spyOnEvent($('#el'), 'click');
  7459. $('#el').click();
  7460. expect('click').toHaveBeenTriggeredOn($('#el'));
  7461. </programlisting>
  7462. <para>
  7463. <emphasis role="strong">View testing</emphasis>
  7464. </para>
  7465. <para>
  7466. In this section we will review three dimensions to writing specs
  7467. for Backbone Views: initial setup, view rendering and finally
  7468. templating. The latter two of these are the most commonly tested,
  7469. however well review shortly why writing specs for the
  7470. initialization of your views can also be of benefit.
  7471. </para>
  7472. </sect2>
  7473. <sect2 id="initial-setup">
  7474. <title>Initial setup</title>
  7475. <para>
  7476. At their most basic, specs for Backbone views should validate that
  7477. they are being correctly tied to specific DOM elements and are
  7478. backed by valid data models. The reason to consider doing this is
  7479. that failures to such specs can trip up more complex tests later
  7480. on and theyre fairly simple to write, given the overall value
  7481. offered.
  7482. </para>
  7483. <para>
  7484. To help ensure a consistent testing setup for our specs, we use
  7485. <literal>beforeEach()</literal> to append both an empty
  7486. <literal>UL</literal> (#todoList) to the DOM and initialize a new
  7487. instance of a TodoView using an empty Todo model.
  7488. <literal>afterEach()</literal> is used to remove the previous
  7489. #todoList <literal>UL</literal> as well as the previous instance
  7490. of the view.
  7491. </para>
  7492. <programlisting language="javascript">
  7493. describe('Tests for TodoView', function() {
  7494. beforeEach(function() {
  7495. $('body').append('&lt;ul id=&quot;todoList&quot;&gt;&lt;/ul&gt;');
  7496. this.todoView = new TodoView({ model: new Todo() });
  7497. });
  7498. afterEach(function() {
  7499. this.todoView.remove();
  7500. $('#todoList').remove();
  7501. });
  7502. ...
  7503. </programlisting>
  7504. <para>
  7505. The first spec useful to write is a check that the TodoView weve
  7506. created is using the correct <literal>tagName</literal> (element
  7507. or className). The purpose of this test is to make sure its been
  7508. correctly tied to a DOM element when it was created.
  7509. </para>
  7510. <para>
  7511. Backbone views typically create empty DOM elements once
  7512. initialized, however these elements are not attached to the
  7513. visible DOM in order to allow them to be constructed without an
  7514. impact on the performance of rendering.
  7515. </para>
  7516. <programlisting language="javascript">
  7517. it('Should be tied to a DOM element when created, based off the property provided.', function() {
  7518. //what html element tag name represents this view?
  7519. expect(todoView.el.tagName.toLowerCase()).toBe('li');
  7520. });
  7521. </programlisting>
  7522. <para>
  7523. Once again, if the TodoView has not already been written, we will
  7524. experience failing specs. Thankfully, solving this is as simple as
  7525. creating a new Backbone.View with a specific
  7526. <literal>tagName</literal>.
  7527. </para>
  7528. <programlisting language="javascript">
  7529. var todoView = Backbone.View.extend({
  7530. tagName: &quot;li&quot;
  7531. });
  7532. </programlisting>
  7533. <para>
  7534. If instead of testing against the <literal>tagName</literal> you
  7535. would prefer to use a className instead, we can take advantage of
  7536. jasmine-jquerys <literal>toHaveClass()</literal> matcher to cater
  7537. for this.
  7538. </para>
  7539. <programlisting>
  7540. it('Should have a class of &quot;todos&quot;'), function(){
  7541. expect($(this.view.el)).toHaveClass('todos');
  7542. });
  7543. </programlisting>
  7544. <para>
  7545. The <literal>toHaveClass()</literal> matcher operates on jQuery
  7546. objects and if the plugin hadnt been used, an exception would
  7547. have been incurred (it is of course also possible to test for the
  7548. className by accessing el.className if not opting to use
  7549. jasmine-jquery).
  7550. </para>
  7551. <para>
  7552. You may have noticed that in <literal>beforeEach()</literal>, we
  7553. passed our view an initial (albeit unfilled) Todo model. Views
  7554. should be backed by a model instance which provides data. As this
  7555. is quite important to our views ability to function, we can write
  7556. a spec to ensure a model is both defined (using the
  7557. <literal>toBeDefined()</literal> matcher) and then test attributes
  7558. of the model to ensure defaults both exist and are the value we
  7559. expect them to be.
  7560. </para>
  7561. <programlisting language="javascript">
  7562. it('Is backed by a model instance, which provides the data.', function() {
  7563. expect(todoView.model).toBeDefined();
  7564. // what's the value for Todo.get('done') here?
  7565. expect(todoView.model.get('done')).toBe(false); //or toBeFalsy()
  7566. });
  7567. </programlisting>
  7568. </sect2>
  7569. <sect2 id="view-rendering">
  7570. <title>View rendering</title>
  7571. <para>
  7572. Next were going to take a look at writing specs for view
  7573. rendering. Specifically, we want to test that our TodoView
  7574. elements are actually rendering as expected.
  7575. </para>
  7576. <para>
  7577. In smaller applications, those new to BDD might argue that visual
  7578. confirmation of view rendering could replace unit testing of
  7579. views. The reality is that when dealing with applications that
  7580. might grow to multiple-views, it often makes sense to automate
  7581. this process as much as possible from the get-go. There are also
  7582. aspects of rendering that require verification beyond what is
  7583. visually presented on-screen (which well see very shortly).
  7584. </para>
  7585. <para>
  7586. Were going to begin testing views by writing two specs. The first
  7587. spec will check that the views <literal>render()</literal> method
  7588. is correctly returning the view instance, which is necessary for
  7589. chaining. Our second spec will check that the HTML produced is
  7590. exactly what we expect based on the properties of the model
  7591. instance thats been associated with our TodoView.
  7592. </para>
  7593. <para>
  7594. Unlike some of the previous specs weve covered, this section will
  7595. make greater use of <literal>beforeEach()</literal> to both
  7596. demonstrate how to use nested suites and also ensure a consistent
  7597. set of conditions for our specs. In our first view spec for
  7598. TodoView, were simply going to create a sample model (based on
  7599. Todo) and instantiate a TodoView which associates it with the
  7600. model.
  7601. </para>
  7602. <programlisting language="javascript">
  7603. describe(&quot;TodoView&quot;, function() {
  7604. beforeEach(function() {
  7605. this.model = new Backbone.Model({
  7606. text: &quot;My Todo&quot;,
  7607. order: 1,
  7608. done: false
  7609. });
  7610. this.view = new TodoView({model:this.model});
  7611. });
  7612. describe(&quot;Rendering&quot;, function() {
  7613. it(&quot;returns the view object&quot;, function() {
  7614. expect(this.view.render()).toEqual(this.view);
  7615. });
  7616. it(&quot;produces the correct HTML&quot;, function() {
  7617. this.view.render();
  7618. //let's use jasmine-jquery's toContain() to avoid
  7619. //testing for the complete content of a todo's markup
  7620. expect(this.view.el.innerHTML)
  7621. .toContain('&lt;label class=&quot;todo-content&quot;&gt;My Todo&lt;/label&gt;');
  7622. });
  7623. });
  7624. });
  7625. </programlisting>
  7626. <para>
  7627. Once these specs are run, only the second one (<quote>produces the
  7628. correct HTML</quote>) fails. Our first spec (<quote>returns the
  7629. view object</quote>), which is testing that the TodoView instance
  7630. is returned from <literal>render()</literal>, only passed as this
  7631. is Backbones default behavior. We havent yet overwritten the
  7632. <literal>render()</literal> method with our own version.
  7633. </para>
  7634. <para>
  7635. <emphasis role="strong">Note:</emphasis> For the purposes of
  7636. maintaining readability, all template examples in this section
  7637. will use a minimal version of the following Todo view template. As
  7638. its relatively trivial to expand this, please feel free to refer
  7639. to this sample if needed:
  7640. </para>
  7641. <programlisting>
  7642. &lt;div class=&quot;todo &lt;%= done ? 'done' : '' %&gt;&quot;&gt;
  7643. &lt;div class=&quot;display&quot;&gt;
  7644. &lt;input class=&quot;check&quot; type=&quot;checkbox&quot; &lt;%= done ? 'checked=&quot;checked&quot;' : '' %&gt; /&gt;
  7645. &lt;label class=&quot;todo-content&quot;&gt;&lt;%= text %&gt;&lt;/label&gt;
  7646. &lt;span class=&quot;todo-destroy&quot;&gt;&lt;/span&gt;
  7647. &lt;/div&gt;
  7648. &lt;div class=&quot;edit&quot;&gt;
  7649. &lt;input class=&quot;todo-input&quot; type=&quot;text&quot; value=&quot;&lt;%= content %&gt;&quot; /&gt;
  7650. &lt;/div&gt;
  7651. &lt;/div&gt;
  7652. </programlisting>
  7653. <para>
  7654. The second spec fails with the following message:
  7655. </para>
  7656. <para>
  7657. Expected to contain
  7658. <literal>'&lt;label class=&quot;todo-content&quot;&gt;My Todo&lt;/label&gt;'</literal>.
  7659. </para>
  7660. <para>
  7661. The reason for this is the default behavior for render() doesnt
  7662. create any markup. Lets write a replacement for render() which
  7663. fixes this:
  7664. </para>
  7665. <programlisting language="javascript">
  7666. render: function() {
  7667. var template = '&lt;label class=&quot;todo-content&quot;&gt;&lt;%= text %&gt;&lt;/label&gt;';
  7668. var output = template
  7669. .replace(&quot;&lt;%= text %&gt;&quot;, this.model.get('text'));
  7670. $(this.el).html(output);
  7671. return this;
  7672. }
  7673. </programlisting>
  7674. <para>
  7675. The above specifies an inline string template and replaces fields
  7676. found in the template within the <quote>&lt;% %&gt;</quote> blocks
  7677. with their corresponding values from the associated model. As
  7678. were now also returning the TodoView instance from the method,
  7679. the first spec will also pass. Its worth noting that there are
  7680. serious drawbacks to using HTML strings in your specs to test
  7681. against like this. Even minor changes to your template (a simple
  7682. tab or whitespace) would cause your spec to fail, despite the
  7683. rendered output being the same. Its also more time consuming to
  7684. maintain as most templates in real-world applications are
  7685. significantly more complex. A better option for testing rendered
  7686. output is using jQuery to both select and inspect values.
  7687. </para>
  7688. <para>
  7689. With this in mind, lets re-write the specs, this time using some
  7690. of the custom matchers offered by jasmine-jquery:
  7691. </para>
  7692. <programlisting language="javascript">
  7693. describe(&quot;Template&quot;, function() {
  7694. beforeEach(function() {
  7695. this.view.render();
  7696. });
  7697. it(&quot;has the correct text content&quot;, function() {
  7698. expect($(this.view.el).find('todo-content'))
  7699. .toHaveText('My Todo');
  7700. });
  7701. });
  7702. </programlisting>
  7703. <para>
  7704. It would be impossible to discuss unit testing without mentioning
  7705. fixtures. Fixtures typically contain test data (e.g HTML) that is
  7706. loaded in when needed (either locally or from an external file)
  7707. for unit testing. So far weve been establishing jQuery
  7708. expectations based on the views el property. This works for a
  7709. number of cases, however, there are instances where it may be
  7710. necessary to render markup into the document. The most optimal way
  7711. to handle this within specs is through using fixtures (another
  7712. feature brought to us by the jasmine-jquery plugin).
  7713. </para>
  7714. <para>
  7715. Re-writing the last spec to use fixtures would look as follows:
  7716. </para>
  7717. <programlisting language="javascript">
  7718. describe(&quot;TodoView&quot;, function() {
  7719. beforeEach(function() {
  7720. ...
  7721. setFixtures('&lt;ul class=&quot;todos&quot;&gt;&lt;/ul&gt;');
  7722. });
  7723. ...
  7724. describe(&quot;Template&quot;, function() {
  7725. beforeEach(function() {
  7726. $('.todos').append(this.view.render().el);
  7727. });
  7728. it(&quot;has the correct text content&quot;, function() {
  7729. expect($('.todos').find('.todo-content'))
  7730. .toHaveText('My Todo');
  7731. });
  7732. });
  7733. });
  7734. </programlisting>
  7735. <para>
  7736. What were now doing in the above spec is appending the rendered
  7737. todo item into the fixture. We then set expectations against the
  7738. fixture, which may be something desirable when a view is setup
  7739. against an element which already exists in the DOM. It would be
  7740. necessary to provide both the fixture and test the
  7741. <literal>el</literal> property correctly picking up the element
  7742. expected when the view is instantiated.
  7743. </para>
  7744. </sect2>
  7745. <sect2 id="rendering-with-a-templating-system">
  7746. <title>Rendering with a templating system</title>
  7747. <para>
  7748. JavaScript templating systems (such as Handlebars, Mustache and
  7749. even Underscores own Micro-templating) support conditional logic
  7750. in template strings. What this effectively means is that we can
  7751. add if/else/ternery expressions inline which can then be evaluated
  7752. as needed, allowing us to build even more powerful templates.
  7753. </para>
  7754. <para>
  7755. In our case, when a user sets a Todo item to be complete (done),
  7756. we may wish to provide them with visual feedback (such as a
  7757. striked line through the text) to differentiate the item from
  7758. those that are remaining. This can be done by attaching a new
  7759. class to the item. Lets begin by writing a test we would ideally
  7760. like to work:
  7761. </para>
  7762. <programlisting language="javascript">
  7763. describe(&quot;When a todo is done&quot;, function() {
  7764. beforeEach(function() {
  7765. this.model.set({done: true}, {silent: true});
  7766. $('.todos').append(this.view.render().el);
  7767. });
  7768. it(&quot;has a done class&quot;, function() {
  7769. expect($('.todos .todo-content:first-child'))
  7770. .toHaveClass(&quot;done&quot;);
  7771. });
  7772. });
  7773. </programlisting>
  7774. <para>
  7775. This will fail with the following message:
  7776. </para>
  7777. <para>
  7778. Expected <quote><label class="todo-content">My
  7779. Todo</label></quote> to have class <quote>done</quote>.
  7780. </para>
  7781. <para>
  7782. which can be fixed in the existing render() method as follows:
  7783. </para>
  7784. <programlisting language="javascript">
  7785. render: function() {
  7786. var template = '&lt;label class=&quot;todo-content&quot;&gt;' +
  7787. '&lt;%= text %&gt;&lt;/label&gt;';
  7788. var output = template
  7789. .replace(&quot;&lt;%= text %&gt;&quot;, this.model.get('text'));
  7790. $(this.el).html(output);
  7791. if (this.model.get('done')) {
  7792. this.$(&quot;.todo-content&quot;).addClass(&quot;done&quot;);
  7793. }
  7794. return this;
  7795. }
  7796. </programlisting>
  7797. <para>
  7798. This can however get unwieldily fairly quickly. As the logic in
  7799. our templates increases, so does the complexity involved. This is
  7800. where templates libraries can help. As mentioned earlier, there
  7801. are a number of popular options available, but for the purposes of
  7802. this chapter were going to stick to using Underscores built-in
  7803. Microtemplating. Whilst there are more advanced options youre
  7804. free to explore, the benefit of this is that no additional files
  7805. are required and we can easily change the existing Jasmine specs
  7806. without too much adjustment.
  7807. </para>
  7808. <para>
  7809. The TodoView object modified to use Underscore templating would
  7810. look as follows:
  7811. </para>
  7812. <programlisting language="javascript">
  7813. var TodoView = Backbone.View.extend({
  7814. tagName: &quot;li&quot;,
  7815. initialize: function(options) {
  7816. this.template = _.template(options.template || &quot;&quot;);
  7817. },
  7818. render: function() {
  7819. $(this.el).html(this.template(this.model.toJSON()));
  7820. return this;
  7821. },
  7822. ...
  7823. });
  7824. </programlisting>
  7825. <para>
  7826. Above, the initialize() method compiles a supplied Underscore
  7827. template (using the _.template() function) in the instantiation. A
  7828. more common way of referencing templates is placing them in a
  7829. script tag using a custom script type (e.g
  7830. type=<quote>text/template</quote>). As this isnt a script type
  7831. any browser understands, its simply ignored, however referencing
  7832. the script by an id attribute allows the template to be kept
  7833. separate to other parts of the page which wish to use it. In real
  7834. world applications, its preferable to either do this or load in
  7835. templates stored in external files for testing.
  7836. </para>
  7837. <para>
  7838. For testing purposes, were going to continue using the string
  7839. injection approach to keep things simple. There is however a
  7840. useful trick that can be applied to automatically create or extend
  7841. templates in the Jasmine scope for each test. By creating a new
  7842. directory (say, <quote>templates</quote>) in the
  7843. <quote>spec</quote> folder and adding a new script file with the
  7844. following contents, to jasmine.yml or SpecRunner.html, we can add
  7845. a todo property which contains the Underscore template we wish to
  7846. use:
  7847. </para>
  7848. <programlisting language="javascript">
  7849. beforeEach(function() {
  7850. this.templates = _.extend(this.templates || {}, {
  7851. todo: '&lt;label class=&quot;todo-content&quot;&gt;' +
  7852. '&lt;%= text %&gt;' +
  7853. '&lt;/label&gt;'
  7854. });
  7855. });
  7856. </programlisting>
  7857. <para>
  7858. To finish this off, we simply update our existing spec to
  7859. reference the template when instantiating the TodoView object:
  7860. </para>
  7861. <programlisting language="javascript">
  7862. describe(&quot;TodoView&quot;, function() {
  7863. beforeEach(function() {
  7864. ...
  7865. this.view = new TodoView({
  7866. model: this.model,
  7867. template: this.templates.todo
  7868. });
  7869. });
  7870. ...
  7871. });
  7872. </programlisting>
  7873. <para>
  7874. The existing specs weve looked at would continue to pass using
  7875. this approach, leaving us free to adjust the template with some
  7876. additional conditional logic for Todos with a status of
  7877. <quote>done</quote>:
  7878. </para>
  7879. <programlisting language="javascript">
  7880. beforeEach(function() {
  7881. this.templates = _.extend(this.templates || {}, {
  7882. todo: '&lt;label class=&quot;todo-content &lt;%= done ? 'done' : '' %&gt;&quot;' +
  7883. '&lt;%= text %&gt;' +
  7884. '&lt;/label&gt;'
  7885. });
  7886. });
  7887. </programlisting>
  7888. <para>
  7889. This will now also pass without any issues. Remember that
  7890. jasmine-jquery also supports loading external fixtures into your
  7891. specs easily using its build in <literal>loadFixtures()</literal>
  7892. and <literal>readFixtures()</literal> methods. For more
  7893. information, consider reading the official jasmine-jquery
  7894. <ulink url="https://github.com/velesin/jasmine-jquery">docs</ulink>.
  7895. </para>
  7896. </sect2>
  7897. <sect2 id="conclusions-1">
  7898. <title>Conclusions</title>
  7899. <para>
  7900. We have now covered how to write Jasmine tests for models, views
  7901. and collections with Backbone.js. Whilst testing routing can at
  7902. times be desirable, some developers feel it can be more optimal to
  7903. leave this to third-party tools such as Selenium, so do keep this
  7904. in mind.
  7905. </para>
  7906. <para>
  7907. James Newbery was kind enough to help me with writing the Views
  7908. section above and his articles on
  7909. <ulink url="http://tinnedfruit.com/2011/04/26/testing-backbone-apps-with-jasmine-sinon-3.html">Testing
  7910. Backbone Apps With SinonJS</ulink> were of great inspiration
  7911. (youll actually find some Handlebars examples of the view specs
  7912. in part 3 of his article). If you would like to learn more about
  7913. writing spies and mocks for Backbone using
  7914. <ulink url="http://sinonjs.org">SinonJS</ulink> as well as how to
  7915. test Backbone routers, do consider reading his series.
  7916. </para>
  7917. </sect2>
  7918. <sect2 id="exercise">
  7919. <title>Exercise</title>
  7920. <para>
  7921. As an exercise, I recommend now trying the Jasmine Koans in
  7922. <literal>practicals\jasmine-joans</literal> and trying to fix some
  7923. of the purposefully failing tests it has to offer. This is an
  7924. excellent way of not just learning how Jasmine specs and suites
  7925. work, but working through the examples (without peaking back) will
  7926. also put your Backbone skills to test too.
  7927. </para>
  7928. </sect2>
  7929. <sect2 id="further-reading">
  7930. <title>Further reading</title>
  7931. <itemizedlist>
  7932. <listitem>
  7933. <para>
  7934. <ulink url="http://japhr.blogspot.com/2011/11/jasmine-backbonejs-revisited.html">Jasmine
  7935. + Backbone Revisited</ulink>
  7936. </para>
  7937. </listitem>
  7938. <listitem>
  7939. <para>
  7940. <ulink url="http://japhr.blogspot.com/2011/12/phantomjs-and-backbonejs-and-requirejs.html">Backbone,
  7941. PhantomJS and Jasmine</ulink>
  7942. </para>
  7943. </listitem>
  7944. </itemizedlist>
  7945. </sect2>
  7946. </sect1>
  7947. <sect1 id="unit-testing-backbone-applications-with-qunit-and-sinonjs">
  7948. <title><a name="unittestingqunit">Unit Testing Backbone Applications
  7949. With QUnit And SinonJS</a></title>
  7950. <sect2 id="introduction-3">
  7951. <title>Introduction</title>
  7952. <para>
  7953. QUnit is a powerful JavaScript test suite written by jQuery team
  7954. member <ulink url="http://bassistance.de/">Jörn Zaefferer</ulink>
  7955. and used by many large open-source projects (such as jQuery and
  7956. Backbone.js) to test their code. Its both capable of testing
  7957. standard JavaScript code in the browser as well as code on the
  7958. server-side (where environments supported include Rhino, V8 and
  7959. SpiderMonkey). This makes it a robust solution for a large number
  7960. of use-cases.
  7961. </para>
  7962. <para>
  7963. Quite a few Backbone.js contributors feel that QUnit is a better
  7964. introductory framework for testing if you dont wish to start off
  7965. with Jasmine and BDD right away. As well see later on in this
  7966. chapter, QUnit can also be combined with third-party solutions
  7967. such as SinonJS to produce an even more powerful testing solution
  7968. supporting spies and mocks, which some say is preferable over
  7969. Jasmine.
  7970. </para>
  7971. <para>
  7972. My personal recommendation is that its worth comparing both
  7973. frameworks and opting for the solution that you feel the most
  7974. comfortable with.
  7975. </para>
  7976. </sect2>
  7977. </sect1>
  7978. <sect1 id="qunit">
  7979. <title>QUnit</title>
  7980. <sect2 id="getting-setup-1">
  7981. <title>Getting Setup</title>
  7982. <para>
  7983. Luckily, getting QUnit setup is a fairly straight-forward process
  7984. that will take less than 5 minutes.
  7985. </para>
  7986. <para>
  7987. We first setup a testing environment composed of three files:
  7988. </para>
  7989. <itemizedlist>
  7990. <listitem>
  7991. <para>
  7992. A HTML <emphasis role="strong">structure</emphasis> for
  7993. displaying test results,
  7994. </para>
  7995. </listitem>
  7996. <listitem>
  7997. <para>
  7998. The <emphasis role="strong">qunit.js</emphasis> file composing
  7999. the testing framework and,
  8000. </para>
  8001. </listitem>
  8002. <listitem>
  8003. <para>
  8004. The <emphasis role="strong">qunit.css</emphasis> file for
  8005. styling test results.
  8006. </para>
  8007. </listitem>
  8008. </itemizedlist>
  8009. <para>
  8010. The latter two of these can be downloaded from the
  8011. <ulink url="http://qunitjs.com">QUnit website</ulink>.
  8012. </para>
  8013. <para>
  8014. If you would prefer, you can use a hosted version of the QUnit
  8015. source files for testing purposes. The hosted URLs can be found at
  8016. [http://github.com/jquery/qunit/raw/master/qunit/].
  8017. </para>
  8018. <sect3 id="sample-html-with-qunit-compatible-markup">
  8019. <title>Sample HTML with QUnit-compatible markup:</title>
  8020. <programlisting language="html">
  8021. &lt;!DOCTYPE html&gt;
  8022. &lt;html&gt;
  8023. &lt;head&gt;
  8024. &lt;title&gt;QUnit Test Suite&lt;/title&gt;
  8025. &lt;link rel=&quot;stylesheet&quot; href=&quot;qunit.css&quot;&gt;
  8026. &lt;script src=&quot;qunit.js&quot;&gt;&lt;/script&gt;
  8027. &lt;!-- Your application --&gt;
  8028. &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  8029. &lt;!-- Your tests --&gt;
  8030. &lt;script src=&quot;tests.js&quot;&gt;&lt;/script&gt;
  8031. &lt;/head&gt;
  8032. &lt;body&gt;
  8033. &lt;h1 id=&quot;qunit-header&quot;&gt;QUnit Test Suite&lt;/h1&gt;
  8034. &lt;h2 id=&quot;qunit-banner&quot;&gt;&lt;/h2&gt;
  8035. &lt;div id=&quot;qunit-testrunner-toolbar&quot;&gt;&lt;/div&gt;
  8036. &lt;h2 id=&quot;qunit-userAgent&quot;&gt;&lt;/h2&gt;
  8037. &lt;ol id=&quot;qunit-tests&quot;&gt;test markup, hidden.&lt;/ol&gt;
  8038. &lt;/body&gt;
  8039. &lt;/html&gt;
  8040. </programlisting>
  8041. <para>
  8042. Lets go through the elements above with qunit mentioned in
  8043. their ID. When QUnit is running:
  8044. </para>
  8045. <itemizedlist>
  8046. <listitem>
  8047. <para>
  8048. <emphasis role="strong">qunit-header</emphasis> shows the
  8049. name of the test suite
  8050. </para>
  8051. </listitem>
  8052. <listitem>
  8053. <para>
  8054. <emphasis role="strong">qunit-banner</emphasis> shows up as
  8055. red if a test fails and green if all tests pass
  8056. </para>
  8057. </listitem>
  8058. <listitem>
  8059. <para>
  8060. <emphasis role="strong">qunit-testrunner-toolbar</emphasis>
  8061. contains additional options for configuring the display of
  8062. tests
  8063. </para>
  8064. </listitem>
  8065. <listitem>
  8066. <para>
  8067. <emphasis role="strong">qunit-userAgent</emphasis> displays
  8068. the navigator.userAgent property
  8069. </para>
  8070. </listitem>
  8071. <listitem>
  8072. <para>
  8073. <emphasis role="strong">qunit-tests</emphasis> is a
  8074. container for our test results
  8075. </para>
  8076. </listitem>
  8077. </itemizedlist>
  8078. <para>
  8079. When running correctly, the above test runner looks as follows:
  8080. </para>
  8081. <figure>
  8082. <title>screenshot 1</title>
  8083. <mediaobject>
  8084. <imageobject>
  8085. <imagedata fileref="img/7d4de12.png" />
  8086. </imageobject>
  8087. <textobject><phrase>screenshot 1</phrase></textobject>
  8088. </mediaobject>
  8089. </figure>
  8090. <para>
  8091. The numbers of the form (a, b, c) after each test name
  8092. correspond to a) failed asserts, b) passed asserts and c) total
  8093. asserts. Clicking on a test name expands it to display all of
  8094. the assertions for that test case. Assertions in green have
  8095. successfully passed.
  8096. </para>
  8097. <figure>
  8098. <title>screenshot 2</title>
  8099. <mediaobject>
  8100. <imageobject>
  8101. <imagedata fileref="img/9df4.png" />
  8102. </imageobject>
  8103. <textobject><phrase>screenshot 2</phrase></textobject>
  8104. </mediaobject>
  8105. </figure>
  8106. <para>
  8107. If however any tests fail, the test gets highlighted (and the
  8108. qunit-banner at the top switches to red):
  8109. </para>
  8110. <figure>
  8111. <title>screenshot 3</title>
  8112. <mediaobject>
  8113. <imageobject>
  8114. <imagedata fileref="img/3e5545.png" />
  8115. </imageobject>
  8116. <textobject><phrase>screenshot 3</phrase></textobject>
  8117. </mediaobject>
  8118. </figure>
  8119. </sect3>
  8120. </sect2>
  8121. <sect2 id="assertions">
  8122. <title>Assertions</title>
  8123. <para>
  8124. QUnit supports a number of basic
  8125. <emphasis role="strong">assertions</emphasis>, which are used in
  8126. testing to verify that the result being returned by our code is
  8127. what we expect. If an assertion fails, we know that a bug
  8128. exists.Similar to Jasmine, QUnit can be used to easily test for
  8129. regressions. Specifically, when a bug is found one can write an
  8130. assertion to test the existence of the bug, write a patch and then
  8131. commit both. If subsequent changes to the code break the test
  8132. youll know what was responsible and be able to address it more
  8133. easily.
  8134. </para>
  8135. <para>
  8136. Some of the supported QUnit assertions were going to look at
  8137. first are:
  8138. </para>
  8139. <itemizedlist>
  8140. <listitem>
  8141. <para>
  8142. <literal>ok ( state, message )</literal> - passes if the first
  8143. argument is truthy
  8144. </para>
  8145. </listitem>
  8146. <listitem>
  8147. <para>
  8148. <literal>equal ( actual, expected, message )</literal> - a
  8149. simple comparison assertion with type coercion
  8150. </para>
  8151. </listitem>
  8152. <listitem>
  8153. <para>
  8154. <literal>notEqual ( actual, expected, message )</literal> -
  8155. the opposite of the above
  8156. </para>
  8157. </listitem>
  8158. <listitem>
  8159. <para>
  8160. <literal>expect( amount )</literal> - the number of assertions
  8161. expected to run within each test
  8162. </para>
  8163. </listitem>
  8164. <listitem>
  8165. <para>
  8166. <literal>strictEqual( actual, expected, message)</literal> -
  8167. offers a much stricter comparison than
  8168. <literal>equal()</literal> and is considered the preferred
  8169. method of checking equality as it avoids stumbling on subtle
  8170. coercion bugs
  8171. </para>
  8172. </listitem>
  8173. <listitem>
  8174. <para>
  8175. <literal>deepEqual( actual, expected, message )</literal> -
  8176. similar to <literal>strictEqual</literal>, comparing the
  8177. contents (with <literal>===</literal>) of the given objects,
  8178. arrays and primitives.
  8179. </para>
  8180. </listitem>
  8181. </itemizedlist>
  8182. <para>
  8183. Creating new test cases with QUnit is relatively straight-forward
  8184. and can be done using <literal>test()</literal>, which constructs
  8185. a test where the first argument is the <literal>name</literal> of
  8186. the test to be displayed in our results and the second is a
  8187. <literal>callback</literal> function containing all of our
  8188. assertions. This is called as soon as QUnit is running.
  8189. </para>
  8190. <sect3 id="basic-test-case-using-test-name-callback">
  8191. <title>Basic test case using test( name, callback ):</title>
  8192. <programlisting language="javascript">
  8193. var myString = 'Hello Backbone.js';
  8194. test( 'Our first QUnit test - asserting results', function(){
  8195. // ok( boolean, message )
  8196. ok( true, 'the test succeeds');
  8197. ok( false, 'the test fails');
  8198. // equal( actualValue, expectedValue, message )
  8199. equal( myString, 'Hello Backbone.js', 'The value expected is Hello Backbone.js!');
  8200. });
  8201. </programlisting>
  8202. <para>
  8203. What were doing in the above is defining a variable with a
  8204. specific value and then testing to ensure the value was what we
  8205. expected it to be. This was done using the comparison assertion,
  8206. <literal>equal()</literal>, which expects its first argument to
  8207. be a value being tested and the second argument to be the
  8208. expected value. We also used <literal>ok()</literal>, which
  8209. allows us to easily test against functions or variables that
  8210. evaluate to booleans.
  8211. </para>
  8212. <para>
  8213. Note: Optionally in our test case, we could have passed an
  8214. <quote>expected</quote> value to <literal>test()</literal>
  8215. defining the number of assertions we expect to run. This takes
  8216. the form: <literal>test( name, [expected], test );</literal> or
  8217. by manually settings the expectation at the top of the test
  8218. function, like so: <literal>expect( 1 )</literal>. I recommend
  8219. you to make it a habit and always define how many assertions you
  8220. expect. More on this later.
  8221. </para>
  8222. <para>
  8223. As testing a simple static variable is fairly trivial, we can
  8224. take this further to test actual functions. In the following
  8225. example we test the output of a function that reverses a string
  8226. to ensure that the output is correct using
  8227. <literal>equal()</literal> and <literal>notEqual()</literal>:
  8228. </para>
  8229. </sect3>
  8230. <sect3 id="comparing-the-actual-output-of-a-function-against-the-expected-output">
  8231. <title>Comparing the actual output of a function against the
  8232. expected output:</title>
  8233. <programlisting language="javascript">
  8234. function reverseString( str ){
  8235. return str.split(&quot;&quot;).reverse().join(&quot;&quot;);
  8236. }
  8237. test( 'reverseString()', function() {
  8238. expect( 5 );
  8239. equal( reverseString('hello'), 'olleh', 'The value expected was olleh' );
  8240. equal( reverseString('foobar'), 'raboof', 'The value expected was raboof' );
  8241. equal( reverseString('world'), 'dlrow', 'The value expected was dlrow' );
  8242. notEqual( reverseString('world'), 'dlroo', 'The value was expected to not be dlroo' );
  8243. equal( reverseString('bubble'), 'double', 'The value expected was elbbub' );
  8244. })
  8245. </programlisting>
  8246. <para>
  8247. Running these tests in the QUnit test runner (which you would
  8248. see when your HTML test page was loaded) we would find that four
  8249. of the assertions pass whilst the last one does not. The reason
  8250. the test against <literal>'double'</literal> fails is because it
  8251. was purposefully written incorrectly. In your own projects if a
  8252. test fails to pass and your assertions are correct, youve
  8253. probably just found a bug!
  8254. </para>
  8255. </sect3>
  8256. </sect2>
  8257. <sect2 id="adding-structure-to-assertions">
  8258. <title>Adding structure to assertions</title>
  8259. <para>
  8260. Housing all of our assertions in one test case can quickly become
  8261. difficult to maintain, but luckily QUnit supports structuring
  8262. blocks of assertions more cleanly. This can be done using
  8263. <literal>module()</literal> - a method that allows us to easily
  8264. group tests together. A typical approach to grouping might be
  8265. keeping multiple tests testing a specific method as part of the
  8266. same group (module).
  8267. </para>
  8268. <sect3 id="basic-qunit-modules">
  8269. <title>Basic QUnit Modules:</title>
  8270. <programlisting language="javascript">
  8271. module( 'Module One' );
  8272. test( 'first test', function() {} );
  8273. test( 'another test', function() {} );
  8274. module( 'Module Two' );
  8275. test( 'second test', function() {} );
  8276. test( 'another test', function() {} );
  8277. module( 'Module Three' );
  8278. test( 'third test', function() {} );
  8279. test( 'another test', function() {} );
  8280. </programlisting>
  8281. <para>
  8282. We can take this further by introducing
  8283. <literal>setup()</literal> and <literal>teardown()</literal>
  8284. callbacks to our modules, where <literal>setup()</literal> is
  8285. run before each test whilst <literal>teardown()</literal> is run
  8286. after each test.
  8287. </para>
  8288. </sect3>
  8289. <sect3 id="using-setup-and-teardown">
  8290. <title>Using setup() and teardown() :</title>
  8291. <programlisting language="javascript">
  8292. module( &quot;Module One&quot;, {
  8293. setup: function() {
  8294. // run before
  8295. },
  8296. teardown: function() {
  8297. // run after
  8298. }
  8299. });
  8300. test(&quot;first test&quot;, function() {
  8301. // run the first test
  8302. });
  8303. </programlisting>
  8304. <para>
  8305. These callbacks can be used to define (or clear) any components
  8306. we wish to instantiate for use in one or more of our tests. As
  8307. well see shortly, this is ideal for defining new instances of
  8308. views, collections, models or routers from a project that we can
  8309. then reference across multiple tests.
  8310. </para>
  8311. </sect3>
  8312. <sect3 id="using-setup-and-teardown-for-instantiation-and-clean-up">
  8313. <title>Using setup() and teardown() for instantiation and
  8314. clean-up:</title>
  8315. <programlisting language="javascript">
  8316. // Define a simple model and collection modeling a store and
  8317. // list of stores
  8318. var Store = Backbone.Model.extend({});
  8319. var StoreList = Backbone.Collection.extend({
  8320. model: store,
  8321. comparator: function( store ) { return store.get('name') }
  8322. });
  8323. // Define a group for our tests
  8324. module( &quot;StoreList sanity check&quot;, {
  8325. setup: function() {
  8326. this.list = new StoreList;
  8327. this.list.add(new Store({ name: &quot;Costcutter&quot; }));
  8328. this.list.add(new Store({ name: &quot;Target&quot; }));
  8329. this.list.add(new Store({ name: &quot;Walmart&quot; }));
  8330. this.list.add(new Store({ name: &quot;Barnes &amp; Noble&quot; });
  8331. },
  8332. teardown: function() {
  8333. window.errors = null;
  8334. }
  8335. });
  8336. // Test the order of items added
  8337. test( &quot;test ordering&quot;, function() {
  8338. expect( 1 );
  8339. var expected = [&quot;Barnes &amp; Noble&quot;, &quot;Costcutter&quot;, &quot;Target&quot;, &quot;Walmart&quot;];
  8340. var actual = this.list.pluck(&quot;name&quot;);
  8341. deepEqual( actual, expected, &quot;is maintained by comparator&quot; );
  8342. });
  8343. </programlisting>
  8344. <para>
  8345. Here, a list of stores is created and stored on
  8346. <literal>setup()</literal>. A <literal>teardown()</literal>
  8347. callback is used to simply clear our a list of errors we might
  8348. be storing within the window scope, but is otherwise not needed.
  8349. </para>
  8350. </sect3>
  8351. </sect2>
  8352. <sect2 id="assertion-examples">
  8353. <title>Assertion examples</title>
  8354. <para>
  8355. Before we continue any further, lets review some more examples of
  8356. how QUnits various assertions can be correctly used when writing
  8357. tests:
  8358. </para>
  8359. <sect3 id="equal---a-comparison-assertion.-it-passes-if-actual-expected">
  8360. <title>equal - a comparison assertion. It passes if actual ==
  8361. expected</title>
  8362. <programlisting language="javascript">
  8363. test( &quot;equal&quot;, 2, function() {
  8364. var actual = 6 - 5;
  8365. equal( actual, true, &quot;passes as 1 == true&quot; );
  8366. equal( actual, 1, &quot;passes as 1 == 1&quot; );
  8367. });
  8368. </programlisting>
  8369. </sect3>
  8370. <sect3 id="notequal---a-comparison-assertion.-it-passes-if-actual-expected">
  8371. <title>notEqual - a comparison assertion. It passes if actual !=
  8372. expected</title>
  8373. <programlisting language="javascript">
  8374. test( &quot;notEqual&quot;, 2, function() {
  8375. var actual = 6 - 5;
  8376. notEqual( actual, false, &quot;passes as 1 != false&quot; );
  8377. notEqual( actual, 0, &quot;passes as 1 != 0&quot; );
  8378. });
  8379. </programlisting>
  8380. </sect3>
  8381. <sect3 id="strictequal---a-comparison-assertion.-it-passes-if-actual-expected.">
  8382. <title>strictEqual - a comparison assertion. It passes if actual
  8383. === expected.</title>
  8384. <programlisting language="javascript">
  8385. test( &quot;strictEqual&quot;, 2, function() {
  8386. var actual = 6 - 5;
  8387. strictEqual( actual, true, &quot;fails as 1 !== true&quot; );
  8388. strictEqual( actual, 1, &quot;passes as 1 === 1&quot; );
  8389. });
  8390. </programlisting>
  8391. </sect3>
  8392. <sect3 id="notstrictequal---a-comparison-assertion.-it-passes-if-actual-expected.">
  8393. <title>notStrictEqual - a comparison assertion. It passes if
  8394. actual !== expected.</title>
  8395. <programlisting language="javascript">
  8396. test(&quot;notStrictEqual&quot;, 2, function() {
  8397. var actual = 6 - 5;
  8398. notStrictEqual( actual, true, &quot;passes as 1 !== true&quot; );
  8399. notStrictEqual( actual, 1, &quot;fails as 1 === 1&quot; );
  8400. });
  8401. </programlisting>
  8402. </sect3>
  8403. <sect3 id="deepequal---a-recursive-comparison-assertion.-unlike-strictequal-it-works-on-objects-arrays-and-primitives.">
  8404. <title>deepEqual - a recursive comparison assertion. Unlike
  8405. strictEqual(), it works on objects, arrays and primitives.</title>
  8406. <programlisting language="javascript">
  8407. test(&quot;deepEqual&quot;, 4, function() {
  8408. var actual = {q: 'foo', t: 'bar'};
  8409. var el = $('div');
  8410. var children = $('div').children();
  8411. equal( actual, {q: 'foo', t: 'bar'}, &quot;fails - objects are not equal using equal()&quot; );
  8412. deepEqual( actual, {q: 'foo', t: 'bar'}, &quot;passes - objects are equal&quot; );
  8413. equal( el, children, &quot;fails - jQuery objects are not the same&quot; );
  8414. deepEqual(el, children, &quot;fails - objects not equivalent&quot; );
  8415. });
  8416. </programlisting>
  8417. </sect3>
  8418. <sect3 id="notdeepequal---a-comparison-assertion.-this-returns-the-opposite-of-deepequal">
  8419. <title>notDeepEqual - a comparison assertion. This returns the
  8420. opposite of deepEqual</title>
  8421. <programlisting language="javascript">
  8422. test(&quot;notDeepEqual&quot;, 2, function() {
  8423. var actual = {q: 'foo', t: 'bar'};
  8424. notEqual( actual, {q: 'foo', t: 'bar'}, &quot;passes - objects are not equal&quot; );
  8425. notDeepEqual( actual, {q: 'foo', t: 'bar'}, &quot;fails - objects are equivalent&quot; );
  8426. });
  8427. </programlisting>
  8428. </sect3>
  8429. <sect3 id="raises---an-assertion-which-tests-if-a-callback-throws-any-exceptions">
  8430. <title>raises - an assertion which tests if a callback throws any
  8431. exceptions</title>
  8432. <programlisting language="javascript">
  8433. test(&quot;raises&quot;, 1, function() {
  8434. raises(function() {
  8435. throw new Error( &quot;Oh no! It's an error!&quot; );
  8436. }, &quot;passes - an error was thrown inside our callback&quot;);
  8437. });
  8438. </programlisting>
  8439. </sect3>
  8440. </sect2>
  8441. <sect2 id="fixtures">
  8442. <title>Fixtures</title>
  8443. <para>
  8444. From time to time we may need to write tests that modify the DOM.
  8445. Managing the clean-up of such operations between tests can be a
  8446. genuine pain, but thankfully QUnit has a solution to this problem
  8447. in the form of the <literal>#qunit-fixture</literal> element, seen
  8448. below.
  8449. </para>
  8450. <sect3 id="fixture-markup">
  8451. <title>Fixture markup:</title>
  8452. <programlisting language="html">
  8453. &lt;!DOCTYPE html&gt;
  8454. &lt;html&gt;
  8455. &lt;head&gt;
  8456. &lt;title&gt;QUnit Test&lt;/title&gt;
  8457. &lt;link rel=&quot;stylesheet&quot; href=&quot;qunit.css&quot;&gt;
  8458. &lt;script src=&quot;qunit.js&quot;&gt;&lt;/script&gt;
  8459. &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  8460. &lt;script src=&quot;tests.js&quot;&gt;&lt;/script&gt;
  8461. &lt;/head&gt;
  8462. &lt;body&gt;
  8463. &lt;h1 id=&quot;qunit-header&quot;&gt;QUnit Test&lt;/h1&gt;
  8464. &lt;h2 id=&quot;qunit-banner&quot;&gt;&lt;/h2&gt;
  8465. &lt;div id=&quot;qunit-testrunner-toolbar&quot;&gt;&lt;/div&gt;
  8466. &lt;h2 id=&quot;qunit-userAgent&quot;&gt;&lt;/h2&gt;
  8467. &lt;ol id=&quot;qunit-tests&quot;&gt;&lt;/ol&gt;
  8468. &lt;div id=&quot;qunit-fixture&quot;&gt;&lt;/div&gt;
  8469. &lt;/body&gt;
  8470. &lt;/html&gt;
  8471. </programlisting>
  8472. <para>
  8473. We can either opt to place static markup in the fixture or just
  8474. insert/append any DOM elements we may need to it. QUnit will
  8475. automatically reset the <literal>innerHTML</literal> of the
  8476. fixture after each test to its original value. In case youre
  8477. using jQuery, its useful to know that QUnit checks for its
  8478. availability and will opt to use <literal>$(el).html()</literal>
  8479. instead, which will cleanup any jQuery event handlers too.
  8480. </para>
  8481. </sect3>
  8482. <sect3 id="fixtures-example">
  8483. <title>Fixtures example:</title>
  8484. <para>
  8485. Let us now go through a more complete example of using fixtures.
  8486. One thing that most of us are used to doing in jQuery is working
  8487. with lists - theyre often used to define the markup for menus,
  8488. grids and a number of other components. You may have used jQuery
  8489. plugins before that manipulated a given list in a particular way
  8490. and it can be useful to test that the final (manipulated) output
  8491. of the plugin is what was expected.
  8492. </para>
  8493. <para>
  8494. For the purposes of our next example, were going to use Ben
  8495. Almans <literal>$.enumerate()</literal> plugin, which can
  8496. prepend each item in a list by its index, optionally allowing us
  8497. to set what the first number in the list is. The code snippet
  8498. for the plugin can be found below, followed by an example of the
  8499. output is generates:
  8500. </para>
  8501. <programlisting language="javascript">
  8502. $.fn.enumerate = function( start ) {
  8503. if ( typeof start !== &quot;undefined&quot; ) {
  8504. // Since `start` value was provided, enumerate and return
  8505. // the initial jQuery object to allow chaining.
  8506. return this.each(function(i){
  8507. $(this).prepend( &quot;&lt;b&gt;&quot; + ( i + start ) + &quot;&lt;/b&gt; &quot; );
  8508. });
  8509. } else {
  8510. // Since no `start` value was provided, function as a
  8511. // getter, returing the appropriate value from the first
  8512. // selected element.
  8513. var val = this.eq( 0 ).children( &quot;b&quot; ).eq( 0 ).text();
  8514. return Number( val );
  8515. }
  8516. };
  8517. /*
  8518. &lt;ul&gt;
  8519. &lt;li&gt;1. hello&lt;/li&gt;
  8520. &lt;li&gt;2. world&lt;/li&gt;
  8521. &lt;li&gt;3. i&lt;/li&gt;
  8522. &lt;li&gt;4. am&lt;/li&gt;
  8523. &lt;li&gt;5. foo&lt;/li&gt;
  8524. &lt;/ul&gt;
  8525. */
  8526. </programlisting>
  8527. <para>
  8528. Lets now write some specs for the plugin. First, we define the
  8529. markup for a list containing some sample items inside our
  8530. <literal>qunit-fixture</literal> element:
  8531. </para>
  8532. <programlisting language="html">
  8533. &amp;lt;div id=&amp;quot;qunit-fixture&amp;quot;&amp;gt;
  8534. &amp;lt;ul&amp;gt;
  8535. &amp;lt;li&amp;gt;hello&amp;lt;/li&amp;gt;
  8536. &amp;lt;li&amp;gt;world&amp;lt;/li&amp;gt;
  8537. &amp;lt;li&amp;gt;i&amp;lt;/li&amp;gt;
  8538. &amp;lt;li&amp;gt;am&amp;lt;/li&amp;gt;
  8539. &amp;lt;li&amp;gt;foo&amp;lt;/li&amp;gt;
  8540. &amp;lt;/ul&amp;gt;
  8541. &amp;lt;/div&amp;gt;
  8542. </programlisting>
  8543. <para>
  8544. Next, we need to think about what should be tested.
  8545. <literal>$.enumerate()</literal> supports a few different use
  8546. cases, including:
  8547. </para>
  8548. <itemizedlist>
  8549. <listitem>
  8550. <para>
  8551. <emphasis role="strong">No arguments passed</emphasis> - i.e
  8552. <literal>$(el).enumerate()</literal>
  8553. </para>
  8554. </listitem>
  8555. <listitem>
  8556. <para>
  8557. <emphasis role="strong">0 passed as an argument</emphasis> -
  8558. i.e <literal>$(el).enumerate(0)</literal>
  8559. </para>
  8560. </listitem>
  8561. <listitem>
  8562. <para>
  8563. <emphasis role="strong">1 passed as an argument</emphasis> -
  8564. i.e <literal>$(el).enumerate(1)</literal>
  8565. </para>
  8566. </listitem>
  8567. </itemizedlist>
  8568. <para>
  8569. As the text value for each list item is of the form <quote>n.
  8570. item-text</quote> and we only require this to test against the
  8571. expected output, we can simply access the content using
  8572. <literal>$(el).eq(index).text()</literal> (for more information
  8573. on .eq() see
  8574. <ulink url="http://api.jquery.com/eq/">here</ulink>).
  8575. </para>
  8576. <para>
  8577. and finally, here are our test cases:
  8578. </para>
  8579. <programlisting language="javascript">
  8580. module(&quot;jQuery#enumerate&quot;);
  8581. test( &quot;No arguments passed&quot;, 5, function() {
  8582. var items = $(&quot;#qunit-fixture li&quot;).enumerate();
  8583. equal( items.eq(0).text(), &quot;1. hello&quot;, &quot;first item should have index 1&quot; );
  8584. equal( items.eq(1).text(), &quot;2. world&quot;, &quot;second item should have index 2&quot; );
  8585. equal( items.eq(2).text(), &quot;3. i&quot;, &quot;third item should have index 3&quot; );
  8586. equal( items.eq(3).text(), &quot;4. am&quot;, &quot;fourth item should have index 4&quot; );
  8587. equal( items.eq(4).text(), &quot;5. foo&quot;, &quot;fifth item should have index 5&quot; );
  8588. });
  8589. test( &quot;0 passed as an argument&quot;, 5, function() {
  8590. var items = $(&quot;#qunit-fixture li&quot;).enumerate( 0 );
  8591. equal( items.eq(0).text(), &quot;0. hello&quot;, &quot;first item should have index 0&quot; );
  8592. equal( items.eq(1).text(), &quot;1. world&quot;, &quot;second item should have index 1&quot; );
  8593. equal( items.eq(2).text(), &quot;2. i&quot;, &quot;third item should have index 2&quot; );
  8594. equal( items.eq(3).text(), &quot;3. am&quot;, &quot;fourth item should have index 3&quot; );
  8595. equal( items.eq(4).text(), &quot;4. foo&quot;, &quot;fifth item should have index 4&quot; );
  8596. });
  8597. test( &quot;1 passed as an argument&quot;, 3, function() {
  8598. var items = $(&quot;#qunit-fixture li&quot;).enumerate( 1 );
  8599. equal( items.eq(0).text(), &quot;1. hello&quot;, &quot;first item should have index 1&quot; );
  8600. equal( items.eq(1).text(), &quot;2. world&quot;, &quot;second item should have index 2&quot; );
  8601. equal( items.eq(2).text(), &quot;3. i&quot;, &quot;third item should have index 3&quot; );
  8602. equal( items.eq(3).text(), &quot;4. am&quot;, &quot;fourth item should have index 4&quot; );
  8603. equal( items.eq(4).text(), &quot;5. foo&quot;, &quot;fifth item should have index 5&quot; );
  8604. });
  8605. </programlisting>
  8606. </sect3>
  8607. </sect2>
  8608. <sect2 id="asynchronous-code">
  8609. <title>Asynchronous code</title>
  8610. <para>
  8611. As with Jasmine, the effort required to run synchronous tests with
  8612. QUnit is fairly straight-forward. That said, what about tests that
  8613. require asynchronous callbacks (such as expensive processes, Ajax
  8614. requests and so on)? When were dealing with asynchronous code,
  8615. rather than letting QUnit control when the next test runs, we can
  8616. inform that we need it to stop running and wait until its okay to
  8617. continue once again.
  8618. </para>
  8619. <para>
  8620. Remember: running asynchronous code without any special
  8621. considerations can cause incorrect assertions to appear in other
  8622. tests, so we want to make sure we get it right.
  8623. </para>
  8624. <para>
  8625. Writing QUnit tests for asynchronous code is made possible using
  8626. the <literal>start()</literal> and `<literal>stop()</literal>
  8627. methods, which programmatically set the start and stop points
  8628. during such tests. Here’s a simple example:
  8629. </para>
  8630. <programlisting language="javascript">
  8631. test(&quot;An async test&quot;, function(){
  8632. stop();
  8633. expect( 1 );
  8634. $.ajax({
  8635. url: &quot;/test&quot;,
  8636. dataType: 'json',
  8637. success: function( data ){
  8638. deepEqual(data, {
  8639. topic: &quot;hello&quot;,
  8640. message: &quot;hi there!&quot;
  8641. });
  8642. start();
  8643. }
  8644. });
  8645. });
  8646. </programlisting>
  8647. <para>
  8648. A jQuery <literal>$.ajax()</literal> request is used to connect to
  8649. a test resource and assert that the data returned is correct.
  8650. <literal>deepEqual()</literal> is used here as it allows us to
  8651. compare different data types (e.g objects, arrays) and ensures
  8652. that what is returned is exactly what we’re expecting. We know
  8653. that our Ajax request is asynchronous and so we first call
  8654. <literal>stop()</literal>, run the code making the request and
  8655. finally at the very end of our callback, inform QUnit that it is
  8656. okay to continue running other tests.
  8657. </para>
  8658. <para>
  8659. Note: rather than including <literal>stop()</literal>, we can
  8660. simply exclude it and substitute <literal>test()</literal> with
  8661. <literal>asyncTest()</literal> if we prefer. This improves
  8662. readability when dealing with a mixture of asynchronous and
  8663. synchronous tests in your suite. Whilst this setup should work
  8664. fine for many use-cases, there is no guarantee that the callback
  8665. in our <literal>$.ajax()</literal> request will actually get
  8666. called. To factor this into our tests, we can use
  8667. <literal>expect()</literal> once again to define how many
  8668. assertions we expect to see within our test. This is a healthy
  8669. safety blanket as it ensures that if a test completes with an
  8670. insufficient number of assertions, we know something went wrong
  8671. and fix it.
  8672. </para>
  8673. </sect2>
  8674. </sect1>
  8675. <sect1 id="sinonjs">
  8676. <title>SinonJS</title>
  8677. <para>
  8678. Similar to the section on testing Backbone.js apps using the Jasmine
  8679. BDD framework, we’re nearly ready to take what we’ve learned and
  8680. write a number of QUnit tests for our Todo application.
  8681. </para>
  8682. <para>
  8683. Before we start though, you may have noticed that QUnit doesn’t
  8684. support test spies. Test spies are functions which record arguments,
  8685. exceptions and return values for any of their calls. They’re
  8686. typically used to test callbacks and how functions may be used in
  8687. the application being tested. In testing frameworks, spies can
  8688. usually be either anonymous functions or wrap functions which
  8689. already exist.
  8690. </para>
  8691. <sect2 id="what-is-sinonjs">
  8692. <title>What is SinonJS?</title>
  8693. <para>
  8694. In order for us to substitute support for spies in QUnit, we will
  8695. be taking advantage of a mocking framework called
  8696. <ulink url="http://sinonjs.org/">SinonJS</ulink> by Christian
  8697. Johansen. We will also be using the
  8698. <ulink url="http://sinonjs.org/qunit/">SinonJS-QUnit
  8699. adapter</ulink> which provides seamless integration with QUnit
  8700. (meaning setup is minimal). Sinon.JS is completely test-framework
  8701. agnostic and should be easy to use with any testing framework, so
  8702. it’s ideal for our needs.
  8703. </para>
  8704. <para>
  8705. The framework supports three features we’ll be taking advantage of
  8706. for unit testing our application:
  8707. </para>
  8708. <itemizedlist>
  8709. <listitem>
  8710. <para>
  8711. <emphasis role="strong">Anonymous spies</emphasis>
  8712. </para>
  8713. </listitem>
  8714. <listitem>
  8715. <para>
  8716. <emphasis role="strong">Spying on existing methods</emphasis>
  8717. </para>
  8718. </listitem>
  8719. <listitem>
  8720. <para>
  8721. <emphasis role="strong">A rich inspection interface</emphasis>
  8722. </para>
  8723. </listitem>
  8724. </itemizedlist>
  8725. <para>
  8726. Using <literal>this.spy()</literal> without any arguments creates
  8727. an anonymous spy. This is comparable to
  8728. <literal>jasmine.createSpy()</literal> and we can observe basic
  8729. usage of a SinonJS spy in the following example:
  8730. </para>
  8731. <sect3 id="basic-spies">
  8732. <title>Basic Spies:</title>
  8733. <programlisting language="javascript">
  8734. test(&quot;should call all subscribers for a message exactly once&quot;, function () {
  8735. var message = getUniqueString();
  8736. var spy = this.spy();
  8737. PubSub.subscribe( message, spy );
  8738. PubSub.publishSync( message, &quot;Hello World&quot; );
  8739. ok( spy1.calledOnce, &quot;the subscriber was called once&quot; );
  8740. });
  8741. </programlisting>
  8742. <para>
  8743. We can also use <literal>this.spy()</literal> to spy on existing
  8744. functions (like jQuery’s <literal>$.ajax</literal>) in the
  8745. example below. When spying on a function which already exists,
  8746. the function behaves normally but we get access to data about
  8747. its calls which can be very useful for testing purposes.
  8748. </para>
  8749. </sect3>
  8750. <sect3 id="spying-on-existing-functions">
  8751. <title>Spying On Existing Functions:</title>
  8752. <programlisting language="javascript">
  8753. test( &quot;should inspect jQuery.getJSON's usage of jQuery.ajax&quot;, function () {
  8754. this.spy( jQuery, &quot;ajax&quot; );
  8755. jQuery.getJSON( &quot;/todos/completed&quot; );
  8756. ok( jQuery.ajax.calledOnce );
  8757. equals( jQuery.ajax.getCall(0).args[0].url, &quot;/todos/completed&quot; );
  8758. equals( jQuery.ajax.getCall(0).args[0].dataType, &quot;json&quot; );
  8759. });
  8760. </programlisting>
  8761. <para>
  8762. SinonJS comes with a rich spy interface which callows us to test
  8763. whether a spy was called with a specific argument, if it was
  8764. called a specific number of times and test against the values of
  8765. arguments. A complete list of features supported in the
  8766. interface can be found here (http://sinonjs.org/docs/), but
  8767. let’s take a look at some examples demonstrating some of the
  8768. most commonly used ones:
  8769. </para>
  8770. </sect3>
  8771. <sect3 id="matching-arguments-test-a-spy-was-called-with-a-specific-set-of-arguments">
  8772. <title>Matching arguments: test a spy was called with a specific
  8773. set of arguments:</title>
  8774. <programlisting language="javascript">
  8775. test( &quot;Should call a subscriber with standard matching&quot;: function () {
  8776. var spy = sinon.spy();
  8777. PubSub.subscribe( &quot;message&quot;, spy );
  8778. PubSub.publishSync( &quot;message&quot;, { id: 45 } );
  8779. assertTrue( spy.calledWith( { id: 45 } ) );
  8780. });
  8781. </programlisting>
  8782. </sect3>
  8783. <sect3 id="stricter-argument-matching-test-a-spy-was-called-at-least-once-with-specific-arguments-and-no-others">
  8784. <title>Stricter argument matching: test a spy was called at least
  8785. once with specific arguments and no others:</title>
  8786. <programlisting language="javascript">
  8787. test( &quot;Should call a subscriber with strict matching&quot;: function () {
  8788. var spy = sinon.spy();
  8789. PubSub.subscribe( &quot;message&quot;, spy );
  8790. PubSub.publishSync( &quot;message&quot;, &quot;many&quot;, &quot;arguments&quot; );
  8791. PubSub.publishSync( &quot;message&quot;, 12, 34 );
  8792. // This passes
  8793. assertTrue( spy.calledWith(&quot;many&quot;) );
  8794. // This however, fails
  8795. assertTrue( spy.calledWithExactly( &quot;many&quot; ) );
  8796. });
  8797. </programlisting>
  8798. </sect3>
  8799. <sect3 id="testing-call-order-testing-if-a-spy-was-called-before-or-after-another-spy">
  8800. <title>Testing call order: testing if a spy was called before or
  8801. after another spy:</title>
  8802. <programlisting language="javascript">
  8803. test( &quot;Should call a subscriber and maintain call order&quot;: function () {
  8804. var a = sinon.spy();
  8805. var b = sinon.spy();
  8806. PubSub.subscribe( &quot;message&quot;, a );
  8807. PubSub.subscribe( &quot;event&quot;, b );
  8808. PubSub.publishSync( &quot;message&quot;, { id: 45 } );
  8809. PubSub.publishSync( &quot;event&quot;, [1, 2, 3] );
  8810. assertTrue( a.calledBefore(b) );
  8811. assertTrue( b.calledAfter(a) );
  8812. });
  8813. </programlisting>
  8814. </sect3>
  8815. <sect3 id="match-execution-counts-test-a-spy-was-called-a-specific-number-of-times">
  8816. <title>Match execution counts: test a spy was called a specific
  8817. number of times:</title>
  8818. <programlisting language="javascript">
  8819. test( &quot;Should call a subscriber and check call counts&quot;, function () {
  8820. var message = getUniqueString();
  8821. var spy = this.spy();
  8822. PubSub.subscribe( message, spy );
  8823. PubSub.publishSync( message, &quot;some payload&quot; );
  8824. // Passes if spy was called once and only once.
  8825. ok( spy.calledOnce ); // calledTwice and calledThrice are also supported
  8826. // The number of recorded calls.
  8827. equal( spy.callCount, 1 );
  8828. // Directly checking the arguments of the call
  8829. equals( spy.getCall(0).args[0], message );
  8830. });
  8831. </programlisting>
  8832. </sect3>
  8833. </sect2>
  8834. <sect2 id="stubs-and-mocks">
  8835. <title>Stubs and mocks</title>
  8836. <para>
  8837. SinonJS also supports two other powerful features which are useful
  8838. to be aware of: stubs and mocks. Both stubs and mocks implement
  8839. all of the features of the spy API, but have some added
  8840. functionality.
  8841. </para>
  8842. <sect3 id="stubs">
  8843. <title>Stubs</title>
  8844. <para>
  8845. A stub allows us to replace any existing behaviour for a
  8846. specific method with something else. They can be very useful for
  8847. simulating exceptions and are most often used to write test
  8848. cases when certain dependencies of your code-base may not yet be
  8849. written.
  8850. </para>
  8851. <para>
  8852. Let us briefly re-explore our Backbone Todo application, which
  8853. contained a Todo model and a TodoList collection. For the
  8854. purpose of this walkthrough, we want to isolate our TodoList
  8855. collection and fake the Todo model to test how adding new models
  8856. might behave.
  8857. </para>
  8858. <para>
  8859. We can pretend that the models have yet to be written just to
  8860. demonstrate how stubbing might be carried out. A shell
  8861. collection just containing a reference to the model to be used
  8862. might look like this:
  8863. </para>
  8864. <programlisting language="javascript">
  8865. var TodoList = Backbone.Collection.extend({
  8866. model: Todo
  8867. });
  8868. // Let's assume our instance of this collection is
  8869. this.todoList;
  8870. </programlisting>
  8871. <para>
  8872. Assuming our collection is instantiating new models itself, it’s
  8873. necessary for us to stub the models constructor function for the
  8874. the test. This can be done by creating a simple stub as follows:
  8875. </para>
  8876. <programlisting language="javascript">
  8877. this.todoStub = sinon.stub( window, &quot;Todo&quot; );
  8878. </programlisting>
  8879. <para>
  8880. The above creates a stub of the Todo method on the window
  8881. object. When stubbing a persistent object, it’s necessary to
  8882. restore it to its original state. This can be done in a
  8883. <literal>teardown()</literal> as follows:
  8884. </para>
  8885. <programlisting language="javascript">
  8886. this.todoStub.restore();
  8887. </programlisting>
  8888. <para>
  8889. After this, we need to alter what the constructor returns, which
  8890. can be efficiently done using a plain
  8891. <literal>Backbone.Model</literal> constructor. Whilst this isn’t
  8892. a Todo model, it does still provide us an actual Backbone model.
  8893. </para>
  8894. <programlisting language="javascript">
  8895. teardown: function() {
  8896. this.todoStub = sinon.stub( window, &quot;Todo&quot; );
  8897. this.model = new Backbone.Model({
  8898. id: 2,
  8899. title: &quot;Hello world&quot;
  8900. });
  8901. this.todoStub.returns( this.model );
  8902. });
  8903. </programlisting>
  8904. <para>
  8905. The expectation here might be that this snippet would ensure our
  8906. TodoList collection always instantiates a stubbed Todo model,
  8907. but because a reference to the model in the collection is
  8908. already present, we need to reset the model property of our
  8909. collection as follows:
  8910. </para>
  8911. <programlisting language="javascript">
  8912. this.todoList.model = Todo;
  8913. </programlisting>
  8914. <para>
  8915. The result of this is that when our TodoList collection
  8916. instantiates new Todo models, it will return our plain Backbone
  8917. model instance as desired. This allows us to write a spec for
  8918. testing the addition of new model literals as follows:
  8919. </para>
  8920. <programlisting language="javascript">
  8921. module( &quot;Should function when instantiated with model literals&quot;, {
  8922. setup:function() {
  8923. this.todoStub = sinon.stub(window, &quot;Todo&quot;);
  8924. this.model = new Backbone.Model({
  8925. id: 2,
  8926. title: &quot;Hello world&quot;
  8927. });
  8928. this.todoStub.returns(this.model);
  8929. this.todos = new TodoList();
  8930. // Let's reset the relationship to use a stub
  8931. this.todos.model = Todo;
  8932. this.todos.add({
  8933. id: 2,
  8934. title: &quot;Hello world&quot;
  8935. });
  8936. },
  8937. teardown: function() {
  8938. this.todoStub.restore();
  8939. }
  8940. });
  8941. test(&quot;should add a model&quot;, function() {
  8942. equal( this.todos.length, 1 );
  8943. });
  8944. test(&quot;should find a model by id&quot;, function() {
  8945. equal( this.todos.get(5).get(&quot;id&quot;), 5 );
  8946. });
  8947. });
  8948. </programlisting>
  8949. </sect3>
  8950. <sect3 id="mocks">
  8951. <title>Mocks</title>
  8952. <para>
  8953. Mocks are effectively the same as stubs, however they mock a
  8954. complete API out and have some built-in expectations for how
  8955. they should be used. The difference between a mock and a spy is
  8956. that as the expectations for their use are pre-defined, it will
  8957. fail if any of these are not met.
  8958. </para>
  8959. <para>
  8960. Here’s a snippet with sample usage of a mock based on PubSubJS.
  8961. Here, we have a <literal>clearTodo()</literal> method as a
  8962. callback and use mocks to verify its behavior.
  8963. </para>
  8964. <programlisting language="javascript">
  8965. test(&quot;should call all subscribers when exceptions&quot;, function () {
  8966. var myAPI = { clearTodo: function () {} };
  8967. var spy = this.spy();
  8968. var mock = this.mock( myAPI );
  8969. mock.expects( &quot;clearTodo&quot; ).once().throws();
  8970. PubSub.subscribe( &quot;message&quot;, myAPI.clearTodo );
  8971. PubSub.subscribe( &quot;message&quot;, spy );
  8972. PubSub.publishSync( &quot;message&quot;, undefined );
  8973. mock.verify();
  8974. ok( spy.calledOnce );
  8975. });
  8976. </programlisting>
  8977. </sect3>
  8978. </sect2>
  8979. </sect1>
  8980. <sect1 id="practical-3">
  8981. <title>Practical</title>
  8982. <para>
  8983. We can now begin writing test specs for our Todo application, which
  8984. are listed and separated by component (e.g Models, Collections
  8985. etc.). It’s useful to pay attention to the name of the test, the
  8986. logic being tested and most importantly the assertions being made as
  8987. this will give you some insight into how what we’ve learned can be
  8988. applied to a complete application.
  8989. </para>
  8990. <para>
  8991. To get the most out of this section, I recommend looking at the
  8992. QUnit Koans included in the
  8993. <literal>practicals\qunit-koans</literal> folder - this is a port of
  8994. the Backbone.js Jasmine Koans over to QUnit that I converted for
  8995. this post.
  8996. </para>
  8997. <para>
  8998. <emphasis>In case you haven’t had a chance to try out one of the
  8999. Koans kits as yet, they are a set of unit tests using a specific
  9000. testing framework that both demonstrate how a set of specs for an
  9001. application may be written, but also leave some tests unfilled so
  9002. that you can complete them as an exercise.</emphasis>
  9003. </para>
  9004. <sect2 id="models-3">
  9005. <title>Models</title>
  9006. <para>
  9007. For our models we want to at minimum test that:
  9008. </para>
  9009. <itemizedlist>
  9010. <listitem>
  9011. <para>
  9012. New instances can be created with the expected default values
  9013. </para>
  9014. </listitem>
  9015. <listitem>
  9016. <para>
  9017. Attributes can be set and retrieved correctly
  9018. </para>
  9019. </listitem>
  9020. <listitem>
  9021. <para>
  9022. Changes to state correctly fire off custom events where needed
  9023. </para>
  9024. </listitem>
  9025. <listitem>
  9026. <para>
  9027. Validation rules are correctly enforced
  9028. </para>
  9029. </listitem>
  9030. </itemizedlist>
  9031. <programlisting language="javascript">
  9032. module( 'About Backbone.Model');
  9033. test('Can be created with default values for its attributes.', function() {
  9034. expect( 1 );
  9035. var todo = new Todo();
  9036. equal( todo.get('text'), &quot;&quot; );
  9037. });
  9038. test('Will set attributes on the model instance when created.', function() {
  9039. expect( 3 );
  9040. var todo = new Todo( { text: 'Get oil change for car.' } );
  9041. equal( todo.get('text'), &quot;Get oil change for car.&quot; );
  9042. equal( todo.get('done'), false );
  9043. equal( todo.get('order'), 0 );
  9044. });
  9045. test('Will call a custom initialize function on the model instance when created.', function() {
  9046. expect( 1 );
  9047. var toot = new Todo({ text: 'Stop monkeys from throwing their own crap!' });
  9048. equal( toot.get('text'), 'Stop monkeys from throwing their own rainbows!' );
  9049. });
  9050. test('Fires a custom event when the state changes.', function() {
  9051. expect( 1 );
  9052. var spy = this.spy();
  9053. var todo = new Todo();
  9054. todo.bind( 'change', spy );
  9055. // How would you update a property on the todo here?
  9056. // Hint: http://documentcloud.github.com/backbone/#Model-set
  9057. todo.set( { text: &quot;new text&quot; } );
  9058. ok( spy.calledOnce, &quot;A change event callback was correctly triggered&quot; );
  9059. });
  9060. test('Can contain custom validation rules, and will trigger an error event on failed validation.', function() {
  9061. expect( 3 );
  9062. var errorCallback = this.spy();
  9063. var todo = new Todo();
  9064. todo.bind('error', errorCallback);
  9065. // What would you need to set on the todo properties to cause validation to fail?
  9066. todo.set( { done: &quot;not a boolean&quot; } );
  9067. ok( errorCallback.called, 'A failed validation correctly triggered an error' );
  9068. notEqual( errorCallback.getCall(0), undefined );
  9069. equal( errorCallback.getCall(0).args[1], 'Todo.done must be a boolean value.' );
  9070. });
  9071. </programlisting>
  9072. </sect2>
  9073. <sect2 id="collections-3">
  9074. <title>Collections</title>
  9075. <para>
  9076. For our collection we’ll want to test that:
  9077. </para>
  9078. <itemizedlist>
  9079. <listitem>
  9080. <para>
  9081. New model instances can be added as both objects and arrays
  9082. </para>
  9083. </listitem>
  9084. <listitem>
  9085. <para>
  9086. Changes to models result in any necessary custom events being
  9087. fired
  9088. </para>
  9089. </listitem>
  9090. <listitem>
  9091. <para>
  9092. A <literal>url</literal> property for defining the URL
  9093. structure for models is correctly defined
  9094. </para>
  9095. </listitem>
  9096. </itemizedlist>
  9097. <programlisting language="javascript">
  9098. module( 'About Backbone.Collection');
  9099. test( 'Can add Model instances as objects and arrays.', function() {
  9100. expect( 3 );
  9101. var todos = new TodoList();
  9102. equal( todos.length, 0 );
  9103. todos.add( { text: 'Clean the kitchen' } );
  9104. equal( todos.length, 1 );
  9105. todos.add([
  9106. { text: 'Do the laundry', done: true },
  9107. { text: 'Go to the gym' }
  9108. ]);
  9109. equal( todos.length, 3 );
  9110. });
  9111. test( 'Can have a url property to define the basic url structure for all contained models.', function() {
  9112. expect( 1 );
  9113. var todos = new TodoList();
  9114. equal( todos.url, '/todos/' );
  9115. });
  9116. test('Fires custom named events when the models change.', function() {
  9117. expect(2);
  9118. var todos = new TodoList();
  9119. var addModelCallback = this.spy();
  9120. var removeModelCallback = this.spy();
  9121. todos.bind( 'add', addModelCallback );
  9122. todos.bind( 'remove', removeModelCallback );
  9123. // How would you get the 'add' event to trigger?
  9124. todos.add( {text:&quot;New todo&quot;} );
  9125. ok( addModelCallback.called );
  9126. // How would you get the 'remove' callback to trigger?
  9127. todos.remove( todos.last() );
  9128. ok( removeModelCallback.called );
  9129. });
  9130. </programlisting>
  9131. </sect2>
  9132. <sect2 id="views-4">
  9133. <title>Views</title>
  9134. <para>
  9135. For our views we want to ensure:
  9136. </para>
  9137. <itemizedlist>
  9138. <listitem>
  9139. <para>
  9140. They are being correctly tied to a DOM element when created
  9141. </para>
  9142. </listitem>
  9143. <listitem>
  9144. <para>
  9145. They can render, after which the DOM representation of the
  9146. view should be visible
  9147. </para>
  9148. </listitem>
  9149. <listitem>
  9150. <para>
  9151. They support wiring up view methods to DOM elements
  9152. </para>
  9153. </listitem>
  9154. </itemizedlist>
  9155. <para>
  9156. One could also take this further and test that user interactions
  9157. with the view correctly result in any models that need to be
  9158. changed being updated correctly.
  9159. </para>
  9160. <programlisting language="javascript">
  9161. module( 'About Backbone.View', {
  9162. setup: function() {
  9163. $('body').append('&lt;ul id=&quot;todoList&quot;&gt;&lt;/ul&gt;');
  9164. this.todoView = new TodoView({ model: new Todo() });
  9165. },
  9166. teardown: function() {
  9167. this.todoView.remove();
  9168. $('#todoList').remove();
  9169. }
  9170. });
  9171. test('Should be tied to a DOM element when created, based off the property provided.', function() {
  9172. expect( 1 );
  9173. equal( this.todoView.el.tagName.toLowerCase(), 'li' );
  9174. });
  9175. test('Is backed by a model instance, which provides the data.', function() {
  9176. expect( 2 );
  9177. notEqual( this.todoView.model, undefined );
  9178. equal( this.todoView.model.get('done'), false );
  9179. });
  9180. test('Can render, after which the DOM representation of the view will be visible.', function() {
  9181. this.todoView.render();
  9182. // Hint: render() just builds the DOM representation of the view, but doesn't insert it into the DOM.
  9183. // How would you append it to the ul#todoList?
  9184. // How do you access the view's DOM representation?
  9185. //
  9186. // Hint: http://documentcloud.github.com/backbone/#View-el
  9187. $('ul#todoList').append(this.todoView.el);
  9188. equal($('#todoList').find('li').length, 1);
  9189. });
  9190. asyncTest('Can wire up view methods to DOM elements.', function() {
  9191. expect( 2 );
  9192. var viewElt;
  9193. $('#todoList').append( this.todoView.render().el );
  9194. setTimeout(function() {
  9195. viewElt = $('#todoList li input.check').filter(':first');
  9196. equal(viewElt.length &gt; 0, true);
  9197. // Make sure that QUnit knows we can continue
  9198. start();
  9199. }, 1000, 'Expected DOM Elt to exist');
  9200. // Hint: How would you trigger the view, via a DOM Event, to toggle the 'done' status.
  9201. // (See todos.js line 70, where the events hash is defined.)
  9202. //
  9203. // Hint: http://api.jquery.com/click
  9204. $('#todoList li input.check').click();
  9205. expect( this.todoView.model.get('done'), true );
  9206. });
  9207. </programlisting>
  9208. </sect2>
  9209. <sect2 id="events">
  9210. <title>Events</title>
  9211. <para>
  9212. For events, we may want to test a few different use cases:
  9213. </para>
  9214. <itemizedlist>
  9215. <listitem>
  9216. <para>
  9217. Extending plain objects to support custom events
  9218. </para>
  9219. </listitem>
  9220. <listitem>
  9221. <para>
  9222. Binding and triggering custom events on objects
  9223. </para>
  9224. </listitem>
  9225. <listitem>
  9226. <para>
  9227. Passing along arguments to callbacks when events are triggered
  9228. </para>
  9229. </listitem>
  9230. <listitem>
  9231. <para>
  9232. Binding a passed context to an event callback
  9233. </para>
  9234. </listitem>
  9235. <listitem>
  9236. <para>
  9237. Removing custom events
  9238. </para>
  9239. </listitem>
  9240. </itemizedlist>
  9241. <para>
  9242. and a few others that will be detailed in our module below:
  9243. </para>
  9244. <programlisting language="javascript">
  9245. module( 'About Backbone.Events', {
  9246. setup: function() {
  9247. this.obj = {};
  9248. _.extend( this.obj, Backbone.Events );
  9249. this.obj.unbind(); // remove all custom events before each spec is run.
  9250. }
  9251. });
  9252. test('Can extend JavaScript objects to support custom events.', function() {
  9253. expect(3);
  9254. var basicObject = {};
  9255. // How would you give basicObject these functions?
  9256. // Hint: http://documentcloud.github.com/backbone/#Events
  9257. _.extend( basicObject, Backbone.Events );
  9258. equal( typeof basicObject.bind, 'function' );
  9259. equal( typeof basicObject.unbind, 'function' );
  9260. equal( typeof basicObject.trigger, 'function' );
  9261. });
  9262. test('Allows us to bind and trigger custom named events on an object.', function() {
  9263. expect( 1 );
  9264. var callback = this.spy();
  9265. this.obj.bind( 'basic event', callback );
  9266. this.obj.trigger( 'basic event' );
  9267. // How would you cause the callback for this custom event to be called?
  9268. ok( callback.called );
  9269. });
  9270. test('Also passes along any arguments to the callback when an event is triggered.', function() {
  9271. expect( 1 );
  9272. var passedArgs = [];
  9273. this.obj.bind('some event', function() {
  9274. for (var i = 0; i &lt; arguments.length; i++) {
  9275. passedArgs.push( arguments[i] );
  9276. }
  9277. });
  9278. this.obj.trigger( 'some event', 'arg1', 'arg2' );
  9279. deepEqual( passedArgs, ['arg1', 'arg2'] );
  9280. });
  9281. test('Can also bind the passed context to the event callback.', function() {
  9282. expect( 1 );
  9283. var foo = { color: 'blue' };
  9284. var changeColor = function() {
  9285. this.color = 'red';
  9286. };
  9287. // How would you get 'this.color' to refer to 'foo' in the changeColor function?
  9288. this.obj.bind( 'an event', changeColor, foo );
  9289. this.obj.trigger( 'an event' );
  9290. equal( foo.color, 'red' );
  9291. });
  9292. test( &quot;Uses 'all' as a special event name to capture all events bound to the object.&quot; , function() {
  9293. expect( 2 );
  9294. var callback = this.spy();
  9295. this.obj.bind( 'all', callback );
  9296. this.obj.trigger( &quot;custom event 1&quot; );
  9297. this.obj.trigger( &quot;custom event 2&quot; );
  9298. equal( callback.callCount, 2 );
  9299. equal( callback.getCall(0).args[0], 'custom event 1' );
  9300. });
  9301. test('Also can remove custom events from objects.', function() {
  9302. expect( 5 );
  9303. var spy1 = this.spy();
  9304. var spy2 = this.spy();
  9305. var spy3 = this.spy();
  9306. this.obj.bind( 'foo', spy1 );
  9307. this.obj.bind( 'bar', spy1 );
  9308. this.obj.bind( 'foo', spy2 );
  9309. this.obj.bind( 'foo', spy3 );
  9310. // How do you unbind just a single callback for the event?
  9311. this.obj.unbind( 'foo', spy1 );
  9312. this.obj.trigger( 'foo' );
  9313. ok( spy2.called );
  9314. // How do you unbind all callbacks tied to the event with a single method
  9315. this.obj.unbind( 'foo' );
  9316. this.obj.trigger( 'foo' );
  9317. ok( spy2.callCount, 1 );
  9318. ok( spy2.calledOnce, &quot;Spy 2 called once&quot; );
  9319. ok( spy3.calledOnce, &quot;Spy 3 called once&quot; );
  9320. // How do you unbind all callbacks and events tied to the object with a single method?
  9321. this.obj.unbind( 'bar' );
  9322. this.obj.trigger( 'bar' );
  9323. equal( spy1.callCount, 0 );
  9324. });
  9325. </programlisting>
  9326. </sect2>
  9327. <sect2 id="app">
  9328. <title>App</title>
  9329. <para>
  9330. It can also be useful to write specs for any application bootstrap
  9331. you may have in place. For the following module, our setup
  9332. initiates and appends a TodoApp view and we can test anything from
  9333. local instances of views being correctly defined to application
  9334. interactions correctly resulting in changes to instances of local
  9335. collections.
  9336. </para>
  9337. <programlisting language="javascript">
  9338. module( 'About Backbone Applications' , {
  9339. setup: function() {
  9340. Backbone.localStorageDB = new Store('testTodos');
  9341. $('#qunit-fixture').append('&lt;div id=&quot;app&quot;&gt;&lt;/div&gt;');
  9342. this.App = new TodoApp({ appendTo: $('#app') });
  9343. },
  9344. teardown: function() {
  9345. this.App.todos.reset();
  9346. $('#app').remove();
  9347. }
  9348. });
  9349. test('Should bootstrap the application by initializing the Collection.', function() {
  9350. expect( 2 );
  9351. notEqual( this.App.todos, undefined );
  9352. equal( this.App.todos.length, 0 );
  9353. });
  9354. test( 'Should bind Collection events to View creation.' , function() {
  9355. $('#new-todo').val( 'Foo' );
  9356. $('#new-todo').trigger(new $.Event( 'keypress', { keyCode: 13 } ));
  9357. equal( this.App.todos.length, 1 );
  9358. });
  9359. </programlisting>
  9360. </sect2>
  9361. <sect2 id="further-reading-resources">
  9362. <title>Further Reading &amp; Resources</title>
  9363. <para>
  9364. That’s it for this section on testing applications with QUnit and
  9365. SinonJS. I encourage you to try out the
  9366. <ulink url="https://github.com/addyosmani/backbone-koans-qunit">QUnit
  9367. Backbone.js Koans</ulink> and see if you can extend some of the
  9368. examples. For further reading consider looking at some of the
  9369. additional resources below:
  9370. </para>
  9371. <itemizedlist>
  9372. <listitem>
  9373. <para>
  9374. <emphasis role="strong"><ulink url="http://tddjs.com/">Test-driven
  9375. JavaScript Development (book)</ulink></emphasis>
  9376. </para>
  9377. </listitem>
  9378. <listitem>
  9379. <para>
  9380. <emphasis role="strong"><ulink url="http://sinonjs.org/qunit/">SinonJS/QUnit
  9381. Adapter</ulink></emphasis>
  9382. </para>
  9383. </listitem>
  9384. <listitem>
  9385. <para>
  9386. <emphasis role="strong"><ulink url="http://cjohansen.no/en/javascript/using_sinon_js_with_qunit">SinonJS
  9387. and QUnit</ulink></emphasis>
  9388. </para>
  9389. </listitem>
  9390. <listitem>
  9391. <para>
  9392. <emphasis role="strong"><ulink url="http://msdn.microsoft.com/en-us/scriptjunkie/gg749824">Automating
  9393. JavaScript Testing With QUnit</ulink></emphasis>
  9394. </para>
  9395. </listitem>
  9396. <listitem>
  9397. <para>
  9398. <emphasis role="strong"><ulink url="http://benalman.com/talks/unit-testing-qunit.html">Ben
  9399. Alman’s Unit Testing With QUnit</ulink></emphasis>
  9400. </para>
  9401. </listitem>
  9402. <listitem>
  9403. <para>
  9404. <emphasis role="strong"><ulink url="https://github.com/jc00ke/qunit-backbone">Another
  9405. QUnit/Backbone.js demo
  9406. project</ulink></emphasis><literallayout></literallayout>
  9407. </para>
  9408. </listitem>
  9409. <listitem>
  9410. <para>
  9411. <emphasis role="strong"><ulink url="http://devblog.supportbee.com/2012/02/10/helpers-for-testing-backbone-js-apps-using-jasmine-and-sinon-js/">SinonJS
  9412. helpers for Backbone</ulink></emphasis>
  9413. </para>
  9414. </listitem>
  9415. </itemizedlist>
  9416. </sect2>
  9417. <sect2 id="resources">
  9418. <title><a name="resources">Resources</a></title>
  9419. <para>
  9420. Whilst we get with Backbone out of the box can be terribly useful,
  9421. there are some equally beneficial add-ons that can help simplify
  9422. our development process. These include:
  9423. </para>
  9424. <itemizedlist>
  9425. <listitem>
  9426. <para>
  9427. <ulink url="https://github.com/tbranyen/backbone.layoutmanager">Backbone
  9428. Layout Manager</ulink>
  9429. </para>
  9430. </listitem>
  9431. <listitem>
  9432. <para>
  9433. <ulink url="https://github.com/tbranyen/backbone-boilerplate">Backbone
  9434. Boilerplate</ulink>
  9435. </para>
  9436. </listitem>
  9437. <listitem>
  9438. <para>
  9439. <ulink url="https://github.com/derickbailey/backbone.modelbinding">Backbone
  9440. Model Binding</ulink>
  9441. </para>
  9442. </listitem>
  9443. <listitem>
  9444. <para>
  9445. <ulink url="https://github.com/PaulUithol/Backbone-relational">Backbone
  9446. Relational - for model relationships</ulink>
  9447. </para>
  9448. </listitem>
  9449. <listitem>
  9450. <para>
  9451. <ulink url="https://gist.github.com/1271041">View and model
  9452. inheritance</ulink>
  9453. </para>
  9454. </listitem>
  9455. <listitem>
  9456. <para>
  9457. <ulink url="https://github.com/derickbailey/backbone.marionette">Backbone
  9458. Marionette</ulink>
  9459. </para>
  9460. </listitem>
  9461. <listitem>
  9462. <para>
  9463. <ulink url="https://github.com/janmonschke/backbone-couchdb">Backbone
  9464. CouchDB</ulink>
  9465. </para>
  9466. </listitem>
  9467. <listitem>
  9468. <para>
  9469. <ulink url="https://github.com/n-time/backbone.validations">Backbone
  9470. Validations - HTML5 inspired validations</ulink>
  9471. </para>
  9472. </listitem>
  9473. </itemizedlist>
  9474. <para>
  9475. In time, there will be tutorials in the book covering some of
  9476. these resources but until then, please feel free to check them
  9477. out.
  9478. </para>
  9479. </sect2>
  9480. <sect2 id="conclusions-2">
  9481. <title><a name="conclusions">Conclusions</a></title>
  9482. <para>
  9483. That’s it for <quote>Developing Backbone.js Applications</quote>.
  9484. I hope you found this book both useful, enlightening and a good
  9485. start for your journey into exploring Backbone.js.
  9486. </para>
  9487. <para>
  9488. Remember, If there are other topics or areas of this book you feel
  9489. could be expanded further, please feel free to let me know, or
  9490. better yet, send a pull request upstream. I’m always interested in
  9491. making this title as comprehensive as possible.
  9492. </para>
  9493. <para>
  9494. Until next time, the very best of luck with the rest of your
  9495. journey!
  9496. </para>
  9497. <para>
  9498. Copyright Addy Osmani, 2012.
  9499. </para>
  9500. </sect2>
  9501. </sect1>
  9502. </article>