Slide 1

Slide 1 text

Reusing your frontend JS on the server with V8/Rhino Pushing SRP & DI/IoC to whole new level

Slide 2

Slide 2 text

Kenneth Kalmer Chief Rocket Scientist @ ValuationUP.com @kennethkalmer github/kennethkalmer www.opensourcery.co.za kennethkalmer

Slide 3

Slide 3 text

ValuationUP.com Help entrepreneurs make REAL sense of their management accounts Comparative analysis within industry Refactoring financials by getting from red to green Take actions to increase their valuations Valuation is the ultimate metric

Slide 4

Slide 4 text

The challenge We need a very responsive UI Realtime updates as the user inputs information The RTT to the server is not worth the wait for such a simple formula

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Done! Repeat that for 50+ financial models Build fully buzzword compliant frontend

Slide 7

Slide 7 text

Panic! Let’s offer a PDF with all the information A PDF... ...WITH ALL THE INFORMATION

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

We did it! But how?

Slide 10

Slide 10 text

Dilemma Duplicate? Reuse? wkhtmltopdf? (a.k.a. wtftopdf)

Slide 11

Slide 11 text

Reuse! Single Responsibility Principle Dependency Injection/Inversion on Control Use as much JS on the server as possible, where possible!

Slide 12

Slide 12 text

By using therubyracer (github.com/cowboyd/rubyracer) therubyrhino (github.com/cowboyd/rubyrhino) coffee-script 1 gem 'therubyracer', platforms: :ruby 2 gem 'therubyrhino', platforms: :jruby 3 4 gem 'coffee-script'

Slide 13

Slide 13 text

That’s WACC!

Slide 14

Slide 14 text

With coffee 1 # Determine the WACC of the business (weighted average cost of capital) 2 class Demo.WACC 3 4 constructor: (statement) -> 5 @statement = statement 6 7 @cost_of_equity = statement.cost_of_equity 8 unless _.isFinite( @cost_of_equity ) 9 @cost_of_equity = 0.30 10 11 calculate: -> 12 13 e = ( @statement.equity / ( @statement.debt + @statement.equity ) ) * @cost_of_equity 14 d = ( @statement.debt / ( @statement.debt + @statement.equity ) ) * @statement.costOfDebt 15 t = ( 1 - @statement.taxRate ) 16 17 e + d * t 18 19 # Return the calculated WACC 20 value: -> 21 @wacc or= @calculate()

Slide 15

Slide 15 text

Present it 1 class Demo.WaccLayout 2 3 # The view that will render our information 4 view: null 5 6 initialize: (data, view = null) -> 7 @data = data 8 @view = view 9 10 render: -> 11 wacc = new Demo.WACC( @data.statement ) 12 13 @view.render( wacc: wacc.value() )

Slide 16

Slide 16 text

Client-side view & usage 1 # Simple backbone view 2 class Demo.WaccView extends Backbone.View 3 4 render: (options) -> 5 wacc = options.wacc 6 7 @$el.empty().append( "

#{wacc}

" ) 8 9 10 11 12 13 # Initialize a view 14 view = new Demo.WaccView( el: '#wacc' ) 15 16 # Load and render our layout 17 layout = new Demo.WaccLayout( view: view, data: data ) 18 layout.render()

Slide 17

Slide 17 text

Reuse on the server...

Slide 18

Slide 18 text

JavaScript Context 1 require 'v8' 2 # require 'rhino' 3 4 # This service wraps a JS context and allows us to interact with our Javascript 5 # directly in Ruby. 6 class Javascript 7 8 # Our V8 context object 9 attr_reader :context 10 11 # Some delegations 12 delegate :load, :eval, :[], :[]=, to: :context 13 14 def initialize() 15 @context = V8::Context.new 16 #@context = Rhino::Context.new 17 18 # Setup a fake 'window' object 19 @context['window'] = FakeWindow.new( @context ) 20 @context['console'] = FakeConsole.new 21 22 # Load our global setup 23 load_coffee Rails.root.join( 'app/assets/javascripts/setup.js.coffee' ) 24 end 25 26 # truncated, lots more detracting stuff down below... 27 28 end

Slide 19

Slide 19 text

CoffeeScript sprinkles 1 # Compile and load a CoffeeScript file into the current context 2 def load_coffee( path ) 3 compiled_coffeescript = CoffeeScript.compile( File.read( path ) ) 4 context.eval compiled_coffeescript 5 end

Slide 20

Slide 20 text

Pause We have a JavaScript context (V8) at our finger tips Have some basic CoffeeScript loading abilities And some additional plumbing Far from a DOM

Slide 21

Slide 21 text

Server-side wrapper 1 class WaccLayout 2 3 def initialize( view, context = Javascript.new ) 4 @context = context 5 6 load_dependencies 7 8 # Pass our Ruby view into the context 9 @context["view"] = view 10 11 # Setup 12 @context.eval <<-EOJS 13 var layout = new Demo.WaccLayout({ 14 view: view, data: data 15 }); 16 EOJS 17 end 18 19 def render 20 context['layout']['render'].methodcall( context['layout'] ) 21 end 22 23 def load_dependencies 24 @context.load_coffee Rails.root.join("app/assets/javascripts/wacc.js.coffee") 25 @context.load_coffee Rails.root.join("app/assets/javascripts/wacc_layout.js.coffee") 26 end 27 end

Slide 22

Slide 22 text

Server-side view & usage 1 class WaccPdf 2 3 def initialize 4 @pdf = Prawn::Document.new 5 end 6 7 def render( options ) 8 wacc = options.wacc 9 10 @pdf.text "WACC: #{wacc}" 11 end 12 13 end 14 15 16 # Setup a view 17 view = WaccPdf.new 18 19 # Setup a layout and render 20 layout = WaccLayout.new( view ) 21 layout.render

Slide 23

Slide 23 text

1,000 ft view

Slide 24

Slide 24 text

Reminder why we did it?

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Yes please! https://github.com/kennethkalmer/rubyfuza-2013 https://rubyfuza-2013-wacc.herokuapp.com Later this weekend, you’re all getting drunk tonight anyway!!

Slide 27

Slide 27 text

Thank you! It’s been wild!