Upgrade to Pro — share decks privately, control downloads, hide ads and more …

reactive web applications with clojurescript and om

reactive web applications with clojurescript and om

Webmardi 2015-04-07

Pierre-Yves Ritschard

April 07, 2015
Tweet

More Decks by Pierre-Yves Ritschard

Other Decks in Technology

Transcript

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

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

    Show-casing a different method of building SPAs
  3. OM

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

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

    binding! Data-based approach to UI Promotes immutability Interacts well with concurrency routines
  6. WHAT IT SAYS ON THE BOX A JavaScript library for

    building user interfaces https://facebook.github.io/react
  7. 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.
  8. 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.
  9. 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 .
  10. 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
  11. 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 > ) ; }
  12. 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
  13. CLOJURE SPECIFICS Dynamically typed LISP Homo-iconic Macros Functional programming language

    Immutable data structures & STM Runtime polymorphism Host independant
  14. 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
  15. 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
  16. 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
  17. 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 ( . * ) $ "
  18. 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 )
  19. 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 * ) ) )
  20. 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 ) )
  21. 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 } ) )
  22. 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 ]
  23. 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 } }
  24. 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 } }
  25. 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 " } )
  26. 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 ) ) ) ) )
  27. 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 ] ] ) )
  28. 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 " )
  29. 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 } )
  30. 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 ) ) )
  31. 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 ) ) )
  32. BUILDING CLOJURESCRIPT Not bound to a specific build tool People

    usually choose (upside: no grunt or gulp debate) leiningen
  33. 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 } } ] } )
  34. 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
  35. 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
  36. 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 " ) ) ) )
  37. OM APPLICATION BASICS Applications are trees of components bound to

    a part of the application state which generate nodes in the virtual dom.
  38. 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 )
  39. 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 ) )
  40. 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 ) ) } ] ] ) ) ) )
  41. 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 ) } ) ) } ] ] ] ) )
  42. WHAT WE DID NOT SAY Infinite undo Component mixins Targets

    Node.JS Targets JavascriptCore HTML templates Source maps REPLs
  43. 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