Skip to content

Commit

Permalink
[doc] book: Added hello_wiki_mongo.opa example.
Browse files Browse the repository at this point in the history
  • Loading branch information
nrs135 committed Nov 24, 2011
1 parent d17bb06 commit 5e3ab99
Showing 1 changed file with 196 additions and 0 deletions.
196 changes: 196 additions & 0 deletions doc/book/hello_wiki/hello_wiki_mongo.opa
@@ -0,0 +1,196 @@
/**
* {1 Import the high-level MongoDB API module}
*
* For the low-level API see the package stdlib.apis.mongo
*/
import stdlib.apis.{mongo}

/**
* {1 Import standard classes of bootstrap css}
*
* see http://twitter.github.com/bootstrap/
*/
import stdlib.themes.bootstrap

/**
* {1 Import templates}
*/
import stdlib.web.template

/**
* {1 Database and database interaction}
*/

/**
* The basic info. about the database and table location.
*/
type page = {
_id : string;
_rev : Bson.int32;
content : string;
}

/**
* We work at level 1, run-time type-checked storage of a collection of OPA values.
*
* The Mongo.pkg type provides convenience functions for building select and update documents.
**/
(wiki_collection,wiki_pkg) = (MongoCollection.openpkgfatal("default","db","wiki"):Mongo.pkg(page))
pageselect(v) = wiki_pkg.select(Bson.opa2doc(v))
pageupdate(v) = wiki_pkg.update(Bson.opa2doc(v))

/**
* Indexes aren't automatic in MongoDB apart from the non-removable _id index.
* Since we're searching on _rev as well, we need a separate index.
**/
_ = MongoCollection.create_index(wiki_collection, "db.wiki", Bson.opa2doc({_id=1; _rev=1}), 0)

/**
* Retrieves a json document from the database
*
* @param docid The id of the document to retrieve (arbitrary string)
* @return A Template.content
*/
get_content(docid) =
default = Template.text("This page is empty. Double-click to edit.")
extract_content(record:page) = record.content
// Order by reverse _rev to get highest numbered _rev.
orderby = {some=Bson.opa2doc({_rev=-1})}
match MongoCollection.find_one(MongoCollection.orderby(wiki_collection,orderby),pageselect({_id=docid})) with
| {success=page} ->
source = extract_content(page)
(match Template.try_parse(Template.default, source) with
| {~success} -> success
| {failure=_} -> Template.text(source))
| {failure={NotFound}} ->
default
| {~failure} ->
do jlog("hello_wiki_mongo: failure={MongoDriver.string_of_failure(failure)}")
default

/**
* Read the content associated to a topic from the database and return the
* corresponding source code.
*
* @param topic A topic (arbitrary string).
* @return If a page has been saved in for [topic], the source code for this
* page. Otherwise, the source code for the default page.
*/
@publish load_source(topic) =
Template.to_source(Template.default, get_content(topic))

/**
* Read the content associated to a topic from the database and return the
* corresponding xhtml, ready to insert.
*
* @param topic A topic (arbitrary string).
* @return If a page has been saved in for [topic], the xhtml for this
* page. Otherwise, the xhtml for the default page.
*
* Note: This function does not perform any caching.
*/
@publish load_rendered(topic) =
Template.to_xhtml(Template.default, get_content(topic))

/**
* Accept source code, save the corresponding document in the database.
*
* @param topic A topic (arbitrary string).
* @param source Source code to store at this topic. If this source code
* is syntactically valid, store the template datastructure
* corresponding to its content [Template.content].
* Otherwise, the source code is implicitly replaced by the document
* representing this raw code and this document is saved in the database.
*
* @return In case of success, the xhtml for the page that has just been
* saved. In case of failure, an error message.
*/
@publish save_source(topic, source) =
select = pageselect({_id=topic})
update = pageupdate({`$set`={content=source}; `$inc`={_rev=(1:Bson.int32)}})
// Upsert this so we create it if it isn't there
result = MongoCollection.updatee(MongoCollection.upsert(wiki_collection),select,update)
if MongoDriver.is_error(result)
then <>Error: {MongoDriver.pretty_of_result(result)}</>
else
match Template.try_parse(Template.default, source) with
| ~{success} -> Template.to_xhtml(Template.default, success)
| {failure = _} -> <>Error: {source}</>

/**
* {1 User interface}
*/

/**
* Set the user interface in edition mode.
*
* Load the source code for a topic, display an editable zone for this source code.
*
* @param topic The topic to edit.
*/
edit(topic) =
do Dom.set_value(#edit_content, load_source(topic))
do Dom.hide(#show_content)
do Dom.show(#edit_content)
do Dom.give_focus(#edit_content)
void

/**
* Set the user interface in reading mode.
*
* Save the source code for a topic (extracted from [#edit_content]),
* display the rendered version.
*
* @param topic The topic to save.
*/
save(topic) =
content = save_source(topic, Dom.get_value(#edit_content))
do Dom.transform([#show_content <- content])
do Dom.hide(#edit_content)
do Dom.show(#show_content)
void

/**
* Main user interface
*
* @param topic The topic being consulted
* @return A resource, ready to be passed to a dispatcher.
*/
display(topic) =
Resource.styled_page("About {topic}", ["/resources/css.css"],
<div class="topbar"><div class="fill"><div class="container">
<div id=#logo></div>
</div></div></div>
<div class="content container">
<div class="page-header"><h1>About {topic}</></>
<div class="well" id=#show_content ondblclick={_ -> edit(topic)}>
{load_rendered(topic)}
</>
<textarea rows="30" id=#edit_content onblur={_ -> save(topic)}></>
</div>
)

/**
* {1 Main application}
*/

/**
* Dispatch requests to the user interface
*
* Note: The empty request is dispatched as if it were "Hello".
*/
start =
| {path = [] ... } ->
display("Hello")
| {~path ...} ->
display(String.capitalize(String.to_lower(String.concat("::", path))))

/**
* Statically embed a bundle of resources
*/
server = Server.of_bundle([@static_include_directory("resources")])

/**
* Launch the [start] dispatcher
*/
server = Server.simple_dispatch(start)

0 comments on commit 5e3ab99

Please sign in to comment.