Save 37% off PRO during our Black Friday Sale! »

reactive web applications with clojurescript and om

reactive web applications with clojurescript and om

Webmardi 2015-04-07

2fcc875f98607b3007909fe4be99160d?s=128

Pierre-Yves Ritschard

April 07, 2015
Tweet

Transcript

  1. REACTIVE APPLICATIONS WITH OM PIERRE-YVES RITSCHARD @PYR WEBMARDI

  2. @PYR CTO at exoscale, swiss cloud hosting Open source developer:

    pithos, cyanite, riemann, collectd, cloudstack Cassandra MVP
  3. AIM OF THIS TALK An introduction to clojure & clojurescript

    Show-casing a different method of building SPAs
  4. OUTLINE What is Om ? Clojure(script) whirlwind tour Better views

    with React An Om walkthrough
  5. OM

  6. WHAT IT SAYS ON THE BOX A ClojureScript interface to

    Facebook's React. https://github.com/omcljs/om
  7. OM SPECIFICS Small facade (< 2kLoC) for React No two-way

    binding! Data-based approach to UI Promotes immutability Interacts well with concurrency routines
  8. OM IN THE LARGE Circle CI Prismatic Percursor Goya

  9. OM AT EXOSCALE Warp Internal tools Our portal is still

    Angular.JS
  10. BETTER VIEWS WITH REACT

  11. WHAT IT SAYS ON THE BOX A JavaScript library for

    building user interfaces https://facebook.github.io/react
  12. JUST THE UI React takes care of the view and

    does not care about the rest of the application structure. As a library it integrates well with other projects, such as Backbone.
  13. VIRTUAL DOM Multiple buffering approach for the DOM. Changes are

    produced on a virtual DOM, differences are produced to the actual DOM in a single step.
  14. DATA FLOW Departure from two-way binding. Easier to reason about.

    Say goodbye to d i g e s t a l r e a d y i n p r o g r e s s .
  15. A COMPONENT BASED APPROACH Each component is responsible for a

    subset of the DOM and may implement functions which correspond to elements of the component's lifecycle: render (or renderState) willMount didMount willUnmount didUnmount
  16. THE PROBLEM WITH REACT r e n d e r

    : f u n c t i o n ( ) { r e t u r n ( < d i v > < h 3 > T O D O < / h 3 > < T o d o L i s t i t e m s = { t h i s . s t a t e . i t e m s } / > < f o r m o n S u b m i t = { t h i s . h a n d l e S u b m i t } > < i n p u t o n C h a n g e = { t h i s . o n C h a n g e } v a l u e = { t h i s . s t a t e . t e x t } / > < b u t t o n > { ' A d d # ' + ( t h i s . s t a t e . i t e m s . l e n g t h + 1 ) } < / b u t t o n > < / f o r m > < / d i v > ) ; }
  17. CLOJURE(SCRIPT): A WHIRLWIND TOUR

  18. WHAT IT SAYS ON THE BOX Clojure is a dynamic

    programming language that targets the Java Virtual Machine (and the CLR, and JavaScript). http://clojure.org
  19. CLOJURE SPECIFICS Dynamically typed LISP Homo-iconic Macros Functional programming language

    Immutable data structures & STM Runtime polymorphism Host independant
  20. CLOJURE LIBRARIES & TOOLING Typing Pattern-matching Concurrency routines Building and

    running
  21. DYNAMICALLY TYPED LISP ( + 1 2 ) ; ;

    = > 3 ( g e t { : f o o " b a r " } : f o o ) ; ; = > " b a r " ( c o u n t [ : a : b : c : d ] ) ; ; = > 4 ( l e t [ n 4 ] ( * n n ) ) ; ; = > 1 6
  22. DYNAMICALLY TYPED LISP: VARIABLES ( d e f m y

    - v a r " h e l l o " ) ; ; = > # ' m y - v a r ( d e f n f a c t o [ n ] ( r e d u c e * ( m a p i n c ( r a n g e n ) ) ) ) ; ; = > # ' f a c t o
  23. DYNAMICALLY TYPED LISP: LOOPS AND CONDITIONALS ( i f c

    o n d i t i o n ? t r u e - f o r m f a l s e - f o r m ) ( w h e n c o n d i t i o n ? t r u e - f o r m 1 t r u e - f o r m 2 ) ( d o s e q [ i ( r a n g e 1 0 ) ] ( p r i n t l n i ) ) 0
  24. HOMO ICONIC ; ; k e y w o r

    d s : f i r s t - n a m e ; ; m a p s { : u s e r " p y r " : f i r s t - n a m e " P i e r r e - Y v e s " : l a s t - n a m e " R i t s c h a r d " } ; ; v e c t o r s [ 0 1 2 : f o o 3 4 : b a r ] ; ; s e t s # { : e g g p l a n t : z u c c h i n i : p o t a t o : c a r o t } ; ; f u n c t i o n s # ( + % 1 ) ; ; r e g e x # " ^ m y n a m e i s ( . * ) $ "
  25. MACROS ( d e f m a c r o

    u n l e s s [ e x p & b o d y ] ( i f - n o t ~ e x p ( d o ~ @ b o d y ) ) ) ( u n l e s s s o m e - c o n d i t i o n d o - t h i s d o - t h a t )
  26. MACROS ( d e f n f a c t

    o [ n ] ( - > > ( r a n g e 1 0 ) ( m a p i n c ) ( r e d u c e * ) ) )
  27. DESTRUCTURING ( d e f u s e r {

    : u s e r " p y r " : f i r s t - n a m e " P i e r r e - Y v e s " : l a s t - n a m e " R i t s c h a r d " } ) ( l e t [ { : k e y s [ f i r s t - n a m e l a s t - n a m e ] } u s e r ] ( p r i n t l n " H e l l o " f i r s t - n a m e l a s t - n a m e ) )
  28. FUNCTIONAL PROGRAMMING It is better to have 100 functions operate

    on one data structure than 10 functions on 10 data structures. —Alan Perlis ( - > { : u s e r " p y r " : h i s t o r y [ ] : s t a t s { : s e s s i o n - c o u n t 0 : l o g g e d - i n f a l s e } } ( a s s o c - i n [ : s t a t s : l o g g e d - i n ] t r u e ) ( u p d a t e - i n [ : s t a t s : s e s s i o n - c o u n t ] i n c ) ( u p d a t e - i n [ : h i s t o r y ] c o n j { : a c t i o n : l o g - i n : u s e r : p y r } ) )
  29. FUNCTIONAL PROGRAMMING (CONT.) much better than underscore.js ; ; c

    o m p o s i t i o n ( c o m p n a m e k e y ) ( m a p ( c o m p n a m e k e y ) { : a c t i o n : l o g - i n : u s e r " p y r " } ) ; ; = > [ " a c t i o n " " u s e r " ] ; ; p a r t i a l a p p l i c a t i o n / c u r r y i n g ( d e f m y - i n c ( p a r t i a l + 1 ) ) ; ; c l o s u r e s ( d e f n i n c r e m e n t - b y [ n ] ( f n [ x ] ( + n x ) ) ) ( m a p ( i n c r e m e n t - b y 3 ) ( r a n g e 3 ) ) ; ; = > [ 3 4 5 ]
  30. IMMUTABLE DATASTRUCTURES ( d e f u s e r

    - d a t a { : u s e r " p y r " : s t a t s { : s e s s i o n - c o u n t 0 } } ) ( d e f n r e c o r d - s e s s i o n [ u d ] ( u p d a t e - i n u d [ : s t a t s : s e s s i o n - c o u n t ] i n c ) ) ( r e c o r d - s e s s i o n u s e r - d a t a ) ; ; = > { : u s e r " p y r " : s t a t s { : s e s s i o n - c o u n t 1 } } u s e r - d a t a ; ; = > { : u s e r " p y r " : s t a t s { : s e s s i o n - c o u n t 0 } }
  31. STM ( d e f u s e r -

    d a t a ( a t o m { : u s e r " p y r " : s t a t s { : s e s s i o n - c o u n t 0 } } ) ) ( d e f n r e c o r d - s e s s i o n [ u d ] ( u p d a t e - i n u d [ : s t a t s : s e s s i o n - c o u n t ] i n c ) ) ( s w a p ! u s e r - d a t a r e c o r d - s e s s i o n ) ; ; = > { : u s e r " p y r " ; ; : s t a t s { : s e s s i o n - c o u n t 1 } } @ u s e r - d a t a ; ; = > { : u s e r " p y r " ; ; : s t a t s { : s e s s i o n - c o u n t 1 } }
  32. RUNTIME POLYMORPHISM: MULTIMETHODS ( d e f m u l

    t i h a n d l e - e v e n t : a c t i o n ) ( d e f m e t h o d h a n d l e - e v e n t : l o g - i n [ { : k e y s [ u s e r ] } ] ( p r i n t l n " u s e r " u s e r " h a s l o g g e d i n " ) ) ( d e f m e t h o d h a n d l e - e v e n t : l o g - o u t [ { : k e y s [ u s e r ] } ] ( p r i n t l n " u s e r " u s e r " h a s l o g g e d i n " ) ) ( h a n d l e - e v e n t { : a c t i o n : l o g - i n : u s e r " p y r " } ) ( h a n d l e - e v e n t { : a c t i o n : l o g - o u t : u s e r " p y r " } )
  33. RUNTIME POLYMORPHISM: PROTOCOLS ( d e f p r o

    t o c o l K V D a t a b a s e ( s e t k e y ! [ t h i s k v ] ) ( g e t k e y [ t h i s k ] ) ) ( d e f n a t o m - d a t a b a s e [ ] ( l e t [ d b ( a t o m { } ) ] ( r e i f y K V D a t a b a s e ( s e t k e y ! [ t h i s k v ] ( s w a p ! d b a s s o c k v ) ) ( g e t k e y [ t h i s k ] ( g e t @ d b k ) ) ) ) )
  34. NAMESPACES ( n s w e b a p p

    . f r o n t e n d ( : r e q u i r e [ o m . d o m : a s d o m ] [ s a b l o n o . c o r e : r e f e r - m a c r o s [ h t m l ] ] ) )
  35. HOST INDEPENDANT JVM, CLR, Javascript (relies on Google Closure). Interop

    is a first class citizen: ( . l o g j s / w i n d o w . c o n s o l e " h e l l o " )
  36. SIMPLE EXAMPLE: AN HTTP + JSON REQUEST: ( n s

    f o o b a r ( : r e q u i r e [ a j a x . c o r e : r e f e r [ G E T ] ] ) ) ( d e f n h a n d l e - r e s p o n s e [ d a t a ] . . . ) ; ; A u t o m a t i c a l l y i n f e r s f o r m a t f r o m C o n t e n t - T y p e ( G E T " / m y - e n d p o i n t " { : h a n d l e r h a n d l e - r e s p o n s e } ) ; ; H i n t a t f o r m a t ( P U T " / m y - e n d p o i n t " { : p a r a m s { : f o o " b a r " } : f o r m a t : j s o n } )
  37. CLOJURESCRIPT ESSENTIAL LIBS: CORE.ASYNC ( n s t a b

    l e f l i p . c o r e ( : r e q u i r e - m a c r o s [ c l j s . c o r e . a s y n c . m a c r o s : r e f e r [ g o ] ] ) ( : r e q u i r e [ c l j s . c o r e . a s y n c : r e f e r [ t i m e o u t < ! ] ] ) ) ( g o ( l o o p [ [ c u r & n e x t ] ( c y c l e [ " ┬ ─ ┬ ノ( º _ º ノ) " " ( ╯ ° □ ° )╯ ︵ ┻ ━ ┻ " " ( ╯ ° □ ° )╯ ┬ ─ ┬ " " ( ╯ ° □ ° )╯ ┻ ━ ┻ " " ( ╯ ° □ ° )╯ ┬ ─ ┬ " " ( ╯ ° □ ° )╯ ┻ ━ ┻ " ] ) ] ( . r e p l a c e S t a t e j s / w i n d o w . h i s t o r y # j s { } " " ( s t r " / # " c u r ) ) ( < ! ( t i m e o u t 1 0 0 0 ) ) ( r e c u r n e x t ) ) )
  38. CLOJURESCRIPT ESSENTIAL LIBS: CORE.MATCH ( d o s e q

    [ n ( r a n g e 1 1 0 1 ) ] ( p r i n t l n ( m a t c h [ ( m o d n 3 ) ( m o d n 5 ) ] [ 0 0 ] " F i z z B u z z " [ 0 _ ] " F i z z " [ _ 0 ] " B u z z " : e l s e n ) ) )
  39. CLOJURESCRIPT ESSENTIAL LIBS: OPTIONAL TYPING Compile time: core.typed Runtime: schema

  40. BUILDING CLOJURESCRIPT Not bound to a specific build tool People

    usually choose (upside: no grunt or gulp debate) leiningen
  41. BUILDING CLOJURESCRIPT ( d e f p r o j

    e c t m y - p r o j e c t " 0 . 1 . 0 " : d e p e n d e n c i e s [ [ o r g . c l o j u r e / c l o j u r e " 1 . 6 . 0 " ] [ o r g . c l o j u r e / c l o j u r e s c r i p t " 0 . 0 - 3 1 2 6 " ] [ o r g . o m c l j s / o m " 0 . 8 . 8 " ] : p l u g i n s [ [ l e i n - c l j s b u i l d " 1 . 0 . 5 " ] ] : c l j s b u i l d { : b u i l d s [ { : i d " d e v " : s o u r c e - p a t h s [ " s r c " ] : c o m p i l e r { : m a i n " j o b s . f r o n t e n d " : o u t p u t - t o " r e s o u r c e s / p u b l i c / j s / a p p . j s " : o u t p u t - d i r " r e s o u r c e s / p u b l i c / j s " : o p t i m i z a t i o n s : n o n e : s o u r c e - m a p t r u e } } ] } )
  42. COMPILING AND RUNNING l e i n c l j

    s b u i l d o n c e l e i n c l j s b u i l d a u t o
  43. OM WALKTHROUGH

  44. BINDING CLOJURESCRIPT AND REACT All application state is stored in

    an atom. Changes in an atom result in UI updates Binds to react through protocols
  45. BINDING CLOJURESCRIPT AND REACT: PROTOCOLS A component reifies: IRenderState or

    IRender ( f n [ a p p o w n e r ] ( r e i f y I R e n d e r ( r e n d e r [ t h i s ] ( d o m / h 1 " h e l l o " ) ) ) )
  46. OM APPLICATION BASICS Applications are trees of components bound to

    a part of the application state which generate nodes in the virtual dom.
  47. OM APPLICATION BASICS ( d e f a p p

    - s t a t e ( a t o m { : j o b s [ { : t i t l e " d e v e l o p e r " : c o m p a n y " r a i l s s h o p " } { : t i t l e " s y s a d m i n " : c o m p a n y " p h p s h o p " } ] } ) ) ( d e f c o m p o n e n t j o b - l i n e [ p o s i t i o n o w n e r ] ( r e n d e r [ t h i s ] ( l e t [ t i t l e ( : t i t l e a p p ) c o m p a n y ( : c o m p a n y p o s i t i o n ) ] ( h t m l [ : l i ( s t r t i t l e " a t " c o m p a n y ) ] ) ) ) ) ( d e f c o m p o n e n t j o b - l i s t [ a p p o w n e r ] ( r e n d e r [ t h i s ] ( l e t [ j o b s ( : j o b s a p p ) ] ( h t m l [ : u l ( o m / b u i l d - a l l j o b - l i n e j o b s ) ] ) ) ) ) ( o m / r o o t j o b - l i s t a p p - s t a t e )
  48. OM APPLICATION BASICS

  49. ROUTING There is no routing component in om. It's easy

    to build one leveraging the application state Example approach: https://github.com/pyr/om- jobs/blob/master/src/jobs/router.cljs ; ; P a i r s o f r o u t e t o c o m p o n e n t ( d e f r o u t e s [ " / " v i e w s / j o b s " / j o b / : i d " v i e w s / j o b " / p o s t " v i e w s / j o b - p o s t ] ) ( l e t [ r o u t e r ( r o u t e r / i n i t r o u t e s a p p - s t a t e ) t a r g e t { : t a r g e t ( . g e t E l e m e n t B y I d j s / d o c u m e n t " a p p " ) } ] ( o m / r o o t r o u t e r a p p - s t a t e t a r g e t ) )
  50. USER INPUT Two approaches: Local component state Circling back to

    the global app-state
  51. USER INPUT: LOCAL COMPONENT STATE ( d e f c

    o m p o n e n t j o b - p o s t [ a p p o w n e r ] ( r e n d e r - s t a t e [ t h i s s t a t e ] ( l e t [ t i t l e ( : t i t l e s t a t e ) c o m p a n y ( : c o m p a n y s t a t e ) c r e a t e ! ( : c r e a t e ! a p p ) - > s t a t e ( f n [ k ] ( f n [ e v e n t ] ( o m / s e t - s t a t e ! o w n e r [ k ] ( - > e v e n t . - t a r g e t . - v a l u e ) ) ) ) ] ( h t m l [ : f o r m [ : l a b e l " t i t l e " ] [ : i n p u t { : t y p e " t e x t " : v a l u e t i t l e : o n - c h a n g e ( - > s t a t e : t i t l e ) } ] [ : l a b e l " c o m p a n y " ] [ : i n p u t { : t y p e " t e x t " : v a l u e c o m p a n y : o n - c h a n g e ( - > s t a t e : c o m p a n y ) } ] [ : i n p u t { : t y p e " s u b m i t " : o n - c l i c k ( f n [ _ ] ( c r e a t e ! s t a t e ) ) } ] ] ) ) ) )
  52. USER INPUT: GLOBAL STATE ( d e f m o

    d e l - c h a n ( c h a n ) ) ( g o ( l o o p [ a c t i o n ( < ! m o d e l - c h a n ) ] ( l e t [ t y p e ( : t y p e a c t i o n ) i d ( : i d a c t i o n ) ] ( c o n d p = t y p e : d e l e t e ( s w a p ! a p p - s t a t e d i s s o c i d ) : c r e a t e ( s w a p ! a p p - s t a t e a s s o c i d a c t i o n ) ) ) ) ) ( d e f c o m p o n e n t j o b - l i n e [ a p p o w n e r ] ( r e n d e r [ t h i s ] [ : l i [ : d i v [ : p ( : t i t l e a p p ) " a t " ( : c o m p a n y a p p ) ] [ : b u t t o n { : o n - c l i c k ( f n [ _ ] ( > ! m o d e l - c h a n { : t y p e : d e l e t e : i d ( : i d a p p ) } ) ) } ] ] ] ) )
  53. OM USE-CASES

  54. COMPARED TO ANGULAR More up-front work Better reasoning around components

    Not a fit for small CRUD apps
  55. BEST FITS Complex app logic Websockets / SSE (event-stream) Multiple

    presentation of a single source of data
  56. WHAT WE DID NOT SAY Infinite undo Component mixins Targets

    Node.JS Targets JavascriptCore HTML templates Source maps REPLs
  57. GOING FURTHER Om: Angular to Om: A simple job board:

    https://github.com/omcljs/om http://spootnik.org/entries/2014/10/26_from-angularjs-to- om-a-walk-through.html https://github.com/pyr/om-jobs
  58. QUESTIONS ?