Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[doc] book: updated chat and wiki (back to sync)

  • Loading branch information...
commit 343e1e045e9bf5168c6ca634c92f44f632b04cd6 1 parent fc55a97
François-Régis Sinot authored
View
95 doc/book/hello_chat/hello_chat.adoc
@@ -44,6 +44,21 @@ In this listing, we define the communication infrastructure for the chatroom,
the user interface, and finally, the main application. In the rest of the
chapter, we will walk you through all the concepts and constructions introduced.
+A bit of style
+~~~~~~~~~~~~~~
+
+Before exposing the real machinery, let's care from the start how the
+apllication should look like, with this single import line:
+
+[source,opa]
+------------------------
+import stdlib.themes.bootstrap
+------------------------
+
+This automatically brings http://twitter.github.com/bootstrap/[Bootstrap CSS
+from Twitter] to your application, so you can use the predefined classes that
+will just look nice.
+
Setting up communications
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -138,18 +153,20 @@ For starters, consider a possible skeleton for the user interface:
.Skeleton of the user interface (incomplete)
[source,opa]
------------------------
-<div id=#header><div id=#logo></div></div>
-<div id=#conversation></div>
-<input id=#entry/>
-<div class="button">Send!</div>
+<div class="topbar"><div class="fill"><div class="container"><div id=#logo /></div></div></div>
+<div id=#conversation class="container"></div>
+<div id=#footer><div class="container">
+ <input id=#entry class="xlarge"/>
+ <div class="btn primary" >Post</div>
+</div></div>
------------------------
-If you are familiar with HTML, you will recognize easily that this skeleton defines
-a few boxes (or +<div>+), called respectively +header+, +logo+ and +conversation+ and
-one without a name but with a style class +button+,
-as well as a text input zone (or +<input>+) called +entry+. We will use these names to add
-interactions and style. If you are not familiar with HTML, it might be a good idea to
-grab https://developer.mozilla.org/En/HTML[a good HTML reference] and check up the tags as you see them.
+If you are familiar with HTML, you will recognize easily that this skeleton
+defines a few boxes (or +<div>+), with some names (or +id+) and some classes, as
+well as a text input zone (or +<input>+) called +entry+. We will use these names
+to add interactions and style. If you are not familiar with HTML, it might be a
+good idea to grab https://developer.mozilla.org/En/HTML[a good HTML reference]
+and check up the tags as you see them.
Actually, for convenience, and because it fits with the rest of the library, we will
put this user interface inside a function, as follows:
@@ -159,10 +176,12 @@ put this user interface inside a function, as follows:
------------------------
start() =
(
- <div id=#header><div id=#logo></div></div>
- <div id=#conversation></div>
- <input id=#entry/>
- <div class="button">Send!</div>
+ <div class="topbar"><div class="fill"><div class="container"><div id=#logo /></div></div></div>
+ <div id=#conversation class="container"></div>
+ <div id=#footer><div class="container">
+ <input id=#entry class="xlarge"/>
+ <div class="btn primary" >Post</div>
+ </div></div>
)
------------------------
@@ -209,10 +228,12 @@ At this stage, we can already go a bit further and invent an author name, as fol
start() =
(
author = Random.string(8)
- <div id=#header><div id=#logo></div></div>
- <div id=#conversation></div>
- <input id=#entry/>
- <div class="button">Send!</div>
+ <div class="topbar"><div class="fill"><div class="container"><div id=#logo /></div></div></div>
+ <div id=#conversation class="container"></div>
+ <div id=#footer><div class="container">
+ <input id=#entry class="xlarge"/>
+ <div class="btn primary" >Post</div>
+ </div></div>
)
------------------------
@@ -270,9 +291,11 @@ denotes the manipulation of the contents of a web page once that page is display
in the browser.
=======================
-Speaking of types, it is generally a good idea to know the type of functions. Function +broadcast+
-has type +string \-> void+, meaning that it takes an argument with type +string+ and produces a
-value with type +void+. We could have written just as well:
+Speaking of types, it is generally a good idea to know the type of
+functions. Function +broadcast+ has type +string \-> void+, meaning that it
+takes an argument with type +string+ and produces a value with type
++void+. Also, writing +{author=author text=text}+ is a bit painful, so we added
+a syntactic sugar for this. We could have written just as well:
.Broadcasting a message to the room (variant)
[source,opa]
@@ -280,7 +303,7 @@ value with type +void+. We could have written just as well:
broadcast(author: string): void =
(
text = Dom.get_value(#entry)
- message = {author=author text=text}
+ message = {~author ~text}
do Network.broadcast(message, room)
Dom.clear_value(#entry)
)
@@ -301,9 +324,11 @@ that we want to be called whenever the network propagates a message:
------------------------
user_update(x: message) =
(
- line = <div class="line">
- <div class="user">{x.author}:</div>
- <div class="message">{x.text}</div>
+ line = <div class="row line">
+ <div class="span1 columns userpic" />
+ <div class="span2 columns user">{x.author}:</div>
+ <div class="span13 columns message">{x.text}
+ </div>
</div>
do Dom.transform([ #conversation +<- line ])
Dom.scroll_to_bottom(#conversation)
@@ -354,10 +379,12 @@ Let us connect +broadcast+ to our button and our input. This changes function +s
start() =
(
author = Random.string(8)
- <div id=#header><div id=#logo></div></div>
- <div id=#conversation></div>
- <input id=#entry onnewline={_ -> broadcast(author)}/>
- <div class="button" onclick={_ -> broadcast(author)}>Send!</div>
+ <div class="topbar"><div class="fill"><div class="container"><div id=#logo /></div></div></div>
+ <div id=#conversation class="container"></div>
+ <div id=#footer><div class="container">
+ <input id=#entry class="xlarge" onnewline={_ -> broadcast(author)}/>
+ <div class="btn primary" onclick={_ -> broadcast(author)}>Post</div>
+ </div></div>
)
------------------------
@@ -389,10 +416,12 @@ the user loads the page, as follows:
start() =
(
author = Random.string(8)
- <div id=#header><div id=#logo></div></div>
- <div id=#conversation onready={_ -> Network.add_callback(user_update, room)}></div>
- <input id=#entry onnewline={_ -> broadcast(author)}/>
- <div class="button" onclick={_ -> broadcast(author)}>Send!</div>
+ <div class="topbar"><div class="fill"><div class="container"><div id=#logo /></div></div></div>
+ <div id=#conversation class="container" onready={_ -> Network.add_callback(user_update, room)}></div>
+ <div id=#footer><div class="container">
+ <input id=#entry class="xlarge" onnewline={_ -> broadcast(author)}/>
+ <div class="btn primary" onclick={_ -> broadcast(author)}>Post</div>
+ </div></div>
)
------------------------
View
4 doc/book/hello_chat/hello_chat.opa
@@ -50,7 +50,9 @@ user_update(x: message) =
* @param author The name of the author. Will be included in the message broadcasted.
*/
broadcast(author) =
- do Network.broadcast({~author text=Dom.get_value(#entry)}, room)
+ text = Dom.get_value(#entry)
+ message = {~author ~text}
+ do Network.broadcast(message, room)
Dom.clear_value(#entry)
/**
View
37 doc/book/hello_wiki/hello_wiki.adoc
@@ -6,14 +6,6 @@ In itself, developing a simple wiki is error-prone but otherwise not very diffic
developing a rich wiki that both scales up and does not suffer from vulnerabilities caused by
the content provided by users is quite harder. Once again, Opa makes it simple.
-
-
-//
-// About this chapter:
-// Main author: David
-// Paired author:?
-//
-
In this chapter, we will see how to program a complete, albeit simple, wiki
application in Opa. Along the way, we will introduce the Opa database, the
client-server security policy, the mechanism used to incorporate user-defined
@@ -42,6 +34,8 @@ safe format, we define the user interface and finally, the main application. In
the rest of the chapter, we will walk you through all the concepts and
constructions introduced.
+As for the chat, we use http://twitter.github.com/bootstrap/[Bootstrap CSS from
+Twitter] with a single import line.
Setting up storage
~~~~~~~~~~~~~~~~~~
@@ -95,13 +89,14 @@ makes manipulation of data easier:
[source,opa]
-----------------
-db /wiki[_] = Template.text("This page is empty")
+db /wiki[_] = Template.text("This page is empty. Double-click to edit.")
-----------------
The square brackets +[_]+ are a convention to specify that we are talking about
-the contents of a map and, more precisely, providing a default value. Here,
-the default value is +Template.text("This page is empty")+, i.e. a simple
-text in our templating system. As expected, it has type +Template.default_content+.
+the contents of a map and, more precisely, providing a default value. Here, the
+default value is +Template.text("This page is empty. Double-click to edit.")+,
+i.e. a simple text in our templating system. As expected, it has type
++Template.default_content+.
With these two lines, the database is set. Any data written to the database will
be kept persistent, versioned and snapshoted regularly. Should you stop and
@@ -255,9 +250,12 @@ As previously, we define a function to produce the user interface:
display(topic) =
(
Resource.styled_page("About {topic}", ["/resources/css.css"],
- <div id=#header><div id=#logo></div>About {topic}</div>
- <div class="show_content" id=#show_content ondblclick={_ -> edit(topic)}>{load_source(topic)}</>
- <textarea class="edit_content" id=#edit_content style="display:none" cols="40" rows="30" onblur={_ -> save(topic)}></>
+ <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 clas="xxlarge" rows="30" id=#edit_content onblur={_ -> save(topic)}></>
+ </div>
)
)
-----------------
@@ -330,7 +328,7 @@ To get this to work, we need to define a function +start+ as follows:
start(uri) = (
match uri with
| {path = {nil} ...} -> display("Hello")
- | {path = path ...} -> display(List.to_string_using("", "", "::", path))
+ | {path = path ...} -> display(String.concat("::", path))
)
----------------
@@ -349,13 +347,14 @@ pattern did not match, for instance on a request to "http://localhost:8080/hello
In both cases, we execute function +display+. The first case is trivial, while
in the second case, we first convert our list to a string, with separator +"::"+.
-Actually, we will make it a tad nicer by also ensuring that the first letter is uppercase.
-Along the way, we can make this function shorter:
+Actually, we will make it a tad nicer by also ensuring that the first letter is
+uppercase, while the other letters are lowercase. Along the way, we can make
+this function shorter:
[source,opa]
----------------
start =
| {path = [] ...} -> display("Hello")
- | {~path ...} -> display(String.capitalize(List.to_string_using("", "", "::", path))
+ | {~path ...} -> display(String.capitalize(String.to_lower(String.concat("::", path))))
----------------
In this new version, we use a shorter syntax for pattern-matching. We use +[]+
View
17 doc/book/hello_wiki/hello_wiki.opa
@@ -5,6 +5,9 @@
*/
import stdlib.themes.bootstrap
+/**
+ * {1 Import templates}
+ */
import stdlib.web.template
/**
@@ -41,7 +44,7 @@ db /wiki[_] = Template.text("This page is empty. Double-click to edit.")
*
* Note: This function does not perform any caching.
*/
-@publish load_rendered(topic) = Template.to_xhtml( Template.default, /wiki[topic])
+@publish load_rendered(topic) = Template.to_xhtml(Template.default, /wiki[topic])
/**
* Accept source code, save the corresponding document in the database.
@@ -107,12 +110,10 @@ save(topic) =
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"><h1>About {topic}</h1>
- <div id=#show_content ondblclick={_ -> edit(topic)}>
- {load_rendered(topic)}
- </>
- <textarea class="edit_content" id=#edit_content style="display:none"
- cols="40" rows="30" onblur={_ -> save(topic)}></>
+ <div class="content container">
+ <div class="page-header"><h1>About {topic}</></>
+ <div class="well" id=#show_content ondblclick={_ -> edit(topic)}>{load_rendered(topic)}</>
+ <textarea clas="xxlarge" rows="30" id=#edit_content onblur={_ -> save(topic)}></>
</div>
)
@@ -129,7 +130,7 @@ start =
| {path = [] ... } ->
display("Hello")
| {~path ...} ->
- display(String.capitalize(String.to_lower(List.to_string_using("", "", "::", path))))
+ display(String.capitalize(String.to_lower(String.concat("::", path))))
/**
* Statically embed a bundle of resources
View
27 doc/book/hello_wiki/hello_wiki_simple.opa
@@ -1,3 +1,13 @@
+/**
+ * {1 Import standard classes of bootstrap css}
+ *
+ * see http://twitter.github.com/bootstrap/
+ */
+import stdlib.themes.bootstrap
+
+/**
+ * {1 Import templates}
+ */
import stdlib.web.template
/**
@@ -11,7 +21,7 @@ import stdlib.web.template
* Note: By definition, pages stored in the database are always well-formed.
*/
db /wiki: stringmap(Template.default_content)
-db /wiki[_] = Template.text("This page is empty")
+db /wiki[_] = Template.text("This page is empty. Double-click to edit.")
/**
@@ -99,12 +109,12 @@ save(topic) =
*/
display(topic) =
Resource.styled_page("About {topic}", ["/resources/css.css"],
- <div id=#header><div id=#logo></div>About {topic}</div>
- <div class="show_content" id=#show_content ondblclick={_ -> edit(topic)}>
- {load_rendered(topic)}
- </>
- <textarea class="edit_content" id=#edit_content style="display:none"
- cols="40" rows="30" onblur={_ -> save(topic)}></>
+ <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 clas="xxlarge" rows="30" id=#edit_content onblur={_ -> save(topic)}></>
+ </div>
)
/**
@@ -120,7 +130,7 @@ start =
| {path = [] ... } ->
display("Hello")
| {~path ...} ->
- display(String.capitalize(String.to_lower(List.to_string_using("", "", "::", path))))
+ display(String.capitalize(String.to_lower(String.concat("::", path))))
/**
* Statically embed a bundle of resources
@@ -131,4 +141,3 @@ server = Server.of_bundle([@static_include_directory("resources")])
* Launch the [start] dispatcher
*/
server = Server.simple_dispatch(start)
-
View
14 doc/book/hello_wiki/resources/css.css
@@ -7,15 +7,7 @@
}
/***Editing area***/
.content {margin-top:60px;}
-.edit_content {
- background: #fcfcfc;
- padding:0;
- width:100%;
-}
-dt {
- font-weight: bold;
-}
-
-dd {
- text-indent: 5px;
+#edit_content {
+ width:100%;
+ display: none; /* initially hidden */
}
Please sign in to comment.
Something went wrong with that request. Please try again.