Slide 1

Slide 1 text

Dynamically Sassy Generating Dynamic CSS in Rails Jeremy Fairbank

Slide 2

Slide 2 text

What are we going to talk about?

Slide 3

Slide 3 text

Generate dynamic CSS

Slide 4

Slide 4 text

Write Sass functions in Ruby

Slide 5

Slide 5 text

Identify performance issues

Slide 6

Slide 6 text

Caching and background processing

Slide 7

Slide 7 text

Hi, I'm Jeremy ...or @elpapapollo

Slide 8

Slide 8 text

pushagency.io

Slide 9

Slide 9 text

simplybuilt.com

Slide 10

Slide 10 text

Our Problem

Slide 11

Slide 11 text

Implementation Issues

Slide 12

Slide 12 text

Same Sass style sheet for default and custom color palettes

Slide 13

Slide 13 text

Rendering custom color style sheets from a controller

Slide 14

Slide 14 text

Slow Sass render times

Slide 15

Slide 15 text

Caching generated CSS

Slide 16

Slide 16 text

Rendering in a worker

Slide 17

Slide 17 text

Syntactically Awesome Style Sheets sass-lang.com

Slide 18

Slide 18 text

/ / V a r i a b l e s $ f o n t - s i z e : 1 2 p x ; / / N e s t e d r u l e s # f o o { . b a r { f o n t - s i z e : $ f o n t - s i z e ; } } / / I n c l u d e o t h e r S a s s s t y l e s h e e t s @ i m p o r t ' b a r ' ; / / M i x i n s @ m i x i n h a s - c a t s { & : : b e f o r e { c o n t e n t : ' m e o w ' ; d i s p l a y : b l o c k ; } } # i n t e r n e t { @ i n c l u d e h a s - c a t s ; }

Slide 19

Slide 19 text

So What?

Slide 20

Slide 20 text

Modularity

Slide 21

Slide 21 text

DRY

Slide 22

Slide 22 text

Data Structures

Slide 23

Slide 23 text

Scripting

Slide 24

Slide 24 text

Color Functions

Slide 25

Slide 25 text

That's Sass

Slide 26

Slide 26 text

Reusing style sheets

Slide 27

Slide 27 text

It's all in the variables

Slide 28

Slide 28 text

Static Context

Slide 29

Slide 29 text

/ / _ m y - u i . s c s s # f o o { b a c k g r o u n d : n t h ( $ p a l e t t e , 1 ) ; c o l o r : n t h ( $ p a l e t t e , 2 ) ; } / / p a l e t t e 1 . s c s s $ p a l e t t e : ( r e d g r e e n ) ; @ i m p o r t ' m y - u i ' ; / / p a l e t t e 2 . s c s s $ p a l e t t e : ( b l u e y e l l o w ) ; @ i m p o r t ' m y - u i ' ;

Slide 30

Slide 30 text

Dynamic Context

Slide 31

Slide 31 text

Sass Functions

Slide 32

Slide 32 text

/ / d y n a m i c - p a l e t t e . s c s s $ p a l e t t e : g e t - d y n a m i c - p a l e t t e ( ) ; @ i m p o r t ' m y - u i ' ; # c o n f i g / i n i t i a l i z e r s / s a s s . r b m o d u l e S a s s : : S c r i p t : : F u n c t i o n s d e f g e t _ d y n a m i c _ p a l e t t e p a l e t t e = 2 . t i m e s . m a p d o c o l o r = ' # % 0 6 x ' % ( r a n d * 0 x f f f f f f ) S a s s : : S c r i p t : : V a l u e : : C o l o r . f r o m _ h e x ( c o l o r ) e n d S a s s : : S c r i p t : : V a l u e : : L i s t . n e w ( p a l e t t e , : s p a c e ) e n d e n d

Slide 33

Slide 33 text

Injecting data from a user?

Slide 34

Slide 34 text

/ / a p p / a s s e t s / s t y l e s h e e t s / d y n a m i c - p a l e t t e - r a i l s . c s s . s c s s $ p a l e t t e : g e t - d y n a m i c - p a l e t t e - f r o m - u s e r - s o m e h o w - m a g i c a l l y ( ) ; @ i m p o r t ' m y - u i ' ;

Slide 35

Slide 35 text

Use the asset pipeline?

Slide 36

Slide 36 text

"Static" context in the tilt template

Slide 37

Slide 37 text

