Posts about mantissa-introhttps://mithrandi.net/categories/mantissa-intro.atom2018-05-19T08:14:07ZmithrandiNikolaAn introduction to Mantissa (part 3): Navigation powerups and other friendshttps://mithrandi.net/blog/2012/03/an-introduction-to-mantissa-part-3-navigation-powerups-and-other-friends/2012-03-04T10:51:24Z2012-03-04T10:51:24Zmithrandi<div><p>This is the fourth post in a <a class="reference external" href="https://mithrandi.net/categories/mantissa-intro/">series of articles</a> about <a class="reference external" href="https://github.com/twisted/mantissa">Mantissa</a>.</p>
<p>In <a class="reference external" href="https://mithrandi.net/blog/2010/07/an-introduction-to-mantissa-part-2-offerings/">the previous article</a> I described how an offering can provide powerups to
be included in a product, which will then be installed on a user store; in this
installment, I will discuss what form these powerups can actually take, and how
they allow you to expose your application's functionality to the user.</p>
<p>One of the most commonly-implemented powerup interfaces in Mantissa is
<tt class="docutils literal">INavigableElement</tt>. Mantissa has a somewhat generalized idea of
"navigation", whereby a nested menu structure can be defined through
<tt class="docutils literal">INavigableElement</tt> powerups, and then displayed by different implementations
for different protocols; for example, the web view has a visual dropdown menu
system, whereas the SSH server presents a textual menu system. A typical
<tt class="docutils literal">INavigableElement</tt> powerup implementation will look something like this:</p>
<p><a class="reference external" href="https://mithrandi.net/listings/pony_navigable.py.html">pony_navigable.py</a> <a class="reference external" href="https://mithrandi.net/listings/pony_navigable.py">(Source)</a></p>
<pre class="code python"><a name="rest_code_79a18e13b266456ca4d46b1277a31192-1"></a><span class="kn">from</span> <span class="nn">zope.interface</span> <span class="kn">import</span> <span class="n">implements</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-2"></a><span class="kn">from</span> <span class="nn">axiom.item</span> <span class="kn">import</span> <span class="n">Item</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-3"></a><span class="kn">from</span> <span class="nn">axiom.attributes</span> <span class="kn">import</span> <span class="n">integer</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-4"></a><span class="kn">from</span> <span class="nn">xmantissa.ixmantissa</span> <span class="kn">import</span> <span class="n">INavigableElement</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-5"></a><span class="kn">from</span> <span class="nn">xmantissa.webnav</span> <span class="kn">import</span> <span class="n">Tab</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-6"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-7"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-8"></a><span class="k">class</span> <span class="nc">PonyCreator</span><span class="p">(</span><span class="n">Item</span><span class="p">):</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-9"></a> <span class="sd">"""</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-10"></a><span class="sd"> Powerup for creating and managing ponies.</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-11"></a><span class="sd"> """</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-12"></a> <span class="n">implements</span><span class="p">(</span><span class="n">INavigableElement</span><span class="p">)</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-13"></a> <span class="n">powerupInterfaces</span> <span class="o">=</span> <span class="p">[</span><span class="n">INavigableElement</span><span class="p">]</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-14"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-15"></a> <span class="n">ponyQuota</span> <span class="o">=</span> <span class="n">integer</span><span class="p">(</span><span class="n">allowNone</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-16"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-17"></a> <span class="k">def</span> <span class="nf">getTabs</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-18"></a> <span class="k">return</span> <span class="p">[</span><span class="n">Tab</span><span class="p">(</span><span class="s1">'ZOMG PONIES!'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">storeID</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)]</span>
</pre><p><tt class="docutils literal">INavigableElement</tt> only has one method, <tt class="docutils literal">getTabs</tt>, which returns a list of
"tabs" or menu items to be presented in the nav. The primary components of a
tab are a title (which is how the item is displayed in the UI), the <tt class="docutils literal">storeID</tt>
of an item in the same store which the tab points to, and a float between 0.0
and 1.0 indicating the sort priority of the tab (higher values sort sooner). In
this case, we have the tab pointing directly at the <tt class="docutils literal">PonyCreator</tt> item
itself; in order for this to work, we'll need some extra code to allow
<tt class="docutils literal">PonyCreator</tt> to be exposed via the web.</p>
<p>In order for an item in a user's store to be privately accessible via the web
by that user, it needs to be adaptable to the (somewhat poorly-named)
<tt class="docutils literal">INavigableFragment</tt> interface. This is almost always done by defining an
adapter from the item type to <tt class="docutils literal">INavigableFragment</tt>:</p>
<p><a class="reference external" href="https://mithrandi.net/listings/pony_view.py.html">pony_view.py</a> <a class="reference external" href="https://mithrandi.net/listings/pony_view.py">(Source)</a></p>
<pre class="code python"><a name="rest_code_86d1255455f545a491f235c643088e3e-1"></a><span class="kn">from</span> <span class="nn">twisted.python.components</span> <span class="kn">import</span> <span class="n">registerAdapter</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-2"></a><span class="kn">from</span> <span class="nn">nevow.athena</span> <span class="kn">import</span> <span class="n">LiveElement</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-3"></a><span class="kn">from</span> <span class="nn">xmantissa.webtheme</span> <span class="kn">import</span> <span class="n">ThemedDocumentFactory</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-4"></a><span class="kn">from</span> <span class="nn">xmantissa.ixmantissa</span> <span class="kn">import</span> <span class="n">INavigableFragment</span><span class="p">,</span> <span class="n">ITemplateNameResolver</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-5"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-6"></a><span class="k">class</span> <span class="nc">PonyCreatorView</span><span class="p">(</span><span class="n">LiveElement</span><span class="p">):</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-7"></a> <span class="sd">"""</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-8"></a><span class="sd"> Web view for Pony management.</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-9"></a><span class="sd"> """</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-10"></a> <span class="n">implements</span><span class="p">(</span><span class="n">INavigableFragment</span><span class="p">)</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-11"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-12"></a> <span class="n">title</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'Pony management'</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-13"></a> <span class="n">docFactory</span> <span class="o">=</span> <span class="n">ThemedDocumentFactory</span><span class="p">(</span><span class="s1">'pony-creator'</span><span class="p">,</span> <span class="s1">'resolver'</span><span class="p">)</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-14"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-15"></a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ponyCreator</span><span class="p">):</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-16"></a> <span class="nb">super</span><span class="p">(</span><span class="n">PonyCreatorView</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-17"></a> <span class="bp">self</span><span class="o">.</span><span class="n">ponyCreator</span> <span class="o">=</span> <span class="n">ponyCreator</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-18"></a> <span class="bp">self</span><span class="o">.</span><span class="n">resolver</span> <span class="o">=</span> <span class="n">ITemplateNameResolver</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ponyCreator</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-19"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-20"></a><span class="n">registerAdapter</span><span class="p">(</span><span class="n">PonyCreatorView</span><span class="p">,</span> <span class="n">PonyCreator</span><span class="p">,</span> <span class="n">INavigableFragment</span><span class="p">)</span>
</pre><p>Our element will be wrapped in the Mantissa shell when it is rendered, so we
cannot control the page title directly from the template, but the title
attribute provides a way for our element to specify the page title.
<tt class="docutils literal">ThemedDocumentFactory</tt> is used to retrieve the template through the theme
system; the arguments are the name of the template (<tt class="docutils literal"><span class="pre">'pony-creator'</span></tt>) and the
name of the attribute holding the <tt class="docutils literal">ITemplateNameResolver</tt> implementation used
to retrieve the template. This attribute is set in <tt class="docutils literal">__init__</tt> using a
slightly awkward method; the template resolver should really be passed in by
Mantissa somehow, but currently there is no mechanism for doing this, so
instead we retrieve the default resolver ourselves from the site store.</p>
<p>This is all that is needed for hooking some code up to the web view; any
further UI behaviour would be implemented in HTML / JavaScript in
<tt class="docutils literal">PonyCreatorView</tt>, usually by invoking additional methods defined on
<tt class="docutils literal">PonyCreator</tt>.</p>
<p>Next up: Sharing, or "How do I publish public / shared content?"</p></div><div><p>This is the fourth post in a <a class="reference external" href="https://mithrandi.net/categories/mantissa-intro/">series of articles</a> about <a class="reference external" href="https://github.com/twisted/mantissa">Mantissa</a>.</p>
<p>In <a class="reference external" href="https://mithrandi.net/blog/2010/07/an-introduction-to-mantissa-part-2-offerings/">the previous article</a> I described how an offering can provide powerups to
be included in a product, which will then be installed on a user store; in this
installment, I will discuss what form these powerups can actually take, and how
they allow you to expose your application's functionality to the user.</p>
<p>One of the most commonly-implemented powerup interfaces in Mantissa is
<tt class="docutils literal">INavigableElement</tt>. Mantissa has a somewhat generalized idea of
"navigation", whereby a nested menu structure can be defined through
<tt class="docutils literal">INavigableElement</tt> powerups, and then displayed by different implementations
for different protocols; for example, the web view has a visual dropdown menu
system, whereas the SSH server presents a textual menu system. A typical
<tt class="docutils literal">INavigableElement</tt> powerup implementation will look something like this:</p>
<p><a class="reference external" href="https://mithrandi.net/listings/pony_navigable.py.html">pony_navigable.py</a> <a class="reference external" href="https://mithrandi.net/listings/pony_navigable.py">(Source)</a></p>
<pre class="code python"><a name="rest_code_79a18e13b266456ca4d46b1277a31192-1"></a><span class="kn">from</span> <span class="nn">zope.interface</span> <span class="kn">import</span> <span class="n">implements</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-2"></a><span class="kn">from</span> <span class="nn">axiom.item</span> <span class="kn">import</span> <span class="n">Item</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-3"></a><span class="kn">from</span> <span class="nn">axiom.attributes</span> <span class="kn">import</span> <span class="n">integer</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-4"></a><span class="kn">from</span> <span class="nn">xmantissa.ixmantissa</span> <span class="kn">import</span> <span class="n">INavigableElement</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-5"></a><span class="kn">from</span> <span class="nn">xmantissa.webnav</span> <span class="kn">import</span> <span class="n">Tab</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-6"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-7"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-8"></a><span class="k">class</span> <span class="nc">PonyCreator</span><span class="p">(</span><span class="n">Item</span><span class="p">):</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-9"></a> <span class="sd">"""</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-10"></a><span class="sd"> Powerup for creating and managing ponies.</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-11"></a><span class="sd"> """</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-12"></a> <span class="n">implements</span><span class="p">(</span><span class="n">INavigableElement</span><span class="p">)</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-13"></a> <span class="n">powerupInterfaces</span> <span class="o">=</span> <span class="p">[</span><span class="n">INavigableElement</span><span class="p">]</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-14"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-15"></a> <span class="n">ponyQuota</span> <span class="o">=</span> <span class="n">integer</span><span class="p">(</span><span class="n">allowNone</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-16"></a>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-17"></a> <span class="k">def</span> <span class="nf">getTabs</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<a name="rest_code_79a18e13b266456ca4d46b1277a31192-18"></a> <span class="k">return</span> <span class="p">[</span><span class="n">Tab</span><span class="p">(</span><span class="s1">'ZOMG PONIES!'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">storeID</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)]</span>
</pre><p><tt class="docutils literal">INavigableElement</tt> only has one method, <tt class="docutils literal">getTabs</tt>, which returns a list of
"tabs" or menu items to be presented in the nav. The primary components of a
tab are a title (which is how the item is displayed in the UI), the <tt class="docutils literal">storeID</tt>
of an item in the same store which the tab points to, and a float between 0.0
and 1.0 indicating the sort priority of the tab (higher values sort sooner). In
this case, we have the tab pointing directly at the <tt class="docutils literal">PonyCreator</tt> item
itself; in order for this to work, we'll need some extra code to allow
<tt class="docutils literal">PonyCreator</tt> to be exposed via the web.</p>
<p>In order for an item in a user's store to be privately accessible via the web
by that user, it needs to be adaptable to the (somewhat poorly-named)
<tt class="docutils literal">INavigableFragment</tt> interface. This is almost always done by defining an
adapter from the item type to <tt class="docutils literal">INavigableFragment</tt>:</p>
<p><a class="reference external" href="https://mithrandi.net/listings/pony_view.py.html">pony_view.py</a> <a class="reference external" href="https://mithrandi.net/listings/pony_view.py">(Source)</a></p>
<pre class="code python"><a name="rest_code_86d1255455f545a491f235c643088e3e-1"></a><span class="kn">from</span> <span class="nn">twisted.python.components</span> <span class="kn">import</span> <span class="n">registerAdapter</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-2"></a><span class="kn">from</span> <span class="nn">nevow.athena</span> <span class="kn">import</span> <span class="n">LiveElement</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-3"></a><span class="kn">from</span> <span class="nn">xmantissa.webtheme</span> <span class="kn">import</span> <span class="n">ThemedDocumentFactory</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-4"></a><span class="kn">from</span> <span class="nn">xmantissa.ixmantissa</span> <span class="kn">import</span> <span class="n">INavigableFragment</span><span class="p">,</span> <span class="n">ITemplateNameResolver</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-5"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-6"></a><span class="k">class</span> <span class="nc">PonyCreatorView</span><span class="p">(</span><span class="n">LiveElement</span><span class="p">):</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-7"></a> <span class="sd">"""</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-8"></a><span class="sd"> Web view for Pony management.</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-9"></a><span class="sd"> """</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-10"></a> <span class="n">implements</span><span class="p">(</span><span class="n">INavigableFragment</span><span class="p">)</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-11"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-12"></a> <span class="n">title</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'Pony management'</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-13"></a> <span class="n">docFactory</span> <span class="o">=</span> <span class="n">ThemedDocumentFactory</span><span class="p">(</span><span class="s1">'pony-creator'</span><span class="p">,</span> <span class="s1">'resolver'</span><span class="p">)</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-14"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-15"></a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ponyCreator</span><span class="p">):</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-16"></a> <span class="nb">super</span><span class="p">(</span><span class="n">PonyCreatorView</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-17"></a> <span class="bp">self</span><span class="o">.</span><span class="n">ponyCreator</span> <span class="o">=</span> <span class="n">ponyCreator</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-18"></a> <span class="bp">self</span><span class="o">.</span><span class="n">resolver</span> <span class="o">=</span> <span class="n">ITemplateNameResolver</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ponyCreator</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
<a name="rest_code_86d1255455f545a491f235c643088e3e-19"></a>
<a name="rest_code_86d1255455f545a491f235c643088e3e-20"></a><span class="n">registerAdapter</span><span class="p">(</span><span class="n">PonyCreatorView</span><span class="p">,</span> <span class="n">PonyCreator</span><span class="p">,</span> <span class="n">INavigableFragment</span><span class="p">)</span>
</pre><p>Our element will be wrapped in the Mantissa shell when it is rendered, so we
cannot control the page title directly from the template, but the title
attribute provides a way for our element to specify the page title.
<tt class="docutils literal">ThemedDocumentFactory</tt> is used to retrieve the template through the theme
system; the arguments are the name of the template (<tt class="docutils literal"><span class="pre">'pony-creator'</span></tt>) and the
name of the attribute holding the <tt class="docutils literal">ITemplateNameResolver</tt> implementation used
to retrieve the template. This attribute is set in <tt class="docutils literal">__init__</tt> using a
slightly awkward method; the template resolver should really be passed in by
Mantissa somehow, but currently there is no mechanism for doing this, so
instead we retrieve the default resolver ourselves from the site store.</p>
<p>This is all that is needed for hooking some code up to the web view; any
further UI behaviour would be implemented in HTML / JavaScript in
<tt class="docutils literal">PonyCreatorView</tt>, usually by invoking additional methods defined on
<tt class="docutils literal">PonyCreator</tt>.</p>
<p>Next up: Sharing, or "How do I publish public / shared content?"</p></div>An introduction to Mantissa (part 2): Offeringshttps://mithrandi.net/blog/2010/07/an-introduction-to-mantissa-part-2-offerings/2010-07-10T15:46:44Z2010-07-10T15:46:44Zmithrandi<div><p>This is the third post in a <a class="reference external" href="https://mithrandi.net/categories/mantissa-intro/">series of articles</a> about <a class="reference external" href="https://github.com/twisted/mantissa">Mantissa</a>.</p>
<p>The primary code entry point in Mantissa is the offering. Your application can
provide an <tt class="docutils literal">IOffering</tt> plugin in <tt class="docutils literal">xmantissa.plugins</tt>, which hands some
chunks of your code over to be used by Mantissa. There is no real reason to
implement <tt class="docutils literal">IOffering</tt> yourself; instead, simply instantiate
<tt class="docutils literal">xmantissa.offering.Offering</tt> in your plugin module. For example, you might
have an <tt class="docutils literal">xmantissa/plugins/ponies.py</tt> module containing the following code:</p>
<p><a class="reference external" href="https://mithrandi.net/listings/ponies.py.html">ponies.py</a> <a class="reference external" href="https://mithrandi.net/listings/ponies.py">(Source)</a></p>
<pre class="code python"><a name="rest_code_b049c763aa224652b6b0df0bd28defa7-1"></a><span class="kn">from</span> <span class="nn">twisted.python.filepath</span> <span class="kn">import</span> <span class="n">FilePath</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-2"></a>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-3"></a><span class="kn">from</span> <span class="nn">xmantissa.offering</span> <span class="kn">import</span> <span class="n">Offering</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-4"></a>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-5"></a><span class="kn">import</span> <span class="nn">ponies</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-6"></a><span class="kn">from</span> <span class="nn">ponies.ponies</span> <span class="kn">import</span> <span class="n">PonyCreator</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-7"></a><span class="kn">from</span> <span class="nn">ponies.rainbows</span> <span class="kn">import</span> <span class="n">RainbowEditor</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-8"></a><span class="kn">from</span> <span class="nn">ponies.theme</span> <span class="kn">import</span> <span class="n">PoniesTheme</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-9"></a>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-10"></a><span class="n">plugin</span> <span class="o">=</span> <span class="n">Offering</span><span class="p">(</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-11"></a> <span class="n">name</span><span class="o">=</span><span class="sa">u</span><span class="s1">'Ponies'</span><span class="p">,</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-12"></a> <span class="n">description</span><span class="o">=</span><span class="sa">u</span><span class="s2">"""</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-13"></a><span class="s2"> A web-based system for generating ponies.</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-14"></a><span class="s2"> """</span><span class="p">,</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-15"></a> <span class="n">siteRequirements</span><span class="o">=</span><span class="p">[],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-16"></a> <span class="n">appPowerups</span><span class="o">=</span><span class="p">[],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-17"></a> <span class="n">installablePowerups</span><span class="o">=</span><span class="p">[(</span><span class="sa">u</span><span class="s1">'ponies'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Pony creation and editing'</span><span class="p">,</span> <span class="n">PonyCreator</span><span class="p">),</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-18"></a> <span class="p">(</span><span class="sa">u</span><span class="s1">'rainbows'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Rainbow editing'</span><span class="p">,</span> <span class="n">RainbowEditor</span><span class="p">)],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-19"></a> <span class="n">loginInterfaces</span><span class="o">=</span><span class="p">[],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-20"></a> <span class="n">themes</span><span class="o">=</span><span class="p">[</span><span class="n">PoniesTheme</span><span class="p">(</span><span class="s1">'base'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-21"></a> <span class="n">version</span><span class="o">=</span><span class="n">ponies</span><span class="o">.</span><span class="n">version</span><span class="p">,</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-22"></a> <span class="n">staticContentPath</span><span class="o">=</span><span class="n">FilePath</span><span class="p">(</span><span class="n">ponies</span><span class="o">.</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">sibling</span><span class="p">(</span><span class="s1">'static'</span><span class="p">))</span>
</pre><p>There are a number of elements here, some of which we will only return to
later, but I'll run through all of them here briefly.</p>
<ul class="simple">
<li>name: A short identifier used as the name of this offering.</li>
<li>description: A long description explaining what this offering is.</li>
<li>siteRequirements: Interfaces that this offering requires from the site store.</li>
<li>appPowerups: Powerups to be installed on the app store.</li></li>
<li>installablePowerups: A list of 3-tuples, <tt class="docutils literal">(name, description, item class)</tt>.
Each of these tuples describes a powerup that can potentially be installed on
a user store.</li>
<li>loginInterfaces: This allows your offering to provide additional ways of
logging into Mantissa. For example, a webmail offering might provide
interfaces that are used when logging in via SMTP or POP3.</li>
<li>themes: The themes system provides a way to provide and override the
templates used by your widgets.</li>
<li>staticContentPath: A path pointing at the root of your static content. The
file hierarchy at this location will be served up by Mantissa at
<tt class="docutils literal">/static/YourOfferingName</tt>.</li>
<li>version: A <tt class="docutils literal">Version</tt> object specifying the version number of your offering.</li>
</ul>
<p>For more information, take a look at <tt class="docutils literal">xmantissa.ixmantissa.IOffering</tt>, which
documents each of these attributes in more detail. The main highlights here,
though, are <tt class="docutils literal">appPowerups</tt>, for the powerups you want installed on the app
store, and <tt class="docutils literal">installablePowerups</tt>, for powerups you want to be available for
installation on the user store.</p>
<p>To set up a Mantissa site, the first step is to run <tt class="docutils literal">axiomatic mantissa</tt>;
this will prompt you for an admin password as part of installing the base
Mantissa components. To start the site up, you run <tt class="docutils literal">axiomatic start</tt>; usually
you would pass the <tt class="docutils literal"><span class="pre">--nodaemon</span></tt> flag during development, which prevents the
server from daemonizing, and directs log output to the console instead of to a
log file. If you log in as the admin user (<tt class="docutils literal">admin@localhost</tt> by default) via
the web interface, you can install an Offering through the admin UI;
alternatively, you can install offerings from the command line by running
<tt class="docutils literal">axiomatic offering install SomeOfferingName</tt>.</p>
<p>When your offering is installed, the powerups in <tt class="docutils literal">appPowerups</tt> will be
installed onto the app store for your offering. The <tt class="docutils literal">installablePowerups</tt> you
provided will not be installed anywhere immediately, instead they will be
exposed in the admin UI for products. A product is an administratively-defined
collection of powerups; the admin can select any of the <tt class="docutils literal">installablePowerups</tt>
from any of the installed offerings when creating a product. Products are used
in conjunction with signup methods, which are also locally configured by the
admin. A signup has a product associated with it; when a user signs up using
this method, all of the powerups that comprise the product will be installed on
their user store.</p>
<p>Whew! This may all seem like more indirection than is actually necessary, but
if you view Mantissa as a layer that multiplexes between users and
applications, it may make a little more sense.</p>
<p>Next up: Powerup interfaces, or "How does the user actually interact with my code?"</p></div><div><p>This is the third post in a <a class="reference external" href="https://mithrandi.net/categories/mantissa-intro/">series of articles</a> about <a class="reference external" href="https://github.com/twisted/mantissa">Mantissa</a>.</p>
<p>The primary code entry point in Mantissa is the offering. Your application can
provide an <tt class="docutils literal">IOffering</tt> plugin in <tt class="docutils literal">xmantissa.plugins</tt>, which hands some
chunks of your code over to be used by Mantissa. There is no real reason to
implement <tt class="docutils literal">IOffering</tt> yourself; instead, simply instantiate
<tt class="docutils literal">xmantissa.offering.Offering</tt> in your plugin module. For example, you might
have an <tt class="docutils literal">xmantissa/plugins/ponies.py</tt> module containing the following code:</p>
<p><a class="reference external" href="https://mithrandi.net/listings/ponies.py.html">ponies.py</a> <a class="reference external" href="https://mithrandi.net/listings/ponies.py">(Source)</a></p>
<pre class="code python"><a name="rest_code_b049c763aa224652b6b0df0bd28defa7-1"></a><span class="kn">from</span> <span class="nn">twisted.python.filepath</span> <span class="kn">import</span> <span class="n">FilePath</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-2"></a>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-3"></a><span class="kn">from</span> <span class="nn">xmantissa.offering</span> <span class="kn">import</span> <span class="n">Offering</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-4"></a>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-5"></a><span class="kn">import</span> <span class="nn">ponies</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-6"></a><span class="kn">from</span> <span class="nn">ponies.ponies</span> <span class="kn">import</span> <span class="n">PonyCreator</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-7"></a><span class="kn">from</span> <span class="nn">ponies.rainbows</span> <span class="kn">import</span> <span class="n">RainbowEditor</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-8"></a><span class="kn">from</span> <span class="nn">ponies.theme</span> <span class="kn">import</span> <span class="n">PoniesTheme</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-9"></a>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-10"></a><span class="n">plugin</span> <span class="o">=</span> <span class="n">Offering</span><span class="p">(</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-11"></a> <span class="n">name</span><span class="o">=</span><span class="sa">u</span><span class="s1">'Ponies'</span><span class="p">,</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-12"></a> <span class="n">description</span><span class="o">=</span><span class="sa">u</span><span class="s2">"""</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-13"></a><span class="s2"> A web-based system for generating ponies.</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-14"></a><span class="s2"> """</span><span class="p">,</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-15"></a> <span class="n">siteRequirements</span><span class="o">=</span><span class="p">[],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-16"></a> <span class="n">appPowerups</span><span class="o">=</span><span class="p">[],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-17"></a> <span class="n">installablePowerups</span><span class="o">=</span><span class="p">[(</span><span class="sa">u</span><span class="s1">'ponies'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Pony creation and editing'</span><span class="p">,</span> <span class="n">PonyCreator</span><span class="p">),</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-18"></a> <span class="p">(</span><span class="sa">u</span><span class="s1">'rainbows'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Rainbow editing'</span><span class="p">,</span> <span class="n">RainbowEditor</span><span class="p">)],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-19"></a> <span class="n">loginInterfaces</span><span class="o">=</span><span class="p">[],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-20"></a> <span class="n">themes</span><span class="o">=</span><span class="p">[</span><span class="n">PoniesTheme</span><span class="p">(</span><span class="s1">'base'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)],</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-21"></a> <span class="n">version</span><span class="o">=</span><span class="n">ponies</span><span class="o">.</span><span class="n">version</span><span class="p">,</span>
<a name="rest_code_b049c763aa224652b6b0df0bd28defa7-22"></a> <span class="n">staticContentPath</span><span class="o">=</span><span class="n">FilePath</span><span class="p">(</span><span class="n">ponies</span><span class="o">.</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">sibling</span><span class="p">(</span><span class="s1">'static'</span><span class="p">))</span>
</pre><p>There are a number of elements here, some of which we will only return to
later, but I'll run through all of them here briefly.</p>
<ul class="simple">
<li>name: A short identifier used as the name of this offering.</li>
<li>description: A long description explaining what this offering is.</li>
<li>siteRequirements: Interfaces that this offering requires from the site store.</li>
<li>appPowerups: Powerups to be installed on the app store.</li></li>
<li>installablePowerups: A list of 3-tuples, <tt class="docutils literal">(name, description, item class)</tt>.
Each of these tuples describes a powerup that can potentially be installed on
a user store.</li>
<li>loginInterfaces: This allows your offering to provide additional ways of
logging into Mantissa. For example, a webmail offering might provide
interfaces that are used when logging in via SMTP or POP3.</li>
<li>themes: The themes system provides a way to provide and override the
templates used by your widgets.</li>
<li>staticContentPath: A path pointing at the root of your static content. The
file hierarchy at this location will be served up by Mantissa at
<tt class="docutils literal">/static/YourOfferingName</tt>.</li>
<li>version: A <tt class="docutils literal">Version</tt> object specifying the version number of your offering.</li>
</ul>
<p>For more information, take a look at <tt class="docutils literal">xmantissa.ixmantissa.IOffering</tt>, which
documents each of these attributes in more detail. The main highlights here,
though, are <tt class="docutils literal">appPowerups</tt>, for the powerups you want installed on the app
store, and <tt class="docutils literal">installablePowerups</tt>, for powerups you want to be available for
installation on the user store.</p>
<p>To set up a Mantissa site, the first step is to run <tt class="docutils literal">axiomatic mantissa</tt>;
this will prompt you for an admin password as part of installing the base
Mantissa components. To start the site up, you run <tt class="docutils literal">axiomatic start</tt>; usually
you would pass the <tt class="docutils literal"><span class="pre">--nodaemon</span></tt> flag during development, which prevents the
server from daemonizing, and directs log output to the console instead of to a
log file. If you log in as the admin user (<tt class="docutils literal">admin@localhost</tt> by default) via
the web interface, you can install an Offering through the admin UI;
alternatively, you can install offerings from the command line by running
<tt class="docutils literal">axiomatic offering install SomeOfferingName</tt>.</p>
<p>When your offering is installed, the powerups in <tt class="docutils literal">appPowerups</tt> will be
installed onto the app store for your offering. The <tt class="docutils literal">installablePowerups</tt> you
provided will not be installed anywhere immediately, instead they will be
exposed in the admin UI for products. A product is an administratively-defined
collection of powerups; the admin can select any of the <tt class="docutils literal">installablePowerups</tt>
from any of the installed offerings when creating a product. Products are used
in conjunction with signup methods, which are also locally configured by the
admin. A signup has a product associated with it; when a user signs up using
this method, all of the powerups that comprise the product will be installed on
their user store.</p>
<p>Whew! This may all seem like more indirection than is actually necessary, but
if you view Mantissa as a layer that multiplexes between users and
applications, it may make a little more sense.</p>
<p>Next up: Powerup interfaces, or "How does the user actually interact with my code?"</p></div>An introduction to Mantissa (part 1): The Data Modelhttps://mithrandi.net/blog/2010/06/an-introduction-to-mantissa-part-1-the-data-model/2010-06-07T19:38:03Z2010-06-07T19:38:03Zmithrandi<div><p>This is the second post in a <a href="http://mithrandi.net/blog/tag/mantissa-intro/">series of articles</a> about <a href="http://divmod.org/trac/wiki/DivmodMantissa">Mantissa</a>.</p>
<p>As you may have already surmised, Mantissa is designed to host more than one application in the same deployment. Due to the way Mantissa builds on Axiom, it is important to first understand the data model, before starting to understand how the components of your application plug into Mantissa.</p>
<p>A Mantissa deployment, or “node”, consists of a top-level Axiom store called the site store, and various substores contained therein. The substores fall into two categories; user stores, and application stores. The site store primarily contains <a href="http://twistedmatrix.com/documents/current/api/twisted.application.service.IService.html">IService</a> powerups for the various services running in the node (think HTTP server, SMTP server, etc.), and the substore items for the user / app stores. It also contains the <a href="http://twistedmatrix.com/documents/current/api/twisted.cred.portal.IRealm.html">IRealm</a> implementation for authentication (using <a href="http://twistedmatrix.com/documents/current/core/howto/cred.html">twisted.cred</a>, naturally), along with account / login method information for each user account, as well as other configuration data such as API keys (think PayPal API, or similar).</p>
<p>The user stores are where your actual application data will be stored in a typical Mantissa application. Each user has their own Axiom user store (SQLite database), where data for that user is stored. The account / login method items for that particular user are also duplicated in the user store, to allow user stores to be transferred between site stores. Items in different user stores should not interact with each other, except via the interstore messaging API. This forms the core of Mantissa’s scaling model; while each individual user’s data must all be located on the same node, different users may have their data stored on different nodes, allowing you to spread your users across as many nodes as you need to in order to achieve the desired performance.</p>
<p>Application stores are sort of a special kind of user store; internally, they’re treated as a user store with a username corresponding to the name of the Offering (offerings will be discussed later), and a domain of None. Each application installed on a Mantissa node will have an application store; exactly what to put in there isn’t entirely clear, the best I can come up with is “application-global data”.</p>
<p>Mantissa has a system called “sharing”, whereby access to items in one user’s store can be granted to other users, or even anonymous public access granted. In the case of “websharing” (which works over HTTP), the items are accessed using a URL that specifies which user’s data is being accessed, as well as the identifier of the particular item. For example, the URL might look like http://myapp.com/mithrandi/timeline or http://mithrandi.myapp.com/timeline (“mithrandi” is the username, “timeline” is the share ID).</p>
<p>For a user-centric application (like Google Docs or Dropbox), this works pretty well; if the data in your application is primarily of global scope, then this may not be a good fit at all. There are some ways around this: firstly, not every “user” needs to be an actual live user. Thus, you can divide your data across “artificial” user stores, and share it to the users that need to have access to it, retaining Mantissa’s scalability in the process. For “global” data access or operations, you can send a message to each user store to perform a certain query or operation, and then collate the results; this model of “send the computation to the data” is similar in some ways to <a href="http://en.wikipedia.org/wiki/MapReduce">MapReduce</a>.</p>
<p>However, if your application truly does not fit into the model, then one possibility is to store the application data in a completely separate service, using Mantissa only for configuration and authorization. The fact that Mantissa stores certain data in Axiom does not prevent you from making queries to an external PostgreSQL database, or Cassandra database, or web service, or whatever else you can come up with; it just means that you derive no specific benefit from Mantissa’s database functionality.</p>
<p>Another serious caveat is that while the conceptual model and APIs for Mantissa scalability are mostly in place, the actual implementation of scaling is not there at all. There is currently no networked implementation of the interstore messaging API, there are no tools for managing a cluster of Mantissa nodes, distributing or migrating user stores, backup/replication of user stores, and so on. The foundation for building these tools and components exists, but at the moment, you’ll need to build them yourself (or pay someone to do it for you) if you need them. Thus, if you are looking for “Facebook size” scalability straight out of the box, Mantissa is probably not for you. On the other hand, depending on your needs, building the tools you need as you grow your application may not pose a significant challenge, so if you’re not afraid of writing a little code, don’t be scared off right away by this. You could also avoid relying on Mantissa for scalability, by storing your application data in another datastore that already meets your scalability needs</p>
<p>Next up: Offerings, or “How do I expose my application to Mantissa?”.</p></div><div><p>This is the second post in a <a href="http://mithrandi.net/blog/tag/mantissa-intro/">series of articles</a> about <a href="http://divmod.org/trac/wiki/DivmodMantissa">Mantissa</a>.</p>
<p>As you may have already surmised, Mantissa is designed to host more than one application in the same deployment. Due to the way Mantissa builds on Axiom, it is important to first understand the data model, before starting to understand how the components of your application plug into Mantissa.</p>
<p>A Mantissa deployment, or “node”, consists of a top-level Axiom store called the site store, and various substores contained therein. The substores fall into two categories; user stores, and application stores. The site store primarily contains <a href="http://twistedmatrix.com/documents/current/api/twisted.application.service.IService.html">IService</a> powerups for the various services running in the node (think HTTP server, SMTP server, etc.), and the substore items for the user / app stores. It also contains the <a href="http://twistedmatrix.com/documents/current/api/twisted.cred.portal.IRealm.html">IRealm</a> implementation for authentication (using <a href="http://twistedmatrix.com/documents/current/core/howto/cred.html">twisted.cred</a>, naturally), along with account / login method information for each user account, as well as other configuration data such as API keys (think PayPal API, or similar).</p>
<p>The user stores are where your actual application data will be stored in a typical Mantissa application. Each user has their own Axiom user store (SQLite database), where data for that user is stored. The account / login method items for that particular user are also duplicated in the user store, to allow user stores to be transferred between site stores. Items in different user stores should not interact with each other, except via the interstore messaging API. This forms the core of Mantissa’s scaling model; while each individual user’s data must all be located on the same node, different users may have their data stored on different nodes, allowing you to spread your users across as many nodes as you need to in order to achieve the desired performance.</p>
<p>Application stores are sort of a special kind of user store; internally, they’re treated as a user store with a username corresponding to the name of the Offering (offerings will be discussed later), and a domain of None. Each application installed on a Mantissa node will have an application store; exactly what to put in there isn’t entirely clear, the best I can come up with is “application-global data”.</p>
<p>Mantissa has a system called “sharing”, whereby access to items in one user’s store can be granted to other users, or even anonymous public access granted. In the case of “websharing” (which works over HTTP), the items are accessed using a URL that specifies which user’s data is being accessed, as well as the identifier of the particular item. For example, the URL might look like http://myapp.com/mithrandi/timeline or http://mithrandi.myapp.com/timeline (“mithrandi” is the username, “timeline” is the share ID).</p>
<p>For a user-centric application (like Google Docs or Dropbox), this works pretty well; if the data in your application is primarily of global scope, then this may not be a good fit at all. There are some ways around this: firstly, not every “user” needs to be an actual live user. Thus, you can divide your data across “artificial” user stores, and share it to the users that need to have access to it, retaining Mantissa’s scalability in the process. For “global” data access or operations, you can send a message to each user store to perform a certain query or operation, and then collate the results; this model of “send the computation to the data” is similar in some ways to <a href="http://en.wikipedia.org/wiki/MapReduce">MapReduce</a>.</p>
<p>However, if your application truly does not fit into the model, then one possibility is to store the application data in a completely separate service, using Mantissa only for configuration and authorization. The fact that Mantissa stores certain data in Axiom does not prevent you from making queries to an external PostgreSQL database, or Cassandra database, or web service, or whatever else you can come up with; it just means that you derive no specific benefit from Mantissa’s database functionality.</p>
<p>Another serious caveat is that while the conceptual model and APIs for Mantissa scalability are mostly in place, the actual implementation of scaling is not there at all. There is currently no networked implementation of the interstore messaging API, there are no tools for managing a cluster of Mantissa nodes, distributing or migrating user stores, backup/replication of user stores, and so on. The foundation for building these tools and components exists, but at the moment, you’ll need to build them yourself (or pay someone to do it for you) if you need them. Thus, if you are looking for “Facebook size” scalability straight out of the box, Mantissa is probably not for you. On the other hand, depending on your needs, building the tools you need as you grow your application may not pose a significant challenge, so if you’re not afraid of writing a little code, don’t be scared off right away by this. You could also avoid relying on Mantissa for scalability, by storing your application data in another datastore that already meets your scalability needs</p>
<p>Next up: Offerings, or “How do I expose my application to Mantissa?”.</p></div>An introduction to Mantissa (part 0)https://mithrandi.net/blog/2010/06/an-introduction-to-mantissa-part-0/2010-06-07T01:59:06Z2010-06-07T01:59:06Zmithrandi<div><p>This is the first post in a <a href="http://mithrandi.net/blog/tag/mantissa-intro/">series of articles</a> about <a href="http://divmod.org/trac/wiki/DivmodMantissa">Mantissa</a>; a <a href="http://python.org/">Python</a> application server built on <a href="http://twistedmatrix.com/">Twisted</a>, <a href="http://divmod.org/trac/wiki/DivmodNevow">Nevow</a>, and <a href="http://divmod.org/trac/wiki/DivmodAxiom">Axiom</a>.</p>
<p>“Application server” is a somewhat fuzzy term that seems to mean different things to different people; but in essence, Mantissa serves as a layer that joins your actual application code to the various ways (different protocols, for example) that your application may be accessed. Or, to put it another way, when writing a Mantissa application, the deployment target is Mantissa, not “a web server”, even if your application is only going to be accessed from the web. That’s not to say that Mantissa completely abstracts away the details of the protocol, making it impossible to control how your application is presented; rather, it provides common ground to mediate that interaction. It provides mechanisms for the common portions of functionality, such as access control, data storage, authentication and authorization, and also makes provision for defining standard interfaces through which your application is exposed to the various protocols you may wish to make use of.</p>
<p>This series is not going to be of the “build your own wiki in 60 seconds” kind. While there are some quirks and rough patches in Mantissa that may make it not the best choice for hacking together an application really quickly, it is probably still something that is doable; but unfortunately, I’m not the right person to be writing that sort of article. What I hope to do, instead, is provide a more in-depth introduction that allows you to understand and embrace (or reject!) the design philosophy of Mantissa for longer-term projects, while noting incomplete areas and other potential pitfalls along the way that may need to be avoided.</p>
<p>This is also not going to be an Axiom, Nevow, Python, or Twisted tutorial; while I will touch on aspects of those components as they relate to Mantissa, you will need to look elsewhere for further detail.</p>
<p>That’s it for now, except to thank <a href="http://jerith.za.net/">Jeremy</a> in passing for nagging me about this until I got around to doing it (or starting it, at least).</p></div><div><p>This is the first post in a <a href="http://mithrandi.net/blog/tag/mantissa-intro/">series of articles</a> about <a href="http://divmod.org/trac/wiki/DivmodMantissa">Mantissa</a>; a <a href="http://python.org/">Python</a> application server built on <a href="http://twistedmatrix.com/">Twisted</a>, <a href="http://divmod.org/trac/wiki/DivmodNevow">Nevow</a>, and <a href="http://divmod.org/trac/wiki/DivmodAxiom">Axiom</a>.</p>
<p>“Application server” is a somewhat fuzzy term that seems to mean different things to different people; but in essence, Mantissa serves as a layer that joins your actual application code to the various ways (different protocols, for example) that your application may be accessed. Or, to put it another way, when writing a Mantissa application, the deployment target is Mantissa, not “a web server”, even if your application is only going to be accessed from the web. That’s not to say that Mantissa completely abstracts away the details of the protocol, making it impossible to control how your application is presented; rather, it provides common ground to mediate that interaction. It provides mechanisms for the common portions of functionality, such as access control, data storage, authentication and authorization, and also makes provision for defining standard interfaces through which your application is exposed to the various protocols you may wish to make use of.</p>
<p>This series is not going to be of the “build your own wiki in 60 seconds” kind. While there are some quirks and rough patches in Mantissa that may make it not the best choice for hacking together an application really quickly, it is probably still something that is doable; but unfortunately, I’m not the right person to be writing that sort of article. What I hope to do, instead, is provide a more in-depth introduction that allows you to understand and embrace (or reject!) the design philosophy of Mantissa for longer-term projects, while noting incomplete areas and other potential pitfalls along the way that may need to be avoided.</p>
<p>This is also not going to be an Axiom, Nevow, Python, or Twisted tutorial; while I will touch on aspects of those components as they relate to Mantissa, you will need to look elsewhere for further detail.</p>
<p>That’s it for now, except to thank <a href="http://jerith.za.net/">Jeremy</a> in passing for nagging me about this until I got around to doing it (or starting it, at least).</p></div>