Permalink
Browse files

refactor: turns out that the whole server interaction couldnt possibl…

…y work because it didnt take into account concurrent connections. so, lots of changes, good thing it was caught before release
  • Loading branch information...
1 parent e7ee1be commit 87fdfa82d0da7583a5473c57b4d1b1f6599019b1 @Zolmeister Zolmeister committed May 8, 2015
Showing with 396 additions and 714 deletions.
  1. +1 −2 package.json
  2. +0 −48 src/cookies.coffee
  3. +44 −0 src/render_to_string.coffee
  4. +55 −134 src/server.coffee
  5. +18 −29 src/state_factory.coffee
  6. +19 −2 src/z.coffee
  7. +2 −1 src/zorium.coffee
  8. +118 −285 test/zorium.coffee
  9. +139 −213 test/zorium_server.coffee
View
@@ -37,16 +37,15 @@
"merge-stream": "^0.1.7",
"mocha": "^2.2.4",
"promiz": "^1.0.3",
+ "qs": "^2.4.1",
"rewire": "^2.3.1",
"rewire-webpack": "^1.0.0",
"transform-loader": "^0.2.1",
"webpack": "^1.8.4"
},
"dependencies": {
"coffee-script": "^1.9.2",
- "cookie": "^0.1.2",
"lodash": "^3.6.0",
- "qs": "^2.4.1",
"routes": "^2.0.0",
"rx-lite": "^2.5.2",
"vdom-to-html": "^2.0.0",
View
@@ -1,48 +0,0 @@
-_ = require 'lodash'
-cookie = require 'cookie'
-Rx = require 'rx-lite'
-
-class Cookies
- constructor: ->
- @cookieSubjects = {}
- @cookieConstructors = {}
-
- # Avoid triggering the cookieConstructor
- # Important because the {opts} for cookies are no longer accessible
- populate: (cookies) =>
- cookies = cookie.parse cookies or ''
- _.forEach cookies, (val, key) =>
- @cookieSubjects[key] = new Rx.BehaviorSubject(val)
-
- getConstructors: => @cookieConstructors
-
- reset: =>
- @cookieSubjects = {}
- @cookieConstructors = {}
-
- set: (key, value, opts) =>
- if @cookieSubjects[key]
- @cookieSubjects[key].onNext value
- else
- @cookieSubjects[key] = new Rx.BehaviorSubject(value)
-
- @cookieConstructors[key] = {
- value
- opts
- }
- if window?
- document.cookie = cookie.serialize key, value, opts
-
- get: (key) =>
- if @cookieSubjects[key]
- return @cookieSubjects[key]
- else
- value = if window?
- cookie.parse(document.cookie or '')?[key] or null
- else
- null
-
- @cookieSubjects[key] = new Rx.BehaviorSubject(value)
- return @cookieSubjects[key]
-
-module.exports = new Cookies()
@@ -0,0 +1,44 @@
+_ = require 'lodash'
+Rx = require 'rx-lite'
+toHTML = require 'vdom-to-html'
+
+z = require './z'
+StateFactory = require './state_factory'
+
+Promise = if window?
+ window.Promise
+else
+ _promiz = 'promiz'
+ require _promiz
+
+module.exports = (tree) ->
+ new Promise (resolve) ->
+ # for unbinding
+ allStates = []
+ states = []
+ disposables = []
+
+ listener = ->
+ z._startRecordingStates()
+ z tree
+ states = z._getRecordedStates()
+ allStates = allStates.concat states
+ z._stopRecordingStates()
+ _.map states, (state) ->
+ unless state._isSubscribing()
+ state._bind_subscriptions()
+ disposables.push state.subscribe listener
+ setTimeout ->
+ finish()
+
+ finish = ->
+ isDone = _.every states, (state) ->
+ state._isFulfilled()
+
+ if isDone
+ result = z tree
+ _.map disposables, (disposable) -> disposable.dispose()
+ _.map allStates, (state) -> state._unbind_subscriptions()
+ resolve toHTML result
+
+ listener()
View
@@ -1,15 +1,11 @@
_ = require 'lodash'
-toHTML = require 'vdom-to-html'
z = require './z'
render = require './render'
StateFactory = require './state_factory'
-cookies = require './cookies'
isSimpleClick = require './is_simple_click'
ev = require './ev'
-SERVER_TIMEOUT_MS = 250
-
getCurrentPath = (mode) ->
hash = window.location.hash.slice(1)
pathname = window.location.pathname
@@ -31,16 +27,6 @@ setPath = (path, mode, isReplacement) ->
class Server
constructor: ->
- @events = {}
- @$$root = null
- @mode = if window?.history?.pushState then 'pathname' else 'hash'
- @currentPath = null
- @isRedrawScheduled = false
- @animationRequestId = null
- @$root = null
- @status = null # server only
- @req = null # server only
-
# coffeelint: disable=missing_fat_arrows
@Redirect = ({path}) ->
@name = 'redirect'
@@ -50,106 +36,42 @@ class Server
@Redirect.prototype = new Error()
# coffeelint: enable=missing_fat_arrows
- if window?
- StateFactory.onAnyUpdate =>
- if @$root
- @go @currentPath
-
- # used for full-page rendering
- @globalRoot = document.getElementById 'zorium-root'
-
- unless @globalRoot
- @globalRoot = document.createElement 'div'
- @globalRoot.id = 'zorium-root'
- document.body.appendChild @globalRoot
-
- # some browsers erroneously call popstate on intial page load (iOS Safari)
- # We need to ignore that first event.
- # https://code.google.com/p/chromium/issues/detail?id=63040
- window.addEventListener 'popstate', (e) =>
- if @currentPath
- setTimeout @go
-
- setStatus: (@status) =>
- if window?
- throw new Error 'z.server.setStatus() called client-side'
- null
-
- setCookie: cookies.set
- getCookie: cookies.get
-
- getReq: =>
- if window?
- throw new Error 'z.server.getReq() called client-side'
- @req
-
- factoryToMiddleware: (factory) =>
- handleRenderError = (err, req, res, next) =>
- if err instanceof @Redirect
- return res.redirect err.path
- else
- return next err
-
- setResCookies = (res, cookies) ->
- _.map cookies.getConstructors(), (config, key) ->
- res.cookie key, config.value, config.opts
-
- (req, res, next) =>
- # Reset state between requests
- @setStatus 200
- @req = req
- cookies.reset()
- StateFactory.reset()
- hasResolved = false
-
- StateFactory.onError (err) ->
- if _.isPlainObject err
- err = new Error JSON.stringify err
- next err
-
- cookies.populate req.headers?.cookie
-
- $root = factory()
-
- # FIXME
- # timeout = setTimeout =>
- # @emit 'timeout', {req}
- # resolve()
- # , SERVER_TIMEOUT_MSgd
-
- resolve = =>
- if hasResolved
- return
- hasResolved = true
- # FIXME
- # clearTimeout timeout
- try
- tree = z $root, {
- path: req.url
- }
-
- setResCookies(res, cookies)
- res.status(@status).send '<!DOCTYPE html>' + toHTML tree
- catch err
- setResCookies(res, cookies)
- handleRenderError(err, req, res, next)
-
- # Initialize tree, kicking off async fetches
- try
- z $root, {
- path: req.url
- }
+ unless window?
+ return
- StateFactory.onNextAllSettlemenmt resolve
+ @events = {}
+ @$$root = null
+ @mode = if window?.history?.pushState then 'pathname' else 'hash'
+ @currentPath = null
+ @isRedrawScheduled = false
+ @animationRequestId = null
+ @$root = null
- catch err
- hasResolved = true
- setResCookies(res, cookies)
- handleRenderError(err, req, res, next)
+ StateFactory.onAnyUpdate =>
+ if @$root
+ @go @currentPath
+
+ # used for full-page rendering
+ @globalRoot = document.getElementById 'zorium-root'
+
+ unless @globalRoot
+ @globalRoot = document.createElement 'div'
+ @globalRoot.id = 'zorium-root'
+ document.body.appendChild @globalRoot
+
+ # some browsers erroneously call popstate on intial page load (iOS Safari)
+ # We need to ignore that first event.
+ # https://code.google.com/p/chromium/issues/detail?id=63040
+ window.addEventListener 'popstate', (e) =>
+ if @currentPath
+ setTimeout @go
+
+ config: ({mode, $root, $$root}) =>
+ unless window?
+ throw new Error 'config called server-side'
- config: ({mode, factory, $$root}) =>
@mode = mode or @mode
- @$root = factory?() or @$root
+ @$root = $root or @$root
@$$root = $$root or @$$root
link: (node) =>
@@ -165,23 +87,6 @@ class Server
return node
- render: (props) =>
- try
- tree = z @$root, props
- catch err
- if err instanceof @Redirect
- return @go err.path
- else throw err
-
- # Because the DOM doesn't let us directly manipulate top-level elements
- # We have to standardize a hack around it
-
- $root = if @$$root is document \
- then @globalRoot \
- else @$$root
-
- render $root, tree
-
go: (path) =>
unless window?
throw new Error 'z.server.go() called server-side'
@@ -200,18 +105,36 @@ class Server
path: path
}
+ renderOrRedirect = (props) =>
+ try
+ tree = z @$root, props
+ catch err
+ if err instanceof @Redirect
+ return @go err.path
+ else throw err
+
+ # Because the DOM doesn't let us directly manipulate top-level elements
+ # We have to standardize a hack around it
+ $root = if @$$root is document \
+ then @globalRoot \
+ else @$$root
+ render $root, tree
+
if not isRedraw
@currentPath = path
setPath path, @mode, hasRouted
@emit 'go', {path}
- @render(props)
+ renderOrRedirect(props)
else
@isRedrawScheduled = true
@animationRequestId = window.requestAnimationFrame =>
@isRedrawScheduled = false
- @render(props)
+ renderOrRedirect(props)
on: (name, fn) =>
+ unless window?
+ throw new Error 'z.server.on() called server-side'
+
(@events[name] = @events[name] or []).push(fn)
emit: (name) =>
@@ -220,6 +143,9 @@ class Server
fn.apply null, args
off: (name, fn) =>
+ unless window?
+ throw new Error 'z.server.off() called server-side'
+
@events[name] = _.without(@events[name], fn)
server = new Server()
@@ -229,10 +155,5 @@ module.exports = {
go: server.go
link: server.link
config: server.config
- setStatus: server.setStatus
- setCookie: server.setCookie
- getCookie: server.getCookie
- getReq: server.getReq
- factoryToMiddleware: server.factoryToMiddleware
Redirect: server.Redirect
}
Oops, something went wrong.

0 comments on commit 87fdfa8

Please sign in to comment.