In this post I'm going to try and pitch Moya to you, Dragon's Den style. Moya has been my hobby project since 2011, and it's about time to share it with other developers. If my pitch is successful, I hope you will invest a little time to try it out.

Moya is a Python project which you can use to build a web application, but it would not be technically correct (the best kind of correct) to call it a Python web framework. This is because although although Moya is built with Python, it's not Python you use to create content. Moya has a language of its own for that, know as Moya Code.

Show Me the Code!

For the show me the code developers, here is an example I wrote to solve the challenging Fizz Buzz problem:

<moya>

    <macro docname="main">
        <for src="1..100" dst="number">
            <switch>
                <case if="number % 15 == 0"/>
                <echo>FizzBuzz</echo>

                <case if="number % 3 == 0"/>
                <echo>Fizz</echo>

                <case if="number % 5 == 0"/>
                <echo>Buzz</echo>

                <default-case/>
                <echo>${number}</echo>
            </switch>
        </for>
    </macro>

</moya>

If you're not a fan of XML, you might just be reaching for the close tab button about now, but hear me out. The code above is possibly verbose due to the XML syntax, but quite readable. You will recognize it at has at least statements, expressions, loops. It is actually a complete language, with the data types and constructs you would expect from a modern language.

Moya Code can be used in a standalone fashion (you can run that script from the command line with moya run fizzbuzz.xml) but it isn't going to replace Python or your go-to scripting language. Moya's sweet spot is high-level glue code between other systems. The kind of code you write day to day when building web applications.

This next snippet is a view (called to generate a response from a URL), which creates a new post in this very blog:

<view libname="view.new-post" content="#content.edit-post" requires=".permissions.admin">
    <forms:get form="#form.newpost" dst="form"/>
    <forms:validate src="form">
        <db:create model="#Post" obj="form.data" dst="post"
            let:user=".user"
            let:slug="form.data.slug or slug:form.data.title"
            let:published_date=".now"
            let:published="form.data.action == 'publish'"/>
        <redirect name="list"/>
    </forms:validate>
</view>

There is a lot of functionality packed in to this snippet. I'm going to gloss over the details (there is documentation for that), but in essence the above code performs the following steps:

  1. Check if the user is an administrator. If not respond with a forbidden error
  2. Get a form object (used to render, validate and process HTML forms)
  3. Validate the form if the current request is a POST request
  4. If it validates, create a post object in the database from the form data, generate a slug, set the published date and the current user
  5. Redirect to the recent posts page

Virtually one tag per step, and still quite readable.

Why Use Moya Code?

