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

Reusing your frontend JS on the server with V8/Rhino

Reusing your frontend JS on the server with V8/Rhino

Writing modern web applications requires a ton of JS, and somewhere in that JS lies some application logic (we're not just talking DOM manipulations here). If you require that same logic on the server-side for say, generating reports, what do you do? I'll show you how ValuationUP.com pushes the single responsibility principle to the max by "embedding" V8 into our report generation code so the same JS that powers our Backbone.js frontend powers our PDF's generated by Prawn.

Thin wrappers, no duplication, practical IoC, ultimate SRP.

Video - http://www.youtube.com/watch?v=pDgtVhN8Ecw

Kenneth Kalmer

February 08, 2013
Tweet

More Decks by Kenneth Kalmer

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. Panic! Let’s offer a PDF with all the information A

    PDF... ...WITH ALL THE INFORMATION
  4. 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()
  5. 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() )
  6. 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( "<h1>#{wacc}</h1>" ) 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()
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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