After change log rendering method, to append HTML elements instead of
rerendering the entire thing, the case of gradual rendering of a log
started to behave much better (because we just append new elements), but
the initial render took a bit longer, because of the fact that appending
large separate HTML elements to DOM at once is not a good idea.
In order to make the situation better I added simple optimization.
Elements are added to DocumentFragment node before inserting to DOM and
appended to DOM only after all elements are processed. That way, when
log needs to be rendered all at once, we will not do any DOM operations
until log is ready.
Till now, log viewer was rendered in handlebars, which was the simplest
solution, but it had a major drawback - every append to log caused it to
rerender which was not efficient and memory consuming.
The new approach is to make Travis.Log interpret the log and send lines
with instructions to the view, so for example if view should add a line,
it gets something like:
{ number: 1, content: '$ bundle install' }
Such approach is required to handle cases where data coming from pusher
is not actually a new line. For example output containing dots from
tests needs to be appended:
$ rake
....
Such output could be sent to client in 2 chunks: "$ rake\n.." and "..".
In such situation we would need to send 3 instructions:
{ number: 1, content: '$ rake' }
{ number: 2, content: '..' }
{ number: 2, content: '..', append: true }
The third instruction can come much later, because tests can take a
while to run, so we can't assume that each line will come in one piece.
The other scenario is \r, for example when showing progress:
\rDownloading: 10%
\rDownloading: 50%
\rDownloading: 100%
Such input should be changed into such instructions:
{ number: 1, content: 'Downloading: 10%' }
{ number: 1, content: 'Downloading: 50%', replace: true }
{ number: 1, content: 'Downloading: 100%', replace: true }
Travis.Log also supports folds, for example on bundle install, the code
was rewritten to make folds management simpler.
If we keep it only on states lower than root and afterSignIn is sent
when app is still in root.loading (which is often the case as we need to
wait for repository deserialization), it will try to find afterSignIn on
root.
When we get payload from pusher, we usually don't send the entire
record. Initially such records where fetched from server right away to
get missing data. This was done becuase Ember can't tell if given data
is complete or not and just assumes that the record is loaded.
To not fire unneeded request, this code sets incomplete flag on records
loaded from pusher and loads the rest of the data only if needed.