Saturday, October 4, 2014

Hosting an ember-cli app inside a ServiceStack (or any) MVC app

The resulting code for this post can be found at: https://github.com/wayne-o/bower-link-test

We are looking into refactoring sonatribe to be an ember-cli SPA. Using the Discourse code base as inspiration I've been spiking various parts to see exactly how feasible this would be.

This all started with us wanting to mimic the discourse header (ignoring the messy sub header):



As you can see the header is a really nice, dock-able UI and has a lot of features. My initial thoughts after looking at the code to isolate the header were that this would be near on impossible. We wanted to do this because we want the transition from sonatribe to our discourse installation to be seamless - it's important that it feels like one app. Ignoring the obvious integration points outside of UI (things like updating sonatribe or discourse when the user receives a PM - this will come later hopefully in the form of plugins). I'd never used Ember or Ruby before and so diving into the Discourse source code was a little daunting!

Reverse engineering the code I managed to stub a lot of the code for things like preload store and bound-avatar and basically get a functioning version of the header isolated into it's own ember-cli project:

Not bad! Wiring this up to our own search API I actually managed to get the grouped search results working. You can see this working here:



The search API is a really poor knocked-together ServiceStack API which has zero optimizations so ignore the poor perf on the results!

All well and good but I was still stubbing the preload store. This is where Discourse have done the clever thing of loading the app with all of the data it needs to render itself on boot. Looking at the Discourse HTML you can see there is a lot of JS/JSON injected in. The problem here is that ember-cli doesn't really work this way. Ember-cli gives you the folder structure to develop in and uses a filesystem watcher to recompile your code (ES6, SCSS etc) into a distributable form. It even serves the index.html inside a HTTP server for you.

Discourse works this way because the SPA knows you're logged in through the presence of a user object injected into the preload store. They use omniauth which loads a popped browser window to handle the auth mechanism - once the user has authenticated it calls into a hook (window.opener.Discourse.authenticationComplete) to notify discourse that authentication has completed and sends the result. If the user successfully authenticated the browser is reloaded and because the user is authenticated the users JSON is loaded into the preload store.

So, serving the index.html as a dynamic (is that still the right term? It sounds so ASP 3.0!!!) page is pretty much a must have.

To do this we are going to need to drop the HTTP server provided by ember-cli and work out a way to push the /dist folder to another folder which can be part of a hosted project. I'm a big fan of ServiceStack but the hosted project could literally be any language/framework.

The idea here is to have 2 projects:

  1. ember-cli app where we can develop the JS and CSS
  2. an integration project where we can host index.html and inject session state and preloaded data
To keep the integration project up-to-date when we make changes in the ember-cli project we can use Bower link (http://bower.io/docs/api/#link) - this will replicate changes to the integration project live using symlinks. You can link the /dist folder to the target project - you can see this here: https://github.com/wayne-o/bower-link-test/blob/master/BowerLinked/BowerLinked/bower_components/assets

Rebuilding the project on changes can be done using the ember build --watch command. 

What this will do is - every time you edit some code in the ember-cli project - ember will notice the files have changed and re-build the code - it then copies the resulting build to the /dist folder. Bower's symlink will then copy the code to the integration project. 

We can then host our own contents of index.html as a view inside our hosted environment: https://github.com/wayne-o/bower-link-test/blob/master/BowerLinked/BowerLinked/Views/Home/Index.cshtml 

So now we get the mixture of serverside code inside the ember-cli index.html and we can inject in session state and preloaded data!

This means I can go back to my header spike project and replace all of the stubbed code with real user data and start to really figure out the auth aspects of this refactoring.

While this is a ridiculously simple example - the project in github does show this all working:





The "this is a test" lines are coming from the cshtml and the ember app is booted below it! All I need to do now is get the authed user from ServiceStack and serialize whatever data we need in the UI into the preload store.

w://