Ottergram was fun and interesting and adorable. And we learned a lot. But it's now time to turn to a larger, more complex web application: CoffeeRun

Rather than implement it in a number of small steps, we'll show the full implementation, but discuss the various components one at a time. Later, we'll add some additional features, but this is the

basic coffeerun

Note that, unlike Ottergram, there will be parts of the code that we won't discuss until a bit later, so there will be things that I'll ask you to ignore for now.

Operation

The idea of coffeerun is a web app for use by a "food truck" or similar commercial enterprise. Customers or servers will fill out a form that asks about the specifications of the drink:

  • what it is (e.g. coffee, tea, latte)
  • the email address associated with the order,
  • the size
  • any flavor shots
  • caffeine level

The email address will be the unique identifier for the order. (As a side-effect, that means only one order can be associated with a single email address, so no sending one intern to order for the whole office.)

When the order is placed, a checkbox is created on a checklist, and when the barista has created that order, they can check it off. When it's checked off, the checkbox disappears. Try it!

basic coffeerun

Components

Part of the pedagogical goal of the Coffeerun app is to talk about modularity: how to divide up a complex coding project into modules that collectively solve the problem. This is a variation on the divide-conquer-glue idea that you learned about in CS 111.

Coffeerun is just barely big enough to justify modularity. In real-life systems, there might be more modules and each module might do more things, but that's a difference in quantity. I think Coffeerun does a good job conveying the ideas.

Coffeerun has the following modules:

  • datastore: this module takes care of storing the collection of orders. It's a kind of database. We'll look up orders in the database by the email address of the person who placed the order. The datastore is a pretty generic database and could be used in other projects.
  • truck: this represents the orders for a single food-truck. In Coffeerun, we'll only have one truck, but it's a good idea to plan for expansion.
  • formhandler: this module provides some useful code to gather the information in a form so that it can be processed. The code is generic, in the sense that it can be used for any form. You could imagine using the formhandler module in a very different system.
  • checklist: this module creates and manages the checklist of orders.

That's enough for now. We'll see how these work together over the next few class meetings.

Modularity

When JavaScript was first created, it didn't have language features to support modularity (unlike Python, Java and many other languages). Any code that was loaded via a script tag was accessible to any other code. For example, suppose we have two JS files, colors.js and main.js and we load them both:

<script src="colors.js"></script>
<script src="main.js"></script>

Suppose that colors.js contains the following code:

var fgColor = '#ff8080';
var bgColor = '#eeeeee';

and main.js refers to those colors:

$('some selector').css({'color': fgColor, 'background-color': bgColor});

That works fine. There is no "barrier" of any sort between the two files. All the names are poured into one big bucket.

That worked well for many years, but eventually, larger projects needed a way to have separate modules and namespaces. So, that was added to the JavaScript language in about 2016.

Let's learn about modules

Go ahead; I'll wait. Then we'll look at modules in Coffeerun.

CoffeeRun with Modules

Look at the source code for

basic CoffeeRun

Let's take a minute to see how CoffeeRun is organized:

coffeerun/
    index.html
    scripts/
        main-module.js
        checklist-module.js
        formhandler-module.js
        datastore-module.js
        truck-module.js

The index.html file is the main page. It has all the static HTML and links the CSS files. It also loads the main module:

    <script type="module" src="scripts/main-module.js"></script>

The main-module.js imports the others:

import { CheckList, makeDescription, makeRowElt } from './checklist-module.js';
import { FormHandler } from './formhandler-module.js';
import { DataStore } from './datastore-module.js';
import { Truck } from './truck-module.js';

(There's other code in there as well, which we'll get to later in the course.)

The first file that we'll be looking at (next time) is the datastore-module.js:

/* Rewritten by Scott, Summer 2020. This implements a class DataStore
 * that has an API for a key/value database. 
 *
 * This version of the API uses return values.
*/

class DataStore {
    // instance variables
    data = {};

    constructor() {
        console.log('running the DataStore function')
        this.data = {};
    }

    add (key, val) {
        this.data[key] = val;
    }

    get (key) {
        return this.data[key];
    }

    getAll () {
        return this.data;
    }

    remove (key) {
        delete this.data[key];
    }
}

var ex1 = new DataStore();
ex1.add('harry', {name: 'Harry Potter',
                  hair: 'black',
                  house: 'Gryffindor'})
ex1.add('ron', {name: 'Ron Weasley',
                hair: 'red',
                house: 'Gryffindor'})
ex1.add('hermione', {name: 'Hermione Granger',
                     hair: 'brown',
                     house: 'Gryffindor'})

console.log('lookup harry', ex1.get('harry'));

function showDictionary(dic) {
    let keys = Object.keys(dic);
    keys.map(k => console.log(k, '=>', dic[k]));
}

console.log('all values before removing ron');
showDictionary(ex1.getAll());

ex1.remove('ron');

console.log('all values after removing ron');
showDictionary(ex1.getAll());

export { DataStore };

Notice the export at the end. That's the key step for today.

However, that module defines a class, which is the topic we will turn to next time.