Skip to content

Commit

Permalink
Tuto 8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
balat committed Sep 13, 2024
1 parent 9b1d828 commit 2618c9a
Show file tree
Hide file tree
Showing 143 changed files with 15,761 additions and 0 deletions.
1,170 changes: 1,170 additions & 0 deletions tutos/8.0/manual/application.wiki

Large diffs are not rendered by default.

797 changes: 797 additions & 0 deletions tutos/8.0/manual/basics-server.wiki

Large diffs are not rendered by default.

Empty file added tutos/8.0/manual/basics.md
Empty file.
1,245 changes: 1,245 additions & 0 deletions tutos/8.0/manual/basics.wiki

Large diffs are not rendered by default.

116 changes: 116 additions & 0 deletions tutos/8.0/manual/chat.wiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
= Chat: Design Overview =

<<wip|Warning this tutorial is not maintained and is not up to date.>>

//Chat// is a chatting module and application, currently for conversations
between two users each. (Extension for multi-user channels is left as an
exercise to the user.)

You can find the code [[https://github.com/ocsigen/tutorial/tree/master/src-examples|here]].

<<|* [[site:chat|try it]]! And
* [[site:darcsweb/?r=tutorial;a=tree;f=/src-examples/chat|browse it]],
* ##darcs get http://ocsigen.org/darcs/tutorial; cd tutorial/src-examples/chat## it, or
* [[site:download/chat.tar.gz|download]] the source code.
>>

== Description ==

When the user is logged in, he sees a list of available users. He can choose
one of them to start a conversation with him. The conversation then becomes
visible to both users as a message prompt and a growing list of messages. The
participating users can enter messages and they are made visible to the other
participants. If a user becomes unavailable for //Chat//, he is removed from the
list of available users, and all conversations he was participating are removed
from the opponent's UI.

== Central Data Types ==

The following data are crucial to the functionality of //Chat//.

=== Conversation ===
A conversation (##type Shared.Conversation.t##) is shared between all its
client participants. It comprises of two values: Firstly, it contains a bus for
transferring conversation messages (c.f. ##type Shared.Conversation.message##)
between the participants. Those are displayed as they come on every client.
Secondly, it contains the set of participating users. This is used when the
conversation is teared down: An event for removing the UI element for the
conversation is sent to every participant.

=== Client Process ===
When a user enters a web page which contains //Chat//, it is rendered in HTML and
a new client process is created in doing so. Every client process holds a
channel, where the server can send messages to //add or to remove conversations//
(c.f. ##type Shared.event##).

=== User Info ===
On the server side, the channel for those events is stored for each user along
with a list of conversations he participates in. This is done with a
Eliom-reference of scope ##session_group##, such that it is shared among all
sessions of a user (and likewise all client processes).

But, when one user requests to start (or end) a conversation with any another
user, it is also necessary to access the event channel of any user.
As it is only stored in an Eliom-reference specific to the user, it is
impossible to access. In favour of this, the user info is moreover
stored in a weak hash table (c.f. module ##Weak_info##) which associates the
user info to each user in a globally accessible way. This association is weak
in its value, the user info, such that it is garbage-collected as soon as the
Eliom-reference storing it is cleared, i.e. when the last session of the
respective user is closed.


=== Available Users ===
The client's UI contains a list of users which are currently available for
chatting. This is achieved as follows.

If it is the user's first client process he is added to the set of available
users, which is available in an react signal ##Chat.users_signal## on a set of
user (i.e. ##User_set.t React.S.t##).
As long as a user is available with at least one client he is kept in the value
of the signal. This signal is send to the client when initializing //Chat//.
Changes in its value are then directly reflected in the list of available users
in the client's UI (c.f. ##Client.change_users##).

To observe when a user is not available through a given client process anymore,
//Chat// awaits for every client process that it is inactive for a given
time. ##Eliom_comet.Channels.wait_timeout## is used for for detecting a client
process to be abandoned.

When it is observed that a user isn't avaible through any client process
anymore, he is removed from the ##Chat.users_signal##, and all conversation he
is participating are teared down (c.f. ##Chat.remove_client_process##).

=== Starting a conversation ===
When the user selects one of the available users for a conversation, one of two
things may happen (c.f. ##Client.create_or_focus_conversation##):

Firstly, if there is already a conversation between exactly those two users,
the message prompt is focused.
Secondly, if there is no conversation between those users yet, a new one is created
by calling the server's ##Chat.create_dialog_service## (dialog is a conversatio
of two) with the selected user as argument.

This service establishes the conversation between the users: It creates the
conversation on the server side (i.e. the set of participants and the bus for
messages) and sends an event to append the conversation to each participant.
Each client is streaming such events (c.f. ##Client.dispatch_event##) and
creates the respective UI-element for the conversation with event handlers for
sending and receiving messages in the conversation.

=== Sending a message in a conversationg ===
Sending messages within an conversation is achieved without any server-side
code; the messages are sent "directly" to all all participants of the
conversation //through its Eliom-bus//. Two things make this work.

Firstly, an event listener on the message prompt of the conversation
(c.f. ##Client.handle_enter_pressed##) awaits that the enter-key is hit in
the message prompt of a conversation. It then creates a message and sends it
to the conversation's event bus.

Secondly, every participant of a conversation is streaming the messages which
are sent over the conversation bus.
A stream iterator on that bus then displays those messages as they arrive
(c.f. ##Client.dispatch_message##).

Have fun. [[site:chat|Be communicative!]]
89 changes: 89 additions & 0 deletions tutos/8.0/manual/custom-conf.wiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
=Custom configuration options=
<<concepts |Custom configuration options>>

It is not convenient to have to edit the code to change some
configurations, like the location where are saved the favorite
images in the Graffiti tutorial
(see: [[wiki:pictures|Saving favorite pictures]]).
Fortunately Ocsigen provides a mechanism to extend its
configuration file.

==Basic interface==

<<code language="ocaml"|
let static_dir =
match Eliom_config.get_config () with
| [Simplexmlparser.Element
("staticdir", [], [Simplexmlparser.PCData dir])] ->
dir
| [] ->
raise (Ocsigen_extensions.Error_in_config_file
("staticdir must be configured"))
| _ ->
raise (Ocsigen_extensions.Error_in_config_file
("Unexpected content inside config"))
>>

This will add a mandatory child to the eliom tag in the
configuration file:
{{{
<eliom module="path/to/your/module.cma">
<staticdir>/tmp/static</staticdir>
</eliom>
}}}


==New interface==

From Eliom 4.0 it is much easier to define configuration file extension.
Have a look at module <<a_api project="eliom" subproject="server"|module Eliom_config>>.
For instance, here is how you can add an element "<ldap>"
in the configuration file to store a list of LDAP servers your application
interacts with.


<<code language="ocaml"|

(** An LDAP server is characterized by an host and a port. *)
type ldap_configuration = {
mutable host : string;
mutable port : int;
}


(** We store a list of LDAP servers to interact with. *)
let ldap_servers = ref []

(** The user-defined extension of the configuration file. *)
let ldap_configuration = Ocsigen_extensions.Configuration.(
(** Default configuration *)
let config () = {
host = "";
port = 339;
}
in
let init () =
ldap_servers := config () :: !ldap_servers
in
let current () =
List.hd !ldap_servers
in

(** Parsing functions. *)
let req_attr name = attribute ~name ~obligatory:true
and opt_attr name = attribute ~name ~obligatory:false
in
let name = "LDAP"
and obligatory = false
and attributes = [
req_attr "host" (fun h -> (current ()).host <- h);
opt_attr "port" (fun p -> (current ()).port <- int_of_string p);
);
]
in
element ~init ~name ~obligatory ~attributes ()
)

let _ = Eliom_config.parse_config [ldap_configuration]

>>
Binary file added tutos/8.0/manual/files/2014-epita-video.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions tutos/8.0/manual/files/screenshots/.directory
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[Dolphin]
PreviewsShown=true
Timestamp=2016,12,12,17,5,19
Version=3
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tutos/8.0/manual/files/screenshots/start1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tutos/8.0/manual/files/screenshots/start2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tutos/8.0/manual/files/screenshots/start3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tutos/8.0/manual/files/screenshots/start4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tutos/8.0/manual/files/screenshots/start5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tutos/8.0/manual/files/screenshots/start6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tutos/8.0/manual/files/tutorial/chapter1/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This code isn't maintened anymore and might not work. Please go to https://github.com/ocsigen/graffiti/simple for the latest working versions.
Loading

0 comments on commit 2618c9a

Please sign in to comment.