Professional Documents
Culture Documents
With Flows
nathanhammond
/login/one-time-password/authenticate
/login/one-time-password/select-delivery-method
/statements/august
/transfers/78
/login
/login/electronic-consent
/contact
/login/reset-password
URLs
/login/one-time-password/setup
/login/terms-of-service-consent
/accounts/12
/tour
/login/complete
/recover-username
/transfers
/accounts
/statements
/logout
/recover-password
/login/one-time-password/register-device
/login/one-time-password/authenticate
/login/one-time-password/select-delivery-method
/statements/august
/transfers/78
/login/reset-password
/login
/tour
/login/complete
/recover-username
Resources
/login/electronic-consent
/transfers
/contact
/login/one-time-password/setup
/login/terms-of-service-consent
/accounts/12
/accounts
/statements
/logout
/recover-password
/login/one-time-password/register-device
/login/one-time-password/authenticate
/login/one-time-password/select-delivery-method /tour
/statements/august
/transfers/78
/login
/login/electronic-consent
/contact
/login/reset-password
Actions
/login/one-time-password/setup
/login/complete
/recover-username
/transfers
/accounts
/statements
/logout
Flows?
login
otp.
authenticate
accounts
Comparison
Resource
Action
Flow
Designing a Flow
Flow Spaghetti
Step 1:
Inventory Your Routes
Router.map(function() {
this.resource('authenticated', { path: '/' }, function() {
this.resource('accounts', { path: '/' }, function() {
this.route('details', { path : 'account/:account_id' });
});
this.resource('transfers');
this.resource('statements', function() {});
this.route('tour');
});
!
this.resource('login', function() {
this.resource('one-time-password', function() {
this.route('setup');
this.route('select-delivery-method');
this.route('authenticate');
this.route('register-device');
});
this.route('electronic-consent');
this.route('terms-of-service-consent');
this.route('reset-password');
this.route('complete');
});
!
this.route('logout');
this.route('recover-username');
this.route('recover-password');
this.route('contact');
});
https://github.com/alexdiliberto/emberconf-2014-demo/blob/master/app/router.js
Step 2:
List Linear Paths
Description
Steps
login.index
accounts.index
Login is clean.
login.index
login.electronicconsent?
login.terms-of-serviceconsent?
Invalid credentials.
login.index
login.index!
flash message
Dismiss message on
keypress.
tour?
accounts.index
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
accounts.index
contact
login.index
one-timeone-timepassword.authenticate password.registerdevice
login.index
one-timepassword.selectdelivery-method
one-timeone-timepassword.authenticate password.registerdevice
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
login.index
one-timepassword.setup
one-timepassword.registerdevice
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
accounts.index
login.reset-password
login.reset-passwordcomplete
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
accounts.index
login.reset-password
login.reset-passwordcomplete
one-timepassword.setup
one-timepassword.registerdevice
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
accounts.index
contact
one-timeone-timepassword.authenticate password.registerdevice
login.reset-password
login.reset-passwordcomplete
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
accounts.index
one-timepassword.selectdelivery-method
login.reset-password
login.reset-passwordcomplete
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
Recover Username
login.index!
flash message
login.electronicconsent?
login.terms-of-serviceconsent?
tour?
accounts.index
tour?
recover-username
one-timeone-timepassword.authenticate password.registerdevice
contact
recover-password
one-timelogin.reset-password
password.authenticate
recover-password
one-timepassword.selectdelivery-method
one-timelogin.reset-password
password.authenticate
login.reset-passwordcomplete
login.electronicconsent?
login.terms-of-serviceconsent?
login.index
one-timepassword.setup?
edit=true
one-timepassword.registerdevice
login.terms-of-serviceconsent?
tour?
accounts.index
login.reset-passwordcomplete
login.electronicconsent?
accounts.index
accounts.index
accounts.index
https://docs.google.com/spreadsheet/ccc?key=0ApnSRONV3LTVdGJuLUlnOGxzY1FadGZHN050ZVJXcWc
Step 3:
Convert Paths Into Node Graph
login
otp.setup
otp.selectdeliverymethod
electronicconsent
terms-ofserviceconsent
otp.
authenticate
complete
otp.
registerdevice
tour
accounts
- or deep link
Step 4:
Describe Edge Traversal
login
otp.setup
otp.selectdeliverymethod
otp.
authenticate
otp.
registerdevice
{
isIdentified: true,
isAuthenticated: false,
hasOneDeliveryMethod:
deliveryMethods.length,
}
electronicconsent
terms-ofserviceconsent
complete
tour
accounts
- or deep link
Step 5:
Identify Backwards Traversals
login
otp.setup
otp.selectdeliverymethod
electronicconsent
terms-ofserviceconsent
otp.
authenticate
complete
otp.
registerdevice
tour
accounts
- or deep link
Step 6:
Done!
resetpassword
login
otp.setup
otp.selectdeliverymethod
otp.
authenticate
otp.
registerdevice
resetpasswordcomplete
electronicconsent
recoverpassword
terms-ofserviceconsent
complete
tour
accounts
- or deep link
#1:
http://www.flickr.com/photos/joi/8224269021
#2:
Don't break the web.
#3:
Ember.Router is awesome.
A Quick Demo
Playing Along
http://alexdiliberto.com/emberconf-2014-demo
https://github.com/alexdiliberto/emberconf-2014-demo
http://alexdiliberto.com/emberconf-2014-demo/
// Direct Login
ic.ajax.defineFixture('/session', {
response: {
/* Successfully identified username and password. */
isIdentified: true,
!
/* Related to one-time-passcode. */
willSetupOTP: false,
isAuthenticated: true,
!
hasElectronicConsent: true,
hasTermsOfServiceConsent: true,
showTour: false
},
textStatus: 'success'
});
/* Related to one-time-passcode. */
willSetupOTP: false,
isAuthenticated: false,
!
hasElectronicConsent: true,
hasTermsOfServiceConsent: true,
showTour: false
},
textStatus: 'success'
});
Coding a Flow
A Naive Approach
git clone https://github.com/alexdiliberto/emberconf-2014-demo
git checkout tags/naive
General Strategy
beforeModel
beforeModel: function() {
if (this.get('session.isAuthenticated')) {
this.replaceWith('one-time-password.register-device');
}
}
Tangent: replaceWith
It's awesome. Use it.
{{action}}
actions: {
authenticate: function() {
ic.ajax.raw('/authenticate').then(function(result) {
if (result.response.isAuthenticated) {
this.set('session.isAuthenticated', true);
this.replaceWith('one-time-password.register-device');
}
});
}
}
An Improved Approach
The "Using Flows" part of the presentation.
General Strategy
Your flow tracks which edges you've traversed in case you need that
information in your application.
Definitions
LoginFlow.addEdge({
from: 'login.index',
to: 'accounts.index',
weight: 1,
conditions: ['isIdentified', 'isAuthenticated']
});
beforeModel
beforeModel: function() {
// Looks up the current flow.
// Identifies where the user should be.
this.get('flow').check();
}
{{action}}
actions: {
authenticate: function() {
var Flow = this.get('flow');
!
ic.ajax.raw('/authenticate').then(function(result) {
if (result.response.isAuthenticated) {
Flow.set('isAuthenticated', true);
Flow.progress();
}
});
}
}
ember-flows
https://github.com/nathanhammond/ember-flows
Resources
https://signalvnoise.com/posts/1926-a-shorthand-for-designing-ui-flows
https://docs.google.com/spreadsheet/ccc?
key=0ApnSRONV3LTVdGJuLUlnOGxzY1FadGZHN050ZVJXcWc
http://alexdiliberto.com/emberconf-2014-demo
https://github.com/alexdiliberto/emberconf-2014-demo
https://github.com/nathanhammond/ember-flows
https://code.google.com/p/libphonenumber/