Morepath 0.6 released!
What's Morepath? Morepath is your friendly neighborhood web framework with super powers. It lets you easily create links between resources, and offers a range of mechanisms that allow you to better organize and reuse code. Morepath is geared towards this modern age of the web where more and more UI logic is moving into JavaScript, into the browser -- it does this by being great at creating RESTful hypermedia APIs.
Besides a few documentation fixes, Morepath 0.6 has a minor improvement and a major improvement.
Both improvements have to do with a relatively obscure use case that I ran into lately. Application composition should be an important feature in a modern web framework, and so does linking, but we only rarely see things like this. That we run into use cases like this goes to show just how far Morepath is ahead in exploring this area. See nesting applications and linking to things in other apps for more information on these subsystems of Morepath.
The major improvement is the ability to link to other applications by the name under which they've been mounted into their parent. By default the name is the path under which they were mounted. Imagine you have the following URL space:
/v1/ /v1/a /v2/a
You can model this as two applications, A
and B
that are
mounted under a core application mounted at v1
. That would look
like this in Morepath:
class V1(morepath.App): pass # makes a root object exist under /v1 @app.path(path='/v1') class Root(object): pass class A(morepath.App): pass class B(morepath.App): pass // mounts everything in app A under /v1/a @V1.mount(app=A, path='a') def a_context(): return {} // mounts everything in app B under /v1/b @V1.mount(app=B, path='b') def b_context(): return {}
Consider how you'd make a link from app A
to a resource in app
B
given this setup. In Morepath before 0.6, you'd have to write:
request.parent.child(B).link(obj)
This would create a link to whatever obj
is (which depends on its
path), for instance:
/v1/b/items/3
The minor improvement is that we realized the .parent.child
combination happens a lot and we've introduced a new sibling method
to combine them in one step:
request.sibling(B).link(obj)
Now considers what happens when a new incompatible version of your
overall API arises, because you've changed something fundamentally in
app B
. Perhaps items appear on a /foos
path instead of an
/items
path, like:
/v2/b/foos/3
You've not changed anything in app A
though. What
you'd like to do is mount the new B
and the old A
into a
V2
app and have everything work as expected:
class V2(morepath.App): pass // mounts everything in app A under /v2/a @V2.mount(app=A, path='a') def a_context(): return {} // mounts everything in app NewB under /v2/b @V2.mount(app=NewB, path='b') def b_context(): return {}
But this is problematic, as we have a hardcoded dependency on app
B
in app A
in the link generation code. Now we'd like to link
to app NewB
instead of B
. But we'd want the original v1
URLs to still work as before, so we can't just modify app A
so to
include a link to NewB
. So in /v1/a
we'd like links to look
like this:
/v1/b/items/3
But in /v2/a
we'd like links to go to the new place in NewB
:
/v2/b/foos/3
The solution is the new ability to find mount applications by name
instead of by class. By the default the name is the same as the
path
argument you give in the mount
directive.
If you write linking code in app A
to read like this:
request.sibling('b').link(obj)
there is no more hardcoded dependency on app B
. Instead the system
now relies on the sibling app mounted under b
to create the link,
whatever it may be. And if A
is mounted under /v1
the sibling
will be B
, but if it's mounted under /v2
the sibling will be
NewB
. So the links will be correct in both cases, and we're saved!
Comments
Comments powered by Disqus