Hopeful I've made the case that you can build a web app with nothing but Moya Code, but why would you want to? Moya Code does eliminate a few of the minor annoyances with Python web frameworks. You don't have to import things in Moya (quick, what is the import statement for Django's reverse method?); there are no issues with circular imports; and very little in the way of boilerplate code; but these are somewhat incidental benefits.

Moya can detect many errors in your code before the server even runs. It can do this because Moya knows more about your project up-front than a Python web application would be able to. So you don't always need to execute the code to find bugs.

For errors that do occur at runtime, Moya's debug library displays nicely syntax highlighted stack traces, with plain English error messages. And the errors pinpoint the bug, rather than leaving you to decipher a traceback that may have occurred as a side effect of the real problem. Error detection and reporting is a focus of Moya, because it doesn't matter if the fix takes seconds, when finding the bug takes minutes. The minutes can add up to hours in a day, and days or weeks in a project.

Then there are Moya's expressions, which are superficially similar to Python and other high level languages, but have built in syntax for web related things. Here's an example that gets the current price of Bitcoin from an online service and displays it in the terminal:

<let btc="(fromjson:get:'https://www.bitstamp.net/api/ticker/')['ask']"/>
<echo>1 BTC is currently worth ${btc} dollars:</echo>

Here's another example, this time in Moya's template language. The following snippet generates a link to the next page, where the current page number is stored in the query string:

<a href="${urlupdate:(page=page+1)}">Next Page</a>

The link generated by this expression preserves the other values in the query string. So if you are rendering a page with a query string of ?q=tuna&sort=price&page=2, the above expression will return ?q=tuna&sort=price&page=3. Implementing this is not particularly difficult in any language, but in Moya, it just works and it is always available (in Moya code or templates)

Data types in Moya are designed to be as expressive as possible, in that you can glean a lot of information from them with attribute access. Here's an example that uses an expression to render an unordered list with the dates in the current month:

<ul>
{% for day in (.now.month_start)...(.now.next_month) %}
	<li>${localize:day.date}</li>
{% endfor %}
</ul>

The expression in the {% for %} tag creates an exclusive range between the date of the start of the month and the end of the month. Again, this is not something that would challenge most programmers, but in Moya it's effortless, and leaves you to focus on the more interesting problems.

But I Really Want to Use Python!

I don't blame you -- I love Python as a language, but in Moya, Python is generally only used to add new features. Most of Moya's built in libraries don't contain any Python code. For instance, the blog and comments system for this site is entirely Moya Code.

For situations where you do need to implement something in Python, Moya offers a number of ways to interface your Python code with Moya Code. The following example was taken from a URL shortening application, which encodes a primary key in base64 to save a few characters:

import moya

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"

@moya.expose.filter('encode_id')
def pack(i):
    letters = ''
    while i:
        letters += alphabet[i % 64]
        i //= 64
    return letters


@moya.expose.filter('decode_id')
def unpack(s):
    i = 0
    for c in reversed(s):
        i = i * 64 + alphabet.index(c)
    return i

This Python code creates two filters, one to encode an ID and one to decode. It could have been done in Moya Code, but the Python was a better fit for this case.

Here's the view that handles a short URL and redirects to the original URL (note the use of the 'decode_id' filter):

<view libname="view.redirect">
    <db:get-required model="#ShortURL" let:id="url.urlkey | 'decode_id'" dst="shorturl"/>
    <inc dst="shorturl.visitcount" />
    <redirect-to url="${shorturl.url}" />
</view>

If you are interested, the rest of the code is on GitHub. Other than the filters, the entire URL shortener application is implemented in a single file.

It is also possible to create entirely new tags or language features with Python extensions. But I'll save that for another post.

Content is King

Another of Moya's systems that could make a big impact on your work-flow is content. This is Moya's answer to the tag soup that is often the result of even a well designed system that uses only templates.

Content is a high level description of a page. It is an XML definition that specifies what will be rendered in a given page, but not how it should be rendered. Separating concerns in this way helps to keep your code manageable.

Let's have a look at some content. The following was taken from Moya's built in FAQ application:

<content libname="content.faqlist" template="faqs.html">
    <title>Frequently Asked Questions</title>
    <section name="body">
        <for src="faqs" dst="qa">
            <faq:faq qa="qa" />
        </for>
    </section>
    <section name="admin_links" if=".permissions.admin">
        <w:link url="${.appurls.createfaq}" icon="star">New question</w:link>
    </section>
</content>

Without going in to the details we can see this content definition divides the page in to a number of sections. A section typically corresponds to an area on the page, which could be a column, a footer or a non-visible area such as CSS in the head tag. The tags inside sections are responsible for generating HTML in the page.

Note the use of a tag called <faq:faq>. This is an example of a custom widget. Widgets ultimately render a template with the data you pass via attributes, but they also have the capacity to process data and pull in CSS, Javascript, and other external files entirely automatically.

Because widgets are self contained, you can easily move them from one part of the page to another or even to a different page entirely, and they will work as expected. No cutting and pasting markup, fixing URLs, or copying media required.

Let's look at another widget. Moya has a rich text editor widget which uses WYSIHTML5. This is how you would replace a textarea in a form with this editor:

<field name="text" label="Text" src="text">
    <wysihtml5:editor/>
</field>

The field tag is part of Moya's form system (I'll leave that for another post). The presence of the <wysihtml5:editor> tag pulls in two external CSS files, two Javascript files, and generates an embedded script tag to set things up. The result is that the form contains an editor that supports bold, italic, links etc.

What Next?

There is plenty more I could cover, but this is a good point to end my pitch. I'll leave the form system, database expressions, JSONRPC support, templated email, and the automatically generated admin site for another post. In the mean time, you might want to check out the tutorial or go straight to the documentation.

Recent Posts

Moya 0.6.0 Released

I'm happy to announce the release of Moya version 0.6.0. This version contains a number of new […]

Creating a Wiki with Moya - Screencast

I'm starting a series of screencasts on how do build a web application in Moya. In part 1, I show […]

Moya 0.5.14 Released

I'm pleased to announce the release of Moya 0.5.14. Moya is a web application platform written in […]

Moya Context Python Interface

The Context class is an interesting data-structure Moya uses to implement much of the features of […]

Moya 0.5.12 released!

Moya 0.5.12 was recently released. Moya is a language and web application server built in Python. […]

Moya discussion group

I've just create a Google discussion group for Moya. Sign up if you are interested in this project. […]

Moya Package Index

Just live is Moya Package Index, a site where you can find and download packages for Moya. If […]

Encrypted Notes with Moya

Encrypted Notes is an application written in Moya, and only the second Moya website online (the […]

A BrainF*** interpreter written in Moya

Have you heard of Brainf***? It's an esoteric programming language, with only 8 commands. It's […]

Moya is a new web development platform written in Python

In this post I'm going to try and pitch Moya to you, Dragon's Den style. Moya has been my hobby […]

Markdown in Posts

This is actually my second post, I deleted the first post because I changed the default markup from […]