Tuesday, January 3, 2017

Time travelling development--for JS apps

TL;DR To how things should really work, watch this talk by Dan Abramov who created Redux, and the debugger concept.

Long version:
Make a change in your code. See it updated instantly. That's what I want because modern development is an iterative process, with fast iterations.

We've got something like that in JS playgrounds like JSFiddle, CodePen, and so on. But there's still a couple of things that are wrong. Make a change and the app runs again, from the top.  It's pretty quick, and for small bits of code, there may be almost no lag. But as your app scales, the time grows.

You can write a test driver, so that once the app loads, it tests itself, but that's another whole lot of code that you have to write. And even then, reloading takes time, and most of the changes that you make are teeny tiny ones. Even a second or two for a reload is too long.

You can do this in your Dev environment, as well as

And you don't want to test your whole app every time you make a change. Most changes are not going to break the app. They're just little tweaks. What you'd like to do is this: put the app in a certain state. Make your code change. Do some fiddling with the interface. See if it works right. If it doesn't then:

  • Revert to the earlier change
  • Rerun the interface twiddles
  • Repeat until it's perfect
  • Make this the new base, state
  • Repeat forever
And if you run into problems you'd like to go back to your base state with the ability to back up through the twiddles and debug anywhere. Now someone has pretty much done that using mainstream tools.

The key is making your code functional. Take state out of the functions. It's a variation on MVC programming. And it points out a flaw in MVC.

The usual MVC routine is this: you've got a data structure, which is your model. You've got code that takes the Model and renders a View. The view contains UI Control components. When a user manipulates the Controls, the model changes -- either by two-way binding, or by calling functions that are part of the Model, or by directly manipulating the Model data structure. But sometimes the View has its own controlling data structures. So there's a Data Model, and a View Model to pay attention to.

Instead, design code using Model, View, Controller, and Actions. The rules are:

All state is in the Model. Base data state and View state are in different parts of the one and only Model.

Models are POJOs, so they can be serialized and parsed back to their former state.

Actions are Plain Old Javascript Objects (POJOS) emitted by Controllers. 

A part of a Model is updated by calling a model updating function with an Action. There is no other way to update a Model. Model updating functions are called Reducers.

Reducers functions have the following signature: (Oldstate, action) -> newState, the signature used for functions called by Array.reduce They take the prior state as an input parameter and produce a new state. Because of their signature, reducers can be called with an array of Actions, using Array.map. So calling:
arrayOfActions.reduce(reducer, initialState)
Will return the final state, after applying all of the actions. 

Reducers don't update the Model; they replace the old state with a new state, in which the changes have been made according to the Actions. So state objects are immutable.

As a consequence:
  • We can save and restore the application state at any time
  • We can take any state, and apply a set of actions to it, giving the state after the actions
This lets us create a debugger that will do what we want it to do.

The technologies that make this easy to do are React and Redux. React is a framework for view rendering. In ordinary practice, View components can be stateless or stateful. Redux is a framework for managing states for React-based applications so that View components can be stateless.

To see how this works, watch this talk by Dan Abromov who created Redux, and the debugger concept.

Also read the docs for React and Redux, which I am in the process of doing.

Sunday, January 1, 2017

Browser synchronization, event capture

BrowserSync is an Open Source project that keeps multiple browser instances in sync.

I've downloaded it and tested it. It has some problems, but it mainly works as advertised. And it can do a bunch of thing that an awesome environment would need to do.

First: the main idea. BrowerSync captures events from each browser that's displaying data from a site that it serves, and forwards those events to other browsers looking at the same site. Even better, the BrowserSync server can proxy another server, including one serving pages by https.

Here's a review of some competing technologies by Addy Osmani, and his review of BrowserSync or browser-sync (github) and site.

I started by installing browser-sync

npm install -g browser-sync

I started by testing it with a polymer/firebase project. I cd's into the directory and ran:

$firebase serve 

That gave:

Starting Firebase development server...

Project Directory: /home/mwolf/tools/prog-with-papa
Public Directory: dist

Server listening at: http://localhost:5000


In another console, I run browser-sync in proxy mode:

browser-sync start -p  http://localhost:5000

That gives:

[BS] Proxying: http://localhost:5000
[BS] Access URLs:
------------------------------------
      Local: http://localhost:3000
   External: http://192.168.1.2:3000
------------------------------------
         UI: http://localhost:3001
UI External: http://192.168.1.2:3001
------------------------------------

And pops open a window on http://localhost:3000

I opened an incognito window and went to the same URL. Sure enough, clicking on menu selections in one made the same changes happen in the other. One of the pages had a text box. Entering text in one entered it in the other. But scrolling the form did not sync! I also tested it using my phone and the external port. Worked fine.

Next test. I proxy a site on the web.

browser-sync start -p  http://slatestarcodex.com/

Next test. I proxy a site on the web. Works fine, including navigation within the site and scrolling. So maybe the scrolling problem has something to do with Polymer?

Then I proxy the site using https. I get:

[BS] Proxying: https://slatestarcodex.com
[BS] Access URLs:
-------------------------------------
      Local: https://localhost:3000
   External: https://192.168.1.2:3000
-------------------------------------
         UI: http://localhost:3001
UI External: http://192.168.1.2:3001

Again, it works as hoped for.

Will it work for google?

browser-sync start -p  https://www.google.com

Opens a browser, but tells me that there's a security problem: the site doesn't have a correct SSL certificate. I approve it as an exception, and it works!

I try with facebook. Facebook lets me get public content, but its security protocols won't let me log in. I assume that the same thing will happen with any site withy good security.

So BrowserSync does some of the things that I want it to do. Others, like bypassing security, are probably impossible by any means.

Couple this with Chrome Plugins, and I may be able to do some of the hacking that I want to do.

At the heart of browser-sync is its ability to capture events and forward them. On the road to discovery, I found the following resources:

HTMLGoodies Advanced Javascript event handling

Ghostlab

Comparison of Ghostlab and BrowserSync

EventTarget.addEventListener at MDN, defines the basic procedure for handling events. By overriding EventTarget.prototype.addEventListener you can capture events for all targets, and do funky things with them.

An article about event capture on css-tricks.com by one of the authors of GhostLab. The article includes links to several CodePen examples for capture.


The workbench paradigm

IDEs and other power software tools suffer a tension between power and complexity. A photo editing tool has a dozen menus; each might have a dozen selections, and many of those might have submenus. If you remove all the menus and selection, you remove the tool's power. If you keep them, then finding out how to do the thing you want to do next means searching for the right command.

Some tools get you around this by letting you type a string that fuzzy-matches to the function that you are looking for. That's better, but still requires quite a bit of fiddling to get the function that you want.

Instead, consider a shop project as a governing paradigm. A skilled craftsman has a workshop full of tools and materials, but for any one task, needs only a few. Before commencing work the craftsman grabs the needed tools and materials, lays them out on the workbench in easy reach, and then sets to work. When possible, a workman prefers materials as close to finished size as possible. He'd prefer already-turned drawer pulls to blocks of raw wood he can turn into pulls.

So, consider building a GUI. The usual method is to go to a palette of components, pick one, place it, then assign properties to it. Then pick the next one, and so on. Instead, pick only the components that you'll need; choose the items in the palette that you want to be visible, and hide the rest; when you've adapted one to size, add it to the palette.

Consider building something with a bunch of icons using Polymer's iron-icons. If we were doing it with a GUI builder, we might pick iron-icon from a palette of iron elemements (there are 35 of them) place it, pull up a property sheet to set its properties, and continue.

One property is the icon image name from an iron-iconset. So we'd choose the icon images that we want. Then on to pick the next icon. If we're a little smart, then instead of picking another icon from the palette we'd duplicate the first one. But we're still left with the task of choosing another image.

Instead, we'd do better to go to a collection of pre-made iron-icons, each with a different image, and use them as our components.

The Polymer library lets us define a collection, and add iron-icon to the collection. But we can only add the raw iron-icon element's definition. And it has another element that contains a set of icon images. But it doesn't have a nice way to subset the images, or to attach a chosen image to an icon instance other than cut and paste with the text editor.