Docs: Added sequence diagrams for web interface lifecycle
For now only startup and reconnect to server.
This commit is contained in:
parent
ed6c01c12a
commit
5fc194accd
4 changed files with 213 additions and 30 deletions
|
|
@ -33,7 +33,7 @@ needs_sphinx = '1.3'
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['codeblockext', 'onlineinclude', 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'sphinxcontrib.httpdomain',
|
extensions = ['codeblockext', 'onlineinclude', 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'sphinxcontrib.httpdomain',
|
||||||
'sphinx.ext.napoleon']
|
'sphinx.ext.napoleon', 'sphinxcontrib.mermaid']
|
||||||
todo_include_todos = True
|
todo_include_todos = True
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,130 @@ on your view model, taking a list of all bound view models:
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.. _sec-plugins-viewmodels-livecycle:
|
||||||
|
|
||||||
|
Lifecycle diagrams
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. _sec-plugins-viewmodels-startup:
|
||||||
|
|
||||||
|
Web interface startup
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. mermaid::
|
||||||
|
|
||||||
|
sequenceDiagram
|
||||||
|
participant Main
|
||||||
|
participant onServerConnect
|
||||||
|
participant fetchSettings
|
||||||
|
participant bindViewModels
|
||||||
|
participant DataUpdater
|
||||||
|
participant LoginStateViewModel
|
||||||
|
|
||||||
|
Note right of DataUpdater: connectCallback = undefined
|
||||||
|
|
||||||
|
activate Main
|
||||||
|
|
||||||
|
Main->>+DataUpdater: connect
|
||||||
|
Note right of DataUpdater: initialized = false
|
||||||
|
DataUpdater-->>Main: ok
|
||||||
|
deactivate Main
|
||||||
|
DataUpdater->>DataUpdater: asynchronous connect to server...
|
||||||
|
activate DataUpdater
|
||||||
|
Note right of DataUpdater: store any callbacks instead of triggering (e.g. onServerConnect, fromHistoryData, fromCurrentData, ...)
|
||||||
|
DataUpdater-X+Main: done
|
||||||
|
deactivate DataUpdater
|
||||||
|
deactivate DataUpdater
|
||||||
|
|
||||||
|
Main->>+DataUpdater: connectCallback = onServerConnect
|
||||||
|
Note right of DataUpdater: connectCallback = onServerConnect
|
||||||
|
DataUpdater-->>-Main: ok
|
||||||
|
Main->>+onServerConnect: call
|
||||||
|
onServerConnect->>+LoginStateViewModel: passiveLogin
|
||||||
|
LoginStateViewModel-->>onServerConnect: ok
|
||||||
|
onServerConnect-->>Main: ok
|
||||||
|
deactivate onServerConnect
|
||||||
|
deactivate Main
|
||||||
|
|
||||||
|
LoginStateViewModel->>+LoginStateViewModel: asynchronous passive login
|
||||||
|
Note over Main,LoginStateViewModel: Session available!
|
||||||
|
LoginStateViewModel-X+onServerConnect: done
|
||||||
|
deactivate LoginStateViewModel
|
||||||
|
deactivate LoginStateViewModel
|
||||||
|
|
||||||
|
onServerConnect->>+DataUpdater: initialized
|
||||||
|
Note right of DataUpdater: initialized = true
|
||||||
|
DataUpdater->DataUpdater: trigger stored callbacks
|
||||||
|
DataUpdater-->>-onServerConnect: ok
|
||||||
|
onServerConnect-X+Main: done
|
||||||
|
deactivate onServerConnect
|
||||||
|
|
||||||
|
Main->>+fetchSettings: call
|
||||||
|
Note right of fetchSettings: trigger onStartup
|
||||||
|
|
||||||
|
fetchSettings-->>Main: ok
|
||||||
|
deactivate Main
|
||||||
|
|
||||||
|
fetchSettings->>+fetchSettings: asynchronous settings fetch
|
||||||
|
fetchSettings->>+bindViewModels: call
|
||||||
|
|
||||||
|
loop for each view model
|
||||||
|
bindViewModels->bindViewModels: trigger onBeforeBinding
|
||||||
|
bindViewModels->bindViewModels: trigger onBoundTo
|
||||||
|
bindViewModels->bindViewModels: trigger onAfterBinding
|
||||||
|
end
|
||||||
|
|
||||||
|
bindViewModels->bindViewModels: trigger onAllBound
|
||||||
|
opt User is logged in
|
||||||
|
bindViewModels->>+LoginStateViewModel: onAllBound
|
||||||
|
LoginStateViewModel->LoginStateViewModel: trigger onUserLoggedIn
|
||||||
|
LoginStateViewModel-->>-bindViewModels: ok
|
||||||
|
end
|
||||||
|
bindViewModels->bindViewModels: trigger onStartupComplete
|
||||||
|
bindViewModels-->>-fetchSettings: ok
|
||||||
|
|
||||||
|
deactivate fetchSettings
|
||||||
|
deactivate fetchSettings
|
||||||
|
|
||||||
|
|
||||||
|
.. _sec-plugins-viewmodels-reconnect:
|
||||||
|
|
||||||
|
Web interface reconnect
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. mermaid::
|
||||||
|
|
||||||
|
sequenceDiagram
|
||||||
|
participant onServerConnect
|
||||||
|
participant DataUpdater
|
||||||
|
participant LoginStateViewModel
|
||||||
|
|
||||||
|
activate DataUpdater
|
||||||
|
DataUpdater->>DataUpdater: call connectCallback
|
||||||
|
DataUpdater->>+onServerConnect: call
|
||||||
|
onServerConnect-->>DataUpdater: ok
|
||||||
|
deactivate DataUpdater
|
||||||
|
|
||||||
|
onServerConnect->>+LoginStateViewModel: passiveLogin
|
||||||
|
LoginStateViewModel-->>onServerConnect: ok
|
||||||
|
deactivate onServerConnect
|
||||||
|
LoginStateViewModel->>+LoginStateViewModel: asynchronous passive login
|
||||||
|
Note over onServerConnect,LoginStateViewModel: Session available!
|
||||||
|
opt User is logged in
|
||||||
|
LoginStateViewModel->LoginStateViewModel: trigger onUserLoggedIn
|
||||||
|
end
|
||||||
|
|
||||||
|
activate onServerConnect
|
||||||
|
LoginStateViewModel-XonServerConnect: done
|
||||||
|
deactivate LoginStateViewModel
|
||||||
|
deactivate LoginStateViewModel
|
||||||
|
|
||||||
|
onServerConnect->>+DataUpdater: initialized
|
||||||
|
DataUpdater->DataUpdater: trigger stored callbacks
|
||||||
|
DataUpdater-->>onServerConnect: ok
|
||||||
|
deactivate DataUpdater
|
||||||
|
deactivate onServerConnect
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
`OctoPrint's core viewmodels <https://github.com/foosel/OctoPrint/tree/devel/src/octoprint/static/js/app/viewmodels>`_
|
`OctoPrint's core viewmodels <https://github.com/foosel/OctoPrint/tree/devel/src/octoprint/static/js/app/viewmodels>`_
|
||||||
|
|
|
||||||
|
|
@ -7,41 +7,99 @@ __copyright__ = "Copyright (C) 2015 Gina Häußge - Released under terms of the
|
||||||
|
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import urllib2
|
import requests
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
from sphinx.directives import LiteralInclude, dedent_lines
|
from sphinx.directives.code import LiteralIncludeReader, LiteralInclude, dedent_lines, \
|
||||||
|
nodes, set_source_info, parselinenos, logger, container_wrapper
|
||||||
|
|
||||||
|
if False:
|
||||||
|
# For type annotation
|
||||||
|
from typing import Any, List # NOQA
|
||||||
|
|
||||||
cache = dict()
|
cache = dict()
|
||||||
|
|
||||||
|
class OnlineIncludeReader(LiteralIncludeReader):
|
||||||
|
|
||||||
|
def read_file(self, filename, location=None):
|
||||||
|
# type: (unicode, Any) -> List[unicode]
|
||||||
|
|
||||||
|
global cache
|
||||||
|
try:
|
||||||
|
if filename in cache:
|
||||||
|
lines = cache[filename]
|
||||||
|
else:
|
||||||
|
with closing(requests.get(filename, stream=True)) as r:
|
||||||
|
r.encoding = self.encoding
|
||||||
|
lines = r.text.splitlines(True)
|
||||||
|
cache[filename] = lines
|
||||||
|
|
||||||
|
if 'tab-width' in self.options:
|
||||||
|
lines = [line.expandtabs(self.options['tab-width']) for line in lines]
|
||||||
|
|
||||||
|
return lines
|
||||||
|
except (IOError, OSError):
|
||||||
|
raise IOError(_('Include file %r not found or reading it failed') % filename)
|
||||||
|
except UnicodeError:
|
||||||
|
raise UnicodeError(_('Encoding %r used for reading included file %r seems to '
|
||||||
|
'be wrong, try giving an :encoding: option') %
|
||||||
|
(self.encoding, filename))
|
||||||
|
|
||||||
|
|
||||||
class OnlineIncludeDirective(LiteralInclude):
|
class OnlineIncludeDirective(LiteralInclude):
|
||||||
|
|
||||||
def read_with_encoding(self, filename, document, codec_info, encoding):
|
def run(self):
|
||||||
global cache
|
# type: () -> List[nodes.Node]
|
||||||
|
document = self.state.document
|
||||||
|
if not document.settings.file_insertion_enabled:
|
||||||
|
return [document.reporter.warning('File insertion disabled',
|
||||||
|
line=self.lineno)]
|
||||||
|
env = document.settings.env
|
||||||
|
|
||||||
|
# convert options['diff'] to absolute path
|
||||||
|
if 'diff' in self.options:
|
||||||
|
_, path = env.relfn2path(self.options['diff'])
|
||||||
|
self.options['diff'] = path
|
||||||
|
|
||||||
f = None
|
|
||||||
try:
|
try:
|
||||||
if not self.arguments[0] in cache:
|
location = self.state_machine.get_source_and_line(self.lineno)
|
||||||
f = codecs.StreamReaderWriter(urllib2.urlopen(self.arguments[0]), codec_info[2],
|
url = self.arguments[0]
|
||||||
codec_info[3], 'strict')
|
|
||||||
lines = f.readlines()
|
reader = OnlineIncludeReader(url, self.options, env.config)
|
||||||
cache[self.arguments[0]] = lines
|
text, lines = reader.read(location=location)
|
||||||
else:
|
|
||||||
lines = cache[self.arguments[0]]
|
retnode = nodes.literal_block(text, text, source=url)
|
||||||
|
set_source_info(self, retnode)
|
||||||
|
if self.options.get('diff'): # if diff is set, set udiff
|
||||||
|
retnode['language'] = 'udiff'
|
||||||
|
elif 'language' in self.options:
|
||||||
|
retnode['language'] = self.options['language']
|
||||||
|
retnode['linenos'] = ('linenos' in self.options or
|
||||||
|
'lineno-start' in self.options or
|
||||||
|
'lineno-match' in self.options)
|
||||||
|
retnode['classes'] += self.options.get('class', [])
|
||||||
|
extra_args = retnode['highlight_args'] = {}
|
||||||
|
if 'emphasize-lines' in self.options:
|
||||||
|
hl_lines = parselinenos(self.options['emphasize-lines'], lines)
|
||||||
|
if any(i >= lines for i in hl_lines):
|
||||||
|
logger.warning('line number spec is out of range(1-%d): %r' %
|
||||||
|
(lines, self.options['emphasize-lines']),
|
||||||
|
location=location)
|
||||||
|
extra_args['hl_lines'] = [x + 1 for x in hl_lines if x < lines]
|
||||||
|
extra_args['linenostart'] = reader.lineno_start
|
||||||
|
|
||||||
|
if 'caption' in self.options:
|
||||||
|
caption = self.options['caption'] or self.arguments[0]
|
||||||
|
retnode = container_wrapper(self, retnode, caption)
|
||||||
|
|
||||||
|
# retnode will be note_implicit_target that is linked from caption and numref.
|
||||||
|
# when options['name'] is provided, it should be primary ID.
|
||||||
|
self.add_name(retnode)
|
||||||
|
|
||||||
|
return [retnode]
|
||||||
|
except Exception as exc:
|
||||||
|
return [document.reporter.warning(str(exc), line=self.lineno)]
|
||||||
|
|
||||||
lines = dedent_lines(lines, self.options.get('dedent'))
|
|
||||||
return lines
|
|
||||||
except (IOError, OSError, urllib2.URLError):
|
|
||||||
return [document.reporter.warning(
|
|
||||||
'Include file %r not found or reading it failed' % self.arguments[0],
|
|
||||||
line=self.lineno)]
|
|
||||||
except UnicodeError:
|
|
||||||
return [document.reporter.warning(
|
|
||||||
'Encoding %r used for reading included file %r seems to '
|
|
||||||
'be wrong, try giving an :encoding: option' %
|
|
||||||
(encoding, self.arguments[0]))]
|
|
||||||
finally:
|
|
||||||
if f is not None:
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def visit_onlineinclude(translator, node):
|
def visit_onlineinclude(translator, node):
|
||||||
translator.visit_literal_block(node)
|
translator.visit_literal_block(node)
|
||||||
|
|
@ -53,4 +111,4 @@ def setup(app):
|
||||||
app.add_directive("onlineinclude", OnlineIncludeDirective)
|
app.add_directive("onlineinclude", OnlineIncludeDirective)
|
||||||
|
|
||||||
handler = (visit_onlineinclude, depart_onlineinclude)
|
handler = (visit_onlineinclude, depart_onlineinclude)
|
||||||
app.add_node(OnlineIncludeDirective, html=handler, latex=handler, text=handler)
|
app.add_node(OnlineIncludeDirective, html=handler, latex=handler, text=handler)
|
||||||
|
|
|
||||||
5
setup.py
5
setup.py
|
|
@ -65,9 +65,10 @@ EXTRA_REQUIRES = dict(
|
||||||
"ddt",
|
"ddt",
|
||||||
|
|
||||||
# Documentation dependencies
|
# Documentation dependencies
|
||||||
"sphinx>=1.3,<1.4",
|
"sphinx>=1.6,<1.7",
|
||||||
"sphinxcontrib-httpdomain",
|
"sphinxcontrib-httpdomain",
|
||||||
"sphinx_rtd_theme",
|
"sphinxcontrib-mermaid",
|
||||||
|
"sphinx_bootstrap_theme",
|
||||||
|
|
||||||
# PyPi upload related
|
# PyPi upload related
|
||||||
"pypandoc"
|
"pypandoc"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue