1# -*- test-case-name: nevow.test.test_athena -*-
2
3"""
4twistd subcommand plugin for launching an athena widget server.
5"""
6
7from twisted.python.usage import Options, UsageError
8from twisted.python.reflect import namedAny
9from twisted.application.internet import TCPServer
10
11from nevow.loaders import stan
12from nevow.athena import LivePage
13from nevow.appserver import NevowSite
14from nevow.tags import html, head, title, body, div, directive
15
16
17class ElementRenderingLivePage(LivePage):
18    """
19    Trivial LivePage which renders an instance of a particular element class.
20    """
21    docFactory = stan(html[
22            head(render=directive('liveglue'))[
23                title(render=directive('title'))],
24            body[
25                div(render=directive('element'))]])
26
27    def __init__(self, element):
28        LivePage.__init__(self)
29        self.element = element
30
31
32    def render_title(self, ctx, data):
33        return ctx.tag[self.element.__class__.__name__]
34
35
36    def render_element(self, ctx, data):
37        self.element.setFragmentParent(self)
38        return ctx.tag[self.element]
39
40
41
42class WidgetPluginRoot(LivePage):
43    """
44    Provide a top-level resource for creating new widget elements with each
45    reqest.
46    """
47    def __init__(self, elementFactory):
48        """
49        Initialize the resource with the passwed plugin element.
50        """
51        LivePage.__init__(self)
52        self.elementFactory = elementFactory
53
54
55    def child_(self, ctx):
56        """
57        Instead of returning the default self, return a new instance of this
58        class, thus allowing page reloads (that produce a new instance).
59        """
60        return ElementRenderingLivePage(self.elementFactory())
61
62
63
64class Options(Options):
65    """
66    Command-line parameters for the athena widget twistd plugin.
67    """
68    def opt_port(self, portstr):
69        """
70        Specify the port number to listen on.
71        """
72        try:
73            self['port'] = int(portstr)
74        except ValueError:
75            raise UsageError(
76                "Specify an integer between 0 and 65535 as a port number.")
77        if self['port'] >= 2 ** 16:
78            raise UsageError(
79                "Specify an integer between 0 and 65535 as a port number.")
80        elif self['port'] < 0:
81            raise UsageError(
82                "Specify an integer between 0 and 65535 as a port number.")
83
84
85    def opt_element(self, qualifiedName):
86        """
87        Specify the LiveElement or LiveFragment class to serve.
88        """
89        try:
90            factory = namedAny(qualifiedName)
91        except (ValueError, AttributeError):
92            raise UsageError("Specify a valid class name to --element")
93        self['element'] = factory
94
95
96    def postOptions(self):
97        """
98        Assign default values for those arguments not specified.
99        """
100        if 'port' not in self:
101            self['port'] = 8080
102        if 'element' not in self:
103            raise UsageError("Specify a class name to --element")
104
105
106
107def makeService(options):
108    """
109    @type options: C{dict}
110
111    @param options: Configuration for the NevowSite to start.  Required
112    keys are::
113
114        C{'element'}: a LiveElement or LiveFragment instance.
115
116        C{'port'}: an C{int} giving the port number to which to bind.
117
118    """
119    element = options['element']
120    page = WidgetPluginRoot(element)
121    return TCPServer(options['port'], NevowSite(page))
122