# s a s s - r a i l s / l i b / s a s s / r a i l s / t e m p l a t e . r b m o d u l e S a s s m o d u l e R a i l s c l a s s S a s s T e m p l a t e < T i l t : : T e m p l a t e # . . . d e f e v a l u a t e ( c o n t e x t , l o c a l s , & b l o c k ) c a c h e _ s t o r e = C a c h e S t o r e . n e w ( c o n t e x t . e n v i r o n m e n t ) o p t i o n s = { : f i l e n a m e = > e v a l _ f i l e , : l i n e = > l i n e , : s y n t a x = > s y n t a x , : c a c h e _ s t o r e = > c a c h e _ s t o r e , : i m p o r t e r = > i m p o r t e r _ c l a s s . n e w ( c o n t e x t . p a t h n a m e . t o _ s ) , : l o a d _ p a t h s = > c o n t e x t . e n v i r o n m e n t . p a t h s . m a p { | p a t h | i m p o r t e r _ c l a s s . n e w ( p a t h . t o _ s ) } , : s p r o c k e t s = > { : c o n t e x t = > c o n t e x t , : e n v i r o n m e n t = > c o n t e x t . e n v i r o n m e n t } } s a s s _ c o n f i g = c o n t e x t . s a s s _ c o n f i g . m e r g e ( o p t i o n s ) e n g i n e = : : S a s s : : E n g i n e . n e w ( d a t a , s a s s _ c o n f i g ) c s s = e n g i n e . r e n d e r e n g i n e . d e p e n d e n c i e s . m a p d o | d e p e n d e n c y | c o n t e x t . d e p e n d _ o n ( d e p e n d e n c y . o p t i o n s [ : f i l e n a m e ] ) e n d c s s r e s c u e : : S a s s : : S y n t a x E r r o r = > e c o n t e x t . _ _ L I N E _ _ = e . s a s s _ b a c k t r a c e . f i r s t [ : l i n e ] r a i s e e e n d # . . . e n d e n d e n d

Slide 38

Slide 38 text

Oh, and assets are precompiled for production

Slide 39

Slide 39 text

We need our own render class for dynamic content

Slide 40

Slide 40 text

S a s s : : E n g i n e to the rescue

Slide 41

Slide 41 text

# l i b / s a s s _ c u s t o m _ p a l e t t e . r b c l a s s S a s s C u s t o m P a l e t t e T E M P L A T E = < < - E O S . f r e e z e $ p a l e t t e : g e t - c u s t o m - p a l e t t e ( ) ; @ i m p o r t ' m y - u i ' ; E O S d e f i n i t i a l i z e ( c o l o r ) @ c o l o r = c o l o r e n d d e f r e n d e r S a s s : : E n g i n e . n e w ( T E M P L A T E , s a s s _ c u s t o m _ o p t i o n s ) . r e n d e r e n d p r i v a t e d e f s a s s _ c u s t o m _ o p t i o n s { s y n t a x : : s c s s , s t y l e : : e x p a n d e d , c u s t o m : { c o l o r : @ c o l o r } } e n d e n d

Slide 42

Slide 42 text

# c o n f i g / i n i t i a l i z e r s / s a s s . r b m o d u l e S a s s : : S c r i p t : : F u n c t i o n s d e f g e t _ c u s t o m _ p a l e t t e c o l o r = S a s s : : S c r i p t : : V a l u e : : C o l o r . f r o m _ h e x ( o p t i o n s [ : c u s t o m ] [ : c o l o r ] ) f a c t o r = S a s s : : S c r i p t : : V a l u e : : N u m b e r . n e w ( 2 0 , ' % ' ) p a l e t t e = [ l i g h t e n ( c o l o r , f a c t o r ) , d a r k e n ( c o l o r , f a c t o r ) ] S a s s : : S c r i p t : : V a l u e : : L i s t . n e w ( p a l e t t e , : s p a c e ) e n d e n d

Slide 43

Slide 43 text

# a p p / c o n t r o l l e r s / p a l e t t e s _ c o n t r o l l e r . r b c l a s s P a l e t t e s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r d e f c u s t o m _ p a l e t t e c u s t o m _ r e n d e r e r = S a s s C u s t o m P a l e t t e . n e w ( p a r a m s [ : c u s t o m _ c o l o r ] ) @ c s s = c u s t o m _ r e n d e r e r . r e n d e r e n d e n d - # a p p / v i e w s / p a l e t t e s / c u s t o m _ p a l e t t e . c s s . h a m l = @ c s s . h t m l _ s a f e

Slide 44

Slide 44 text

Try it out...

Slide 45

Slide 45 text

S a s s : : S y n t a x E r r o r - F i l e t o i m p o r t n o t f o u n d o r u n r e a d a b l e : m y - u i . - _ _ -

Slide 46

Slide 46 text

There's always a load path

Slide 47

Slide 47 text

c l a s s S a s s C u s t o m P a l e t t e p r i v a t e d e f l o a d _ p a t h s r o o t = R a i l s . r o o t . j o i n ( ' a p p ' , ' a s s e t s ' , ' s t y l e s h e e t s ' ) D i r [ r o o t . j o i n ( ' i n c l u d e s ' ) ] e n d d e f s a s s _ c u s t o m _ o p t i o n s { s y n t a x : : s c s s , s t y l e : : e x p a n d e d , l o a d _ p a t h s : l o a d _ p a t h s , c u s t o m : { c o l o r : @ c o l o r } } e n d e n d / / a p p / a s s e t s / s t y l e s h e e t s / i n c l u d e s / _ m y - u i . s c s s # f o o { b a c k g r o u n d : n t h ( $ p a l e t t e , 1 ) ; c o l o r : n t h ( $ p a l e t t e , 2 ) ; }

Slide 48

Slide 48 text

Dynamic Sass Demo sassy-demos.jeremyfairbank.com/palettes

Slide 49

Slide 49 text

Implementing in SimplyBuilt

Slide 50

Slide 50 text

GET /custom_palette.css? custom_color=%23f00

Slide 51

Slide 51 text

...waiting ...waiting ...waiting

Slide 52

Slide 52 text

Done!

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

What's the problem?

Slide 55

Slide 55 text

Slow render times (1-1.5 seconds!)

Slide 56

Slide 56 text

Complex Sass rules

Slide 57

Slide 57 text

Many file dependencies

Slide 58

Slide 58 text

Loops

Slide 59

Slide 59 text

Data structure lookups

Slide 60

Slide 60 text

Compass functions

Slide 61

Slide 61 text

Can impact the whole server

Slide 62

Slide 62 text

Web Server

Slide 63

Slide 63 text

Web Server

Slide 64

Slide 64 text

Web Server

Slide 65

Slide 65 text

Web Server

Slide 66

Slide 66 text

Web Server

Slide 67

Slide 67 text

Yup, we need caching

Slide 68

Slide 68 text

Memcached / memcached.org github.com/mperham/dalli

Slide 69

Slide 69 text

# c o n f i g / e n v i r o n m e n t s / p r o d u c t i o n . r b c o n f i g . c a c h e _ s t o r e = : m e m _ c a c h e _ s t o r e , \ M E M _ C A C H E _ S E R V E R , M E M _ C A C H E _ O P T I O N S R a i l s . c a c h e . w r i t e ( ' f o o ' , ' b a r ' ) R a i l s . c a c h e . f e t c h ( ' f o o ' ) # = > ' b a r '

Slide 70

Slide 70 text

Introduce caching into our Sass rendering

Slide 71

Slide 71 text

c l a s s S a s s R e n d e r e r d e f i n i t i a l i z e ( t e m p l a t e , c a c h e _ k e y , o p t i o n s ) @ c a c h e _ k e y = c a c h e _ k e y @ e n g i n e = S a s s : : E n g i n e . n e w ( t e m p l a t e , o p t i o n s ) e n d d e f r e n d e r f r o m _ c a c h e { @ e n g i n e . r e n d e r } e n d d e f c a c h e d ? R a i l s . c a c h e . e x i s t ? ( @ c a c h e _ k e y ) e n d d e f g e t _ c a c h e d _ c s s R a i l s . c a c h e . f e t c h ( @ c a c h e _ k e y ) e n d p r i v a t e d e f s e t _ c a c h e d _ c s s ( c s s ) R a i l s . c a c h e . w r i t e ( @ c a c h e _ k e y , c s s ) e n d d e f f r o m _ c a c h e ( & w r i t e _ b l o c k ) g e t _ c a c h e d _ c s s | | w r i t e _ b l o c k . c a l l . t a p { | c s s | s e t _ c a c h e d _ c s s ( c s s ) } e n d e n d

Slide 72

Slide 72 text

Incorporate into S a s s C u s t o m P a l e t t e

Slide 73

Slide 73 text

c l a s s S a s s C u s t o m P a l e t t e d e f i n i t i a l i z e ( c o l o r ) @ c o l o r = c o l o r @ c a c h e _ k e y = " c u s t o m _ p a l e t t e / # { @ c o l o r } " @ e n g i n e = S a s s R e n d e r e r . n e w ( T E M P L A T E , @ c a c h e _ k e y , s a s s _ c u s t o m _ o p t i o n s ) e n d d e l e g a t e : r e n d e r , t o : : @ e n g i n e e n d

Slide 74

Slide 74 text

And?

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

What about the first render?

Slide 77

Slide 77 text

Process in the background

Slide 78

Slide 78 text

sidekiq.org

Slide 79

Slide 79 text

Why Bother?

Slide 80

Slide 80 text

Free up server thread quickly to handle new request

Slide 81

Slide 81 text

No thread concurrency guarantees with MRI

Slide 82

Slide 82 text

But...

Slide 83

Slide 83 text

Code complexity

Slide 84

Slide 84 text

Polling client

Slide 85

Slide 85 text

Websockets

Slide 86

Slide 86 text

Failing jobs

Slide 87

Slide 87 text

Waiting on an available worker

Slide 88

Slide 88 text

Network traffic

Slide 89

Slide 89 text

Setting up a Sidekiq worker

Slide 90

Slide 90 text

# a p p / w o r k e r s / s a s s _ c u s t o m _ p a l e t t e _ w o r k e r . r b c l a s s S a s s C u s t o m P a l e t t e W o r k e r i n c l u d e S i d e k i q : : W o r k e r d e f p e r f o r m ( c o l o r ) S a s s C u s t o m P a l e t t e . n e w ( c o l o r ) . r e n d e r e n d e n d c l a s s S a s s C u s t o m P a l e t t e d e f r e n d e r _ a s y n c u n l e s s @ e n g i n e . c a c h e d ? S a s s C u s t o m P a l e t t e W o r k e r . p e f o r m _ a s y n c ( @ c o l o r ) e n d @ c a c h e _ k e y e n d e n d

Slide 91

Slide 91 text

c l a s s S a s s R e n d e r e r d e f s e l f . g e t _ b y _ k e y ( k e y ) R a i l s . c a c h e . f e t c h ( k e y ) e n d d e f s e l f . c a c h e d ? ( k e y ) R a i l s . c a c h e . e x i s t ? ( k e y ) e n d p r i v a t e d e f g e t _ c a c h e d _ c s s s e l f . c l a s s . g e t _ b y _ k e y ( @ c a c h e _ k e y ) e n d e n d

Slide 92

Slide 92 text

What would polling look like?

Slide 93

Slide 93 text

Polling

Slide 94

Slide 94 text

Polling

Slide 95

Slide 95 text

Polling

Slide 96

Slide 96 text

Polling

Slide 97

Slide 97 text

Polling

Slide 98

Slide 98 text

Polling

Slide 99

Slide 99 text

Polling

Slide 100

Slide 100 text

Polling

Slide 101

Slide 101 text

c l a s s P a l e t t e s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r d e f r e q u e s t _ c u s t o m _ p a l e t t e c u s t o m _ r e n d e r e r = S a s s C u s t o m P a l e t t e . n e w ( p a r a m s [ : c u s t o m _ c o l o r ] ) @ k e y = c u s t o m _ r e n d e r e r . r e n d e r _ a s y n c e n d d e f c h e c k _ c u s t o m _ p a l e t t e @ r e a d y = S a s s R e n d e r e r . c a c h e d ? ( p a r a m s [ : k e y ] ) e n d d e f c u s t o m _ p a l e t t e @ c s s = S a s s R e n d e r e r . g e t _ b y _ k e y ( p a r a m s [ : k e y ] ) e n d e n d

Slide 102

Slide 102 text

Is this the best answer?

Slide 103

Slide 103 text

Can refactoring simplify our Sass?

Slide 104

Slide 104 text

YES!

Slide 105

Slide 105 text

Identified areas for code improvement

Slide 106

Slide 106 text

Acceptable render times

Slide 107

Slide 107 text

Eliminate need for background processing

Slide 108

Slide 108 text

Recap

Slide 109

Slide 109 text

Reuse Sass style sheets to render static and dynamic content

Slide 110

Slide 110 text

Sass rendering isn't always fast

Slide 111

Slide 111 text

Web server performance

Slide 112

Slide 112 text

Importance of caching

Slide 113

Slide 113 text

Background processing

Slide 114

Slide 114 text

Sometimes refactoring is all you need

Slide 115

Slide 115 text

So what does this mean?

Slide 116

Slide 116 text

Things aren't always simple

Slide 117

Slide 117 text

We always have to consider performance

Slide 118

Slide 118 text

Thanks! Slides: Demo Source: Blog: sassy-talk.jeremyfairbank.com github.com/jfairbank/dynamically-sassy-demos blog.jeremyfairbank.com [email protected] @elpapapollo github.com/jfairbank