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

A Bird's Eye View of ClojureScript

A Bird's Eye View of ClojureScript

Birdwave is a way to visualize the amazing phenomenon of bird migration. Using ClojureScript and Om allowed the app to be written in a declarative, concise manner, taking full advantage of JavaScript interoperability, as well as Clojure's awesome built-in features such as persistent data structures and the async module. Also, prepare to be dazzled by the data-viz powerhouse that is D3.

Chandu Tennety

January 12, 2015
Tweet

Other Decks in Programming

Transcript

  1. A Bird's Eye View of ClojureScript Chandu Tennety { :

    g i t h u b " t e n n e t y " : t w i t t e r " t e n n e t y " } John Andrews { : g i t h u b " j x a " : t w i t t e r " x a n d r e w s " }
  2. Data for the App data set eBird Data for 1

    year for the US region 11 GB of tab-separated values Over 1700 species
  3. Data for the App The need for an API Safe,

    quick data import Too much data to load at once Dynamic nature of the app d3 handles JSON requests
  4. Clojure Syntax / * J a v a S c

    r i p t * / f u n c t i o n g r e e t ( w h o , e v e n t ) { r e t u r n " G r e e t i n g s " + w h o + " ! W e l c o m e t o " + e v e n t " ! " ; } g r e e t ( " G o o d P e o p l e " , " C o d e m a s h " ) ; ; ; C l o j u r e ( d e f n g r e e t [ w h o e v e n t ] ( s t r " G r e e t i n g s " w h o " ! W e l c o m e t o " e v e n t " ! " ) ) ( g r e e t " G o o d P e o p l e " " C o d e m a s h " )
  5. Parsing the Data ; ; C l o j u

    r e ( d e f f i e l d s [ : s i g h t i n g / g u i d ; ; " G L O B A L U N I Q U E I D E N T I F I E R " : t a x o n / o r d e r ; ; " T A X O N O M I C O R D E R " n i l ; ; " C A T E G O R Y " : t a x o n / c o m m o n - n a m e ; ; " C O M M O N N A M E " : t a x o n / s c i e n t i f i c - n a m e ; ; " S C I E N T I F I C N A M E " : t a x o n / s u b s p e c i e s - c o m m o n - n a m e ; ; " S U B S P E C I E S C O M M O N N A M E " : t a x o n / s u b s p e c i e s - s c i e n t i f i c - n a m e ; ; " S U B S P E C I E S S C I E N T I F I C N A M E " : s i g h t i n g / c o u n t ; ; " O B S E R V A T I O N C O U N T " ; ; x i n d i c a t e s u n c o u n t e d ; ; . . . ] )
  6. Parsing the Data ; ; C l o j u

    r e ( d e f n s i g h t i n g - s e q " R e t u r n a l a z y s e q u e n c e o f l i n e s f r o m f i l e n a m e , t r a n s f o r m e d i n t o s i g h t i n g m a p s " [ f i l e n a m e s k i p - r o w s n t h - r o w ] ( - > > ( i o / r e a d e r f i l e n a m e ) ( l i n e - s e q ) ( d r o p ( o r s k i p - r o w s 1 ) ) ( t a k e - n t h n t h - r o w ) ( m a p s i g h t i n g ) ) )
  7. Datomic Schema Transactional History preserving Query language is Clojure data

    Results are Clojure data structures Query is executed in application server
  8. Datomic ; ; C l o j u r e

    ( q ' [ : f i n d ( s u m ? c o u n t ) ( c o u n t ? e ) : w h e r e [ ? t : t a x o n / o r d e r " 2 8 8 1 " ] [ ? e : s i g h t i n g / t a x o n ? t ] [ ? e : s i g h t i n g / s t a t e " O h i o " ] [ ? e : s i g h t i n g / c o u n t y " S a n d u s k y " ] [ ? e : s i g h t i n g / c o u n t ? c o u n t ] ] ( d b c o n n ) ) ; ; = > [ [ 5 4 0 1 0 8 ] ]
  9. Displaying the Data Got ClojureScript? Immediate wins: Easy to integrate

    into the existing stack Same language on both client and server Interoperability with JavaScript
  10. What is ClojureScript? Compiler for Clojure to JavaScript Emits JS

    optimized for the Google Closure library Several benefits over vanilla JS Persistent data structures Object keys as opposed to only strings Laziness Macros Function argument destructuring
  11. ClojureScript and JavaScript Interop Methods / / J a v

    a S c r i p t v a r a c t i v e S t a t e = f u n c t i o n ( ) { r e t u r n d 3 . s e l e c t ( " . a c t i v e " ) ; } ; ; C l o j u r e S c r i p t ( d e f n a c t i v e - s t a t e [ ] ( . s e l e c t j s / d 3 " . a c t i v e " ) )
  12. ClojureScript and JavaScript Interop Properties / / J a v

    a S c r i p t v a r t a r g e t = f u n c t i o n ( ) { r e t u r n d 3 . e v e n t . t a r g e t ; } ; ; C l o j u r e S c r i p t ( d e f n t a r g e t [ ] ( . - t a r g e t ( . - e v e n t j s / d 3 ) ) ) ; ; O R ( d e f n t a r g e t [ ] ( . . j s / d 3 - e v e n t - t a r g e t )
  13. ClojureScript and JavaScript Interop Fluent APIs and the - >

    Macro / / J a v a S c r i p t v a r m o n t h s = d 3 . t i m e . s c a l e . d o m a i n ( [ n e w D a t e ( 2 0 1 2 , 1 0 , 1 5 ) , n e w D a t e ( 2 0 1 3 , 1 0 , 1 5 ) ] ) . r a n g e ( [ 0 , 9 0 0 ] ) ; ; C l o j u r e S c r i p t ( d e f m o n t h s ( - > ( j s / d 3 . t i m e . s c a l e ) ( . d o m a i n ( a r r a y ( j s / D a t e . 2 0 1 2 1 0 1 5 ) ( j s / D a t e . 2 0 1 3 1 0 1 5 ) ) ) ( . r a n g e ( a r r a y 0 9 0 0 ) ) ) )
  14. We Have a Prototype! But... Unpolished UI No structure to

    the data The database query was slow Differing views of end result Not responsive
  15. Initial Render Remember: Imagination Land ; ; C l o

    j u r e S c r i p t ( u l { : c l a s s N a m e " s p e c i e s - l i s t " } ( l i { : c l a s s N a m e " s p e c i e s " } ( a { : h r e f " # / t a x o n / 1 " } " A b e r t ' s T o w h e e " ) ) ( l i { : c l a s s N a m e " s p e c i e s " } ( a { : h r e f " # / t a x o n / 2 " } " A c a d i a n F l y c a t c h e r " ) ) ( l i { : c l a s s N a m e " s p e c i e s " } ( a { : h r e f " # / t a x o n / 3 " } " A c o r n W o o d p e c k e r " ) ) . . . )
  16. Building the List ; ; C l o j u

    r e S c r i p t ( d e f n s p e c i e s - l i [ s p e c i e s ] ( l i { : c l a s s N a m e " s p e c i e s " } ( a { : h r e f ( p a t h s p e c i e s ) } ( : c o m m o n - n a m e s p e c i e s ) ) ) ) ( m a p s p e c i e s - l i a l l - s p e c i e s )
  17. Interactivity ; ; C l o j u r e

    S c r i p t ( m a p s p e c i e s - l i ( f i l t e r ( m a t c h - s t r i n g " b l a c k - t h r " ) s p e c i e s ) )
  18. Putting It Together ; ; C l o j u

    r e S c r i p t ( d e f n f i l t e r - l i s t - i t e m s [ f i l t e r - t e x t s p e c i e s ] ( u l { : c l a s s N a m e " s p e c i e s - l i s t " } ( m a p s p e c i e s - l i ( f i l t e r ( m a t c h - s t r i n g f i l t e r - t e x t ) s p e c i e s ) ) ) )
  19. React: A Better DOM Render the dom you want React

    takes care of the details Keeps real DOM in sync with your ideal DOM
  20. Om Example ; ; C l o j u r

    e S c r i p t ( d e f n s p e c i e s - l i s t [ m o d e l o w n e r ] ( r e i f y o m / I R e n d e r S t a t e ( r e n d e r - s t a t e [ t h i s s t a t e ] ( a p p l y d o m / u l # j s { : c l a s s N a m e " s p e c i e s - l i s t " } ( o m / b u i l d - a l l s p e c i e s - i t e m ( : f i l t e r e d - l i s t s t a t e ) { : s t a t e ( s e l e c t - k e y s s t a t e [ : h i g h l i g h t e d : s e l e c t e d : s e l e c t - c h
  21. Handling Events In Om ; ; C l o j

    u r e S c r i p t ( d e f n s p e c i e s - i t e m [ m o d e l o w n e r ] ( r e i f y o m / I R e n d e r S t a t e ( r e n d e r - s t a t e [ _ { : k e y s [ h i g h l i g h t e d s e l e c t e d s e l e c t - c h ] } ] ( d o m / l i # j s { : c l a s s N a m e ( s t r " t a x o n " ( i f ( = m o d e l h i g h l i g h t e d ) " h i g h l i g h t e d " ) ( i f ( = m o d e l s e l e c t e d ) " a c t i v e " ) ) } ( d o m / a # j s { : h r e f ( t a x o n - p a t h ( : t a x o n / o r d e r m o d e l ) ) : o n C l i c k ( f n [ e ] ( . p r e v e n t D e f a u l t e ) ( p u t ! s e l e c t - c h m o d e l ) ) } ( d i s p l a y - n a m e m o d e l ) ) ) ) ) )
  22. Integrating With Flickr ; ; C l o j u

    r e S c r i p t ( n s b i r d - w a v e . f l i c k r ( : r e q u i r e [ c e m e r i c k . u r l : r e f e r ( u r l ) ] ) ) ( d e f a p i - b a s e - u r l ( u r l " h t t p s : / / a p i . f l i c k r . c o m / s e r v i c e s / r e s t / " ) ) ; ; # c e m e r i c k . u r l . U R L { : p r o t o c o l " h t t p s " , : u s e r n a m e n i l , : p a s s w o r d n i l , : h o s t " a p i . f l i c k r . c o ( s t r ( a s s o c a p i - b a s e - u r l : q u e r y { : a p i _ k e y " m y _ s u p e r _ f l i c k r _ k e y " } ) ) ; ; h t t p s : / / a p i . f l i c k r . c o m / s e r v i c e s / r e s t / ? a p i _ k e y = m y _ s u p e r _ f l i c k r _ k e y
  23. Integrating With Flickr Updating the Model ; ; C l

    o j u r e S c r i p t ( j s / d 3 . j s o n s e a r c h - u r l ( f n [ d a t a ] ( o m / u p d a t e ! m o d e l : p h o t o ( f i r s t - p h o t o d a t a ) ) ) ) ; ; { " p h o t o s " : { " p a g e " : 1 , " p a g e s " : " 2 2 3 " , " p e r p a g e " : 1 , " t o t a l " : " 2 2 3 " , ; ; " p h o t o " : [ ; ; { " i d " : " 4 7 6 9 6 9 0 1 3 3 " , " o w n e r " : " 3 1 0 6 4 7 0 2 @ N 0 5 " , " s e c r e t " : " 8 1 8 4 0 6 d 0 c d " , " s e r v e r " : " 4 ; ; ] } , " s t a t " : " o k " } ( j s / d 3 . j s o n u r l ( f n [ d a t a ] ( o m / u p d a t e ! m o d e l : a t t r i b u t i o n ( a t t r i b u t i o n d a t a ) ) ) ) ; ; { " p h o t o " : { " i d " : " 4 7 6 9 6 9 0 1 3 3 " , " s e c r e t " : " 8 1 8 4 0 6 d 0 c d " , " s e r v e r " : " 4 1 2 3 " , " f a r m " : 5 , ; ; " o w n e r " : { " n s i d " : " 3 1 0 6 4 7 0 2 @ N 0 5 " , " u s e r n a m e " : " D a w n H u c z e k " , " r e a l n a m e " : " " , " l o c a t i o ; ; . . . ; ; " u r l s " : { ; ; " u r l " : [ ; ; { " t y p e " : " p h o t o p a g e " , " _ c o n t e n t " : " h t t p s : \ / \ / w w w . f l i c k r . c o m \ / p h o t o s \ / 3 1 0 6 4 7 0 2 @ N 0 5 ; ; ] } , " m e d i a " : " p h o t o " } , " s t a t " : " o k " }
  24. Integrating With Flickr The Selection Image Component ; ; C

    l o j u r e S c r i p t ( d o m / d i v # j s { : i d " s e l e c t i o n - i m a g e " : c l a s s N a m e ( i f ( s e q m o d e l ) " l o a d e d " " n o - p h o t o " ) } ( d o m / i m g # j s { : c l a s s N a m e " p h o t o " : s r c ( t r y - w i t h - d e f a u l t m o d e l : u r l _ q " / i m a g e s / l o a d i n g . p n g " ) } ) ( d o m / d i v # j s { : c l a s s N a m e " a t t r i b u t i o n " } ( d o m / h 3 # j s { : c l a s s N a m e " t i t l e " } ( t r y - w i t h - d e f a u l t m o d e l : t i t l e " N o p h o t o a v a i l a b l e " ) ) ( d o m / d i v # j s { : c l a s s N a m e " b y " } … ( i f ( s e q ( : a t t r i b u t i o n m o d e l ) ) ( d o m / a # j s { : c l a s s N a m e " d e t a i l f e t c h e d " : h r e f ( g e t - i n m o d e l [ : a t t r i b u t i o n : u r l ] ) : t a r g e t " _ b l a n k " } ( g e t - i n m o d e l [ : a t t r i b u t i o n : b y ] ) ) ( d o m / a # j s { : c l a s s N a m e " d e t a i l " : h r e f " # " : o n C l i c k # ( f e t c h - a t t r i b u t i o n % m o d e l ) } " v i e w a t t r i b u t i o n " ) ) ) ) )
  25. This Is Way Better! But... Unpolished UI No structure to

    the data The database query was slow Differing views of end result Not responsive
  26. Adding Responsiveness Client changes Detect and store screen size in

    app state xs: 0px < width <= 520px sm: 520px < width <= 768px md: 768px < width <= 1024px lg: 1024px < width
  27. Adding Responsiveness ; ; C l o j u r

    e S c r i p t ( d e f n w a t c h - s c r e e n - s i z e [ m o d e l ] ( l e t [ s i z e - h a n d l e r ( f n [ s i z e ] # ( s w a p ! m o d e l a s s o c : s c r e e n - s i z e s i z e ) ) ] ( - > j s / e n q u i r e ( . r e g i s t e r " s c r e e n a n d ( m i n - w i d t h : 0 p x ) a n d ( m a x - w i d t h : 5 2 0 p x ) " ( s i z e - h a n d l e r " x ( . r e g i s t e r " s c r e e n a n d ( m i n - w i d t h : 5 2 1 p x ) a n d ( m a x - w i d t h : 7 6 8 p x ) " ( s i z e - h a n d l e r " s ( . r e g i s t e r " s c r e e n a n d ( m i n - w i d t h : 7 6 9 p x ) a n d ( m a x - w i d t h : 1 0 2 4 p x ) " ( s i z e - h a n d l e r " m ( . r e g i s t e r " s c r e e n a n d ( m i n - w i d t h : 1 0 2 5 p x ) " ( s i z e - h a n d l e r " l
  28. Adding Responsiveness Client changes Update necessary components to render accordingly

    Map renders states vs counties on md, sm, xs Photo does not render on md, sm, xs Slider changes to select widget on sm, xs Update ajax calls Map data API requests
  29. Adding Responsiveness ; ; C l o j u r

    e S c r i p t ; ; d i s p l a y t h e s l i d e r o n l a r g e s c r e e n s , a n d t h e s e l e c t w i d g e t o n s m a l l ( i f ( c o n t a i n s ? # { " l g " " m d " } ( : s c r e e n - s i z e m o d e l ) ) ( o m / b u i l d d a t e - s l i d e r m o d e l { : s t a t e { : t i m e - p e r i o d - c h t i m e - p e r i o d - c h } } ) ( o m / b u i l d d a t e - s e l e c t m o d e l { : s t a t e { : t i m e - p e r i o d - c h t i m e - p e r i o d - c h } } ) )
  30. Adding Responsiveness Server changes Handle query parameters on API requests

    Add queries to return data aggregated by state vs county
  31. Resources Birdwave Announcing Birdwave Choropleths and d3js Using the Flickr

    API with ClojureScript Adding an Om Component -- a Walkthrough Responsive JavaScript with EnquireJS
  32. Resources ClojureScript The ClojureScript Compilation Pipeline David Nolen on ClojureScript

    and Om David Nolen's seminal post on Om's approach Running ClojureScript with Node React.js Be Predictable, Not Correct (React.js)
  33. Resources d3 ... ... d3js Home page Scott Murray's tutorials

    and his book Mike Bostock's simple map example Building a Choropleth Play with GeoJSON