├── .gitattributes
├── LICENSE
├── README.md
├── eliom
├── tutorial
│ ├── 00100-hello-world
│ │ ├── README.md
│ │ └── src
│ │ │ └── mysite
│ │ │ ├── .ocp-indent
│ │ │ ├── Makefile
│ │ │ ├── Makefile.options
│ │ │ ├── README
│ │ │ ├── mysite.conf.in
│ │ │ ├── mysite.eliom
│ │ │ └── static
│ │ │ └── css
│ │ │ └── mysite.css
│ ├── 00200-static-table
│ │ ├── README.md
│ │ └── src
│ │ │ └── mysite
│ │ │ ├── .ocp-indent
│ │ │ ├── Makefile
│ │ │ ├── Makefile.options
│ │ │ ├── README
│ │ │ ├── config.eliom
│ │ │ ├── config.eliomi
│ │ │ ├── i18n.eliom
│ │ │ ├── i18n.eliomi
│ │ │ ├── id.eliom
│ │ │ ├── id.eliomi
│ │ │ ├── language.eliom
│ │ │ ├── language.eliomi
│ │ │ ├── model.eliom
│ │ │ ├── model.eliomi
│ │ │ ├── money.eliom
│ │ │ ├── money.eliomi
│ │ │ ├── mysite.conf.in
│ │ │ ├── mysite.eliom
│ │ │ ├── product.eliom
│ │ │ ├── product.eliomi
│ │ │ ├── static
│ │ │ └── css
│ │ │ │ └── mysite.css
│ │ │ ├── view.eliom
│ │ │ └── view.eliomi
│ ├── 00300-url-param
│ │ ├── README.md
│ │ └── src
│ │ │ └── mysite
│ │ │ ├── .ocp-indent
│ │ │ ├── Makefile
│ │ │ ├── Makefile.options
│ │ │ ├── README
│ │ │ ├── config.eliom
│ │ │ ├── config.eliomi
│ │ │ ├── i18n.eliom
│ │ │ ├── i18n.eliomi
│ │ │ ├── id.eliom
│ │ │ ├── id.eliomi
│ │ │ ├── language.eliom
│ │ │ ├── language.eliomi
│ │ │ ├── model.eliom
│ │ │ ├── model.eliomi
│ │ │ ├── money.eliom
│ │ │ ├── money.eliomi
│ │ │ ├── mysite.conf.in
│ │ │ ├── mysite.eliom
│ │ │ ├── product.eliom
│ │ │ ├── product.eliomi
│ │ │ ├── service.eliom
│ │ │ ├── service.eliomi
│ │ │ ├── static
│ │ │ └── css
│ │ │ │ └── mysite.css
│ │ │ ├── view.eliom
│ │ │ └── view.eliomi
│ ├── 00400-add-product
│ │ ├── README.md
│ │ └── src
│ │ │ └── mysite
│ │ │ ├── Makefile
│ │ │ ├── Makefile.options
│ │ │ ├── README
│ │ │ ├── config.eliom
│ │ │ ├── config.eliomi
│ │ │ ├── i18n.eliom
│ │ │ ├── i18n.eliomi
│ │ │ ├── id.eliom
│ │ │ ├── id.eliomi
│ │ │ ├── language.eliom
│ │ │ ├── language.eliomi
│ │ │ ├── model.eliom
│ │ │ ├── model.eliomi
│ │ │ ├── money.eliom
│ │ │ ├── money.eliomi
│ │ │ ├── mysite.conf.in
│ │ │ ├── mysite.eliom
│ │ │ ├── product.eliom
│ │ │ ├── product.eliomi
│ │ │ ├── service.eliom
│ │ │ ├── service.eliomi
│ │ │ ├── static
│ │ │ └── css
│ │ │ │ └── mysite.css
│ │ │ ├── view.eliom
│ │ │ └── view.eliomi
│ ├── Makefile
│ └── shared
│ │ ├── config.eliom
│ │ ├── config.eliomi
│ │ ├── i18n.eliom
│ │ ├── i18n.eliomi
│ │ ├── id.eliom
│ │ ├── id.eliomi
│ │ ├── language.eliom
│ │ ├── language.eliomi
│ │ ├── money.eliom
│ │ ├── money.eliomi
│ │ ├── product.eliom
│ │ └── product.eliomi
└── with-ocaml-vdom
│ └── simple
│ ├── .depend
│ ├── .ocp-indent
│ ├── Makefile
│ ├── Makefile.options
│ ├── README
│ ├── README.md
│ ├── mixvdomandeliom.conf.in
│ ├── mixvdomandeliom.eliom
│ └── static
│ └── css
│ └── mixvdomandeliom.css
└── jsoo
├── build-local-csv-file
├── blob.ml
├── blob.mli
├── build.sh
├── main.ml
├── textEncoder.ml
├── textEncoder.mli
└── www
│ ├── css
│ └── style.css
│ ├── index.html
│ └── js
│ ├── main.js
│ └── text-encoding
│ └── encoding.js
├── curriculum-vitae
├── _tags
├── action.ml
├── build.sh
├── controller.ml
├── img
│ ├── ID-100165338.jpg
│ ├── ID-100209817.jpg
│ ├── ID-100371977.jpg
│ └── darth-vader.jpg
├── index.html
├── js
│ └── main.js
├── main.ml
├── model.ml
├── rl.ml
├── style.css
├── types.ml
└── view.ml
├── lsystem
├── build.sh
├── lsystem.ml
└── www
│ ├── css
│ └── style.css
│ ├── index.html
│ └── js
│ └── lsystem.js
├── pico-spreadsheet
├── build.sh
├── spreadsheet.ml
└── www
│ ├── css
│ └── style.css
│ ├── index.html
│ └── js
│ └── spreadsheet.js
├── pong
├── build.sh
├── pong.ml
└── www
│ ├── css
│ └── style.css
│ ├── index.html
│ └── js
│ └── pong.js
├── tic-tac-toe
├── .gitignore
├── action.ml
├── build.sh
├── controller.ml
├── index.html
├── js
│ └── main.js
├── main.ml
├── model.ml
├── style.css
├── types.ml
└── view.ml
└── todomvc-react
├── .gitignore
├── .jshintignore
├── build.sh
├── index.html
├── js
└── todomvc.js
├── node_modules
├── todomvc-app-css
│ ├── index.css
│ ├── package.json
│ └── readme.md
└── todomvc-common
│ ├── base.css
│ ├── base.js
│ ├── package.json
│ └── readme.md
├── package.json
├── readme.md
└── todomvc.ml
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-vendored
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Stéphane Legrand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Examples with Eliom
3 |
4 | - Work in progress: [Eliom tutorial](https://github.com/slegrand45/examples_ocsigen/tree/master/eliom/tutorial/).
5 |
6 | - A very simple example of using [Eliom services](https://ocsigen.org/eliom/dev/manual/clientserver-communication) for the server side with [OCaml Vdom](https://github.com/LexiFi/ocaml-vdom) for the client side.
7 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/eliom/with-ocaml-vdom/simple/](https://github.com/slegrand45/examples_ocsigen/tree/master/eliom/with-ocaml-vdom/simple/)
8 |
9 |
10 | ## Examples with js_of_ocaml
11 |
12 | - A Reactive version of TodoMVC.
13 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/todomvc-react](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/todomvc-react)
14 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/todomvc-react/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/todomvc-react/)
15 |
16 | - A Reactive version of Tic-Tac-Toe game. The idea of this demo came from the talk ["Enterprise Tic-Tac-Toe"](http://fsharpforfunandprofit.com/ettt/) by Scott Wlaschin.
17 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/tic-tac-toe](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/tic-tac-toe)
18 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/tic-tac-toe/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/tic-tac-toe/)
19 |
20 | - Even :space_invader: Darth Vader :space_invader: uses js_of_ocaml. This is a port of the [Darth Vader Résumé](http://articles.novoresume.com/luke-who-is-searching-for-a-job/) from Novorésumé with a [Material Design template](http://demo.themesafari.net/materialize-responsive-resume/). The code uses the force and [Material Design Lite](http://www.getmdl.io).
21 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/curriculum-vitae](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/curriculum-vitae)
22 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/curriculum-vitae/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/curriculum-vitae/)
23 |
24 | - A Pong game. Inspired by the article ["Client-side haskell"](http://ifeanyi.co/posts/client-side-haskell/) written by Ifeanyi Ubah.
25 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/pong](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/pong)
26 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/pong/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/pong/)
27 |
28 | - Draw a plant with SVG and L-System: http://algorithmicbotany.org/papers/abop/abop.pdf
29 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/lsystem](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/lsystem)
30 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/lsystem/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/lsystem/)
31 |
32 | - Pico spreadsheet: editable cells with a reactive SVG graph and a reactive sums line.
33 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/pico-spreadsheet](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/pico-spreadsheet)
34 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/pico-spreadsheet/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/pico-spreadsheet/)
35 |
36 | - How to build and download a CSV file without any server processing and only from client side data.
37 | - Source: [https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/build-local-csv-file](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/build-local-csv-file)
38 | - Demo: [http://slegrand45.github.io/examples_ocsigen.site/jsoo/build-local-csv-file/](http://slegrand45.github.io/examples_ocsigen.site/jsoo/build-local-csv-file/)
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/README.md:
--------------------------------------------------------------------------------
1 | # Hello world!
2 |
3 |
4 | ## What you will learn
5 |
6 | In this first tutorial, we will see how to build this classical example. The goal is simply to display "Hello world!" on a web page.
7 |
8 |
9 | ## Prerequisites
10 |
11 | In order to complete this tutorial, you need to install at a bare minimum:
12 |
13 | - [The OCaml language](http://www.ocaml.org/docs/install.html)
14 | - The OCaml package manager [OPAM](https://opam.ocaml.org/doc/Install.html)
15 |
16 | This tutorial has been tested with OCaml version 4.03.0. So i would recommend to install this version or a greater one.
17 |
18 |
19 | ## Install Eliom
20 |
21 | The easiest way to install Eliom and all its dependencies is to use OPAM:
22 |
23 | ```
24 | $ opam install eliom
25 | ```
26 |
27 | This tutorial has been tested with Eliom version 6.2.0. So i would recommend to install this version or a greater one.
28 |
29 |
30 | ## Create the project
31 |
32 | Now that everything needed is installed, we can create our first project. Eliom provides a shell command named `eliom-distillery` in order to initialize a new project:
33 |
34 | ```
35 | $ eliom-distillery -name mysite -template basic.ppx
36 | ```
37 |
38 | This command creates a new project named `mysite`. The project directory is populated with some files coming from the predefined template named `basic.ppx`:
39 |
40 | ```
41 | $ cd mysite/
42 | $ ls
43 | Makefile Makefile.options mysite.conf.in mysite.eliom README static
44 | ```
45 |
46 |
47 | ## First test
48 |
49 | To test the application, we can run the Eliom server in test mode. You can use one of these commands:
50 |
51 | ```
52 | $ make test.byte
53 | ```
54 |
55 | or
56 |
57 | ```
58 | $ make test.opt
59 | ```
60 |
61 | The first command will compile the server in bytecode mode whereas the second one will compile the server in native executable mode.
62 |
63 | After the compilation, the Eliom server is automatically launched and ready to answer to client requests. Open your web browser and go to [http://localhost:8080/](http://localhost:8080/). You should see "Welcome from Eliom's distillery!".
64 |
65 |
66 | ## Modify the message
67 |
68 | Now we want to display our own message and say hello to the world. To change the default text, we need to edit the main source code file. By default, its name is the project name with the `.eliom` extension. So open the file named `mysite.eliom` with a text editor, change the text "Welcome from Eliom's distillery!" to "Hello world!" and save the file.
69 |
70 | Then we have to rebuild the application to take into account the change. If needed, stop the current server with `Ctrl+C`. Then rerun either `make test.byte` or `make test.opt`. Refresh the web page [http://localhost:8080/](http://localhost:8080/), you should now see "Hello world!".
71 |
72 | Congratulations, you have made your first Eliom application!
73 |
74 |
75 | ## Next step
76 |
77 | In the [next tutorial](../00200-static-table/), we will see how to create a much more interesting web page.
78 |
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/src/mysite/.ocp-indent:
--------------------------------------------------------------------------------
1 | normal
2 | with=0
3 | syntax=lwt mll
4 | max_indent=2
5 | ppx_stritem_ext=0
6 |
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/src/mysite/Makefile.options:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------------------------------------------------
3 | # SETTINGS FOR THE ELIOM PROJECT mysite
4 | #----------------------------------------------------------------------
5 |
6 | PROJECT_NAME := mysite
7 |
8 | # Source files for the server
9 | SERVER_FILES := $(wildcard *.eliomi *.eliom)
10 | # Source files for the client
11 | CLIENT_FILES := $(wildcard *.eliomi *.eliom)
12 |
13 | # OCamlfind packages for the server
14 | SERVER_PACKAGES := lwt.ppx js_of_ocaml.deriving.ppx
15 | # OCamlfind packages for the client
16 | CLIENT_PACKAGES := lwt.ppx js_of_ocaml.ppx js_of_ocaml.deriving.ppx
17 |
18 | # Directory with files to be statically served
19 | LOCAL_STATIC = static
20 |
21 | # The backend for persistent data. Can be dbm or sqlite.
22 | # Make sure you have the following packages installed
23 | # - *dbm* if you use dbm --> opam install dbm.
24 | # - *sqlite3* if you use sqlite --> opam install sqlite3.
25 | PERSISTENT_DATA_BACKEND = sqlite
26 |
27 | # Debug application (yes/no): Debugging info in compilation,
28 | # JavaScript, ocsigenserver
29 | DEBUG := no
30 |
31 | # User to run server with (make run.*)
32 | WWWUSER := www-data
33 | WWWGROUP := www-data
34 |
35 | # Port for running the server (make run.*)
36 | PORT := 80
37 |
38 | # Port for testing (make test.*)
39 | TEST_PORT := 8080
40 |
41 | # Root of installation (must end with /)
42 | PREFIX := /usr/local/
43 |
44 | # Local folder for make test.* (must end with /)
45 | # Do not add files manually in this directory.
46 | # It is just here to test your installation before installing in /
47 | TEST_PREFIX := local/
48 |
49 | # The installation tree (relative to $(PREFIX) when
50 | # installing/running or $(TEST_PREFIX) when testing).
51 | # Configuration file $(PROJECT_NAME).conf
52 | ETCDIR := etc/${PROJECT_NAME}
53 | # Project's library $(PROJECT_NAME).cma (cmxs)
54 | LIBDIR := lib/${PROJECT_NAME}
55 | # Command pipe, eg. $ echo reload > $(INSTALL_PREFIX)$(CMDPIPE)
56 | CMDPIPE := var/run/${PROJECT_NAME}-cmd
57 | # Ocsigenserver's logging files
58 | LOGDIR := var/log/${PROJECT_NAME}
59 | # Ocsigenserver's persistent data files
60 | DATADIR := var/data/${PROJECT_NAME}
61 | # Copy of $(LOCAL_STATIC)
62 | STATICDIR := var/www/${PROJECT_NAME}/static
63 | # Project's JavaScript file
64 | ELIOMSTATICDIR := var/www/${PROJECT_NAME}/eliom
65 |
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/src/mysite/README:
--------------------------------------------------------------------------------
1 |
2 | Instructions
3 | ============
4 |
5 | This project is (initially) generated by eliom-distillery as the basic
6 | project "mysite".
7 |
8 | Generally, you can compile it and run ocsigenserver on it by
9 | $ make test.byte (or test.opt)
10 | See below for other useful targets for make.
11 |
12 | Generated files
13 | ---------------
14 |
15 | The following files in this directory have been generated by
16 | eliom-distillery:
17 |
18 | - mysite.eliom
19 | This is your initial source file.
20 |
21 | - static/
22 | The content of this folder is statically served. Put your CSS or
23 | additional JavaScript files here!
24 |
25 | - Makefile.options
26 | Configure your project here!
27 |
28 | - mysite.conf.in
29 | This file is a template for the configuration file for
30 | ocsigenserver. You will rarely have to edit itself - it takes its
31 | variables from the Makefile.options. This way, the installation
32 | rules and the configuration files are synchronized with respect to
33 | the different folders.
34 |
35 | - Makefile
36 | This contains all rules necessary to build, test, and run your
37 | Eliom application. You better don't touch it ;) See below for the
38 | relevant targets.
39 |
40 | - local/
41 | This directory is the target of the temporary installation of
42 | your application, to test locally before doing a system-wide
43 | installation in /. Do not put anything manually here.
44 |
45 | - README
46 | Not completely describable here.
47 |
48 |
49 | Makefile targets
50 | ----------------
51 |
52 | Here's some help on how to work with this basic distillery project:
53 |
54 | - Test your application by compiling it and running ocsigenserver locally
55 | $ make test.byte (or test.opt)
56 |
57 | - Compile it only
58 | $ make all (or byte or opt)
59 |
60 | - Deploy your project on your system
61 | $ sudo make install (or install.byte or install.opt)
62 |
63 | - Run the server on the deployed project
64 | $ sudo make run.byte (or run.opt)
65 |
66 | If WWWUSER in the Makefile.options is you, you don't need the
67 | `sudo'. If Eliom isn't installed globally, however, you need to
68 | re-export some environment variables to make this work:
69 | $ sudo PATH=$PATH OCAMLPATH=$OCAMLPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH make run.byte/run.opt
70 |
71 | - If you need a findlib package in your project, add it to the
72 | variables SERVER_PACKAGES and/or CLIENT_PACKAGES. The configuration
73 | file will be automatically updated.
74 |
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/src/mysite/mysite.conf.in:
--------------------------------------------------------------------------------
1 | %%% This is the template for your configuration file. The %%VALUES%% below are
2 | %%% taken from the Makefile to generate the actual configuration files.
3 | %%% This comment will disappear.
4 |
5 |
6 |
7 | %%PORT%%
8 | %%% Only set for running, not for testing
9 | %%USERGROUP%%
10 | %%LOGDIR%%
11 | %%DATADIR%%
12 | utf-8
13 | %%% Only set when debugging
14 | %%DEBUGMODE%%
15 | %%CMDPIPE%%
16 |
17 |
18 |
19 | %%% This will include the packages defined as SERVER_PACKAGES in your Makefile:
20 | %%PACKAGES%%
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/src/mysite/mysite.eliom:
--------------------------------------------------------------------------------
1 | [%%shared
2 | open Eliom_lib
3 | open Eliom_content
4 | open Html.D
5 | ]
6 |
7 | module Mysite_app =
8 | Eliom_registration.App (
9 | struct
10 | let application_name = "mysite"
11 | let global_data_path = None
12 | end)
13 |
14 | let main_service =
15 | Eliom_service.create
16 | ~path:(Eliom_service.Path [])
17 | ~meth:(Eliom_service.Get Eliom_parameter.unit)
18 | ()
19 |
20 | let () =
21 | Mysite_app.register
22 | ~service:main_service
23 | (fun () () ->
24 | Lwt.return
25 | (Eliom_tools.F.html
26 | ~title:"mysite"
27 | ~css:[["css";"mysite.css"]]
28 | Html.F.(body [
29 | h1 [pcdata "Hello world!"];
30 | ])))
31 |
--------------------------------------------------------------------------------
/eliom/tutorial/00100-hello-world/src/mysite/static/css/mysite.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: sans-serif;
3 | }
4 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/README.md:
--------------------------------------------------------------------------------
1 | # Display a static HTML table
2 |
3 | ## What you will learn
4 |
5 | In this tutorial, we will see how to build a static HTML table in order to display a list of products.
6 |
7 | ## Files structure
8 |
9 | This structure is not mandatory. Eliom doesn't require any specific structure so you are free to organize your code as you want.
10 |
11 | - `product.*`
12 |
13 | These files define a product record. Note that the `.eliomi` file defines the interface (this is the counterpart of the `.mli` file with OCaml).
14 |
15 | Each product has an identifier, a code, a list of names and a list of prices. The names are localized so they can be translated in several languages. The prices are localized too so they can be displayed in several currencies.
16 |
17 | - `i18n.*`, `id.*`, `money.*`, `language.*`
18 |
19 | Helper files for the product fields.
20 |
21 | - `model.*`
22 |
23 | Contains the list of products. For now, this list is static.
24 |
25 | - `view.*`
26 |
27 | Functions to build the HTML for the web page.
28 |
29 | - `mysite.eliom`
30 |
31 | The default main file for the application.
32 |
33 | ## Add the style sheets
34 |
35 | We use the [Bulma](http://bulma.io/) CSS framework. Pure CSS frameworks like this one are easier to integrate with an Eliom application. Indeed, Eliom can be used to dynamically modify some parts of the web page on the client side. And there is a risk of bad interferences with other CSS frameworks which include some Javascript code.
36 |
37 | In `mysite.eliom`, we create an external CSS link to download the Bulma style sheet:
38 |
39 | ```ocaml
40 | let css_bulma =
41 | Eliom_content.Html.F.(
42 | css_link
43 | ~uri:(
44 | make_uri
45 | ~service:(Eliom_service.static_dir ())
46 | ~https:true ~hostname:"cdnjs.cloudflare.com"
47 | ["ajax"; "libs"; "bulma"; "0.4.0"; "css"; "bulma.min.css"]
48 | ) ()
49 | )
50 | ```
51 |
52 | And to download our local CSS file, we create a local CSS link:
53 |
54 | ```ocaml
55 | let css_mysite =
56 | Eliom_content.Html.F.(
57 | css_link
58 | ~uri:(
59 | make_uri
60 | ~service:(Eliom_service.static_dir ())
61 | ~absolute:true
62 | ["css";"mysite.css"]
63 | ) ()
64 | )
65 | ```
66 |
67 | Then we include these links in the head part of the main page thanks to the `other_head` argument:
68 |
69 | ```ocaml
70 | Mysite_app.register
71 | ~service:main_service
72 | (fun () () ->
73 | Lwt.return
74 | (Eliom_tools.F.html
75 | ~title:"mysite"
76 | ~other_head:[css_bulma; css_mysite]
77 | View.body))
78 | ```
79 |
80 | ## Add packages
81 |
82 | As we need the `Num` package in `money.*` code, both on the server side and on the client side, we have to add this package in `Makefile.options`:
83 |
84 | ```
85 | SERVER_PACKAGES := num lwt.ppx js_of_ocaml.deriving.ppx
86 | CLIENT_PACKAGES := num lwt.ppx js_of_ocaml.ppx js_of_ocaml.deriving.ppx
87 | ```
88 |
89 | We also need to include the missing primitives of `Num` for js_of_ocaml with the `+nat.js` option in `Makefile`:
90 |
91 | ```
92 | JS_OF_ELIOM := js_of_eliom -ppx -jsopt +nat.js
93 | ```
94 |
95 | ## How to build the HTML
96 |
97 | All the HTML for the page is built in `view.eliom` with functions from `Eliom_content.Html.F`. Under the hood, this module uses [TyXML](http://ocsigen.org/tyxml/) which is, like Eliom, a part of the [Ocsigen project](http://ocsigen.org/). `TyXML` stands for `Typed XML` and is a library for building statically correct HTML5 and SVG documents. Indeed, thanks to the strong typing of OCaml language, the library is able to forbid invalid HTML5 or SVG code at compile time.
98 |
99 | As an example, to build a `tr` in a table, we use this code:
100 |
101 | ```ocaml
102 | tr [
103 | td [ pcdata product_code ] ;
104 | td [ pcdata product_name ] ;
105 | td ~a:[a_class ["price"]] [ pcdata product_price ] ;
106 | ]
107 | ```
108 |
109 | `tr`, `td`, `pcdata` and `a_class` are functions coming from `TyXML`. The optional argument `~a` can be used to add attributes to the HTML entity (a class name for instance). The `pcdata` function is used to add plain text.
110 |
111 | Obviously, `TyXML` needs a multitude of functions to be able to build all the HTML5 entities and attributes. In most of the cases, the function name is the same than the HTML5 entity or argument. You can consult [this page](http://ocsigen.org/tyxml/dev/api/index_values) to have a complete list and find the right function.
112 |
113 | ## Test the application
114 |
115 | As usual, run the Eliom server in test mode with one of these commands:
116 |
117 | ```
118 | $ make test.byte
119 | ```
120 |
121 | or
122 |
123 | ```
124 | $ make test.opt
125 | ```
126 |
127 | Then go to [http://localhost:8080/](http://localhost:8080/). You should see the products list.
128 |
129 |
130 | ## Next step
131 |
132 | In the [next tutorial](../00300-url-param/), we will see how to use a parameter in the URL to set the language and how to add an input select to choose the currency.
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/.ocp-indent:
--------------------------------------------------------------------------------
1 | normal
2 | with=0
3 | syntax=lwt mll
4 | max_indent=2
5 | ppx_stritem_ext=0
6 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/Makefile.options:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------------------------------------------------
3 | # SETTINGS FOR THE ELIOM PROJECT mysite
4 | #----------------------------------------------------------------------
5 |
6 | PROJECT_NAME := mysite
7 |
8 | # Source files for the server
9 | SERVER_FILES := $(wildcard *.eliomi *.eliom)
10 | # Source files for the client
11 | CLIENT_FILES := $(wildcard *.eliomi *.eliom)
12 |
13 | # OCamlfind packages for the server
14 | SERVER_PACKAGES := num lwt.ppx js_of_ocaml.deriving.ppx
15 | # OCamlfind packages for the client
16 | CLIENT_PACKAGES := num lwt.ppx js_of_ocaml.ppx js_of_ocaml.deriving.ppx
17 |
18 | # Directory with files to be statically served
19 | LOCAL_STATIC = static
20 |
21 | # The backend for persistent data. Can be dbm or sqlite.
22 | # Make sure you have the following packages installed
23 | # - *dbm* if you use dbm --> opam install dbm.
24 | # - *sqlite3* if you use sqlite --> opam install sqlite3.
25 | PERSISTENT_DATA_BACKEND = sqlite
26 |
27 | # Debug application (yes/no): Debugging info in compilation,
28 | # JavaScript, ocsigenserver
29 | DEBUG := no
30 |
31 | # User to run server with (make run.*)
32 | WWWUSER := www-data
33 | WWWGROUP := www-data
34 |
35 | # Port for running the server (make run.*)
36 | PORT := 80
37 |
38 | # Port for testing (make test.*)
39 | TEST_PORT := 8080
40 |
41 | # Root of installation (must end with /)
42 | PREFIX := /usr/local/
43 |
44 | # Local folder for make test.* (must end with /)
45 | # Do not add files manually in this directory.
46 | # It is just here to test your installation before installing in /
47 | TEST_PREFIX := local/
48 |
49 | # The installation tree (relative to $(PREFIX) when
50 | # installing/running or $(TEST_PREFIX) when testing).
51 | # Configuration file $(PROJECT_NAME).conf
52 | ETCDIR := etc/${PROJECT_NAME}
53 | # Project's library $(PROJECT_NAME).cma (cmxs)
54 | LIBDIR := lib/${PROJECT_NAME}
55 | # Command pipe, eg. $ echo reload > $(INSTALL_PREFIX)$(CMDPIPE)
56 | CMDPIPE := var/run/${PROJECT_NAME}-cmd
57 | # Ocsigenserver's logging files
58 | LOGDIR := var/log/${PROJECT_NAME}
59 | # Ocsigenserver's persistent data files
60 | DATADIR := var/data/${PROJECT_NAME}
61 | # Copy of $(LOCAL_STATIC)
62 | STATICDIR := var/www/${PROJECT_NAME}/static
63 | # Project's JavaScript file
64 | ELIOMSTATICDIR := var/www/${PROJECT_NAME}/eliom
65 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/README:
--------------------------------------------------------------------------------
1 |
2 | Instructions
3 | ============
4 |
5 | This project is (initially) generated by eliom-distillery as the basic
6 | project "mysite".
7 |
8 | Generally, you can compile it and run ocsigenserver on it by
9 | $ make test.byte (or test.opt)
10 | See below for other useful targets for make.
11 |
12 | Generated files
13 | ---------------
14 |
15 | The following files in this directory have been generated by
16 | eliom-distillery:
17 |
18 | - mysite.eliom
19 | This is your initial source file.
20 |
21 | - static/
22 | The content of this folder is statically served. Put your CSS or
23 | additional JavaScript files here!
24 |
25 | - Makefile.options
26 | Configure your project here!
27 |
28 | - mysite.conf.in
29 | This file is a template for the configuration file for
30 | ocsigenserver. You will rarely have to edit itself - it takes its
31 | variables from the Makefile.options. This way, the installation
32 | rules and the configuration files are synchronized with respect to
33 | the different folders.
34 |
35 | - Makefile
36 | This contains all rules necessary to build, test, and run your
37 | Eliom application. You better don't touch it ;) See below for the
38 | relevant targets.
39 |
40 | - local/
41 | This directory is the target of the temporary installation of
42 | your application, to test locally before doing a system-wide
43 | installation in /. Do not put anything manually here.
44 |
45 | - README
46 | Not completely describable here.
47 |
48 |
49 | Makefile targets
50 | ----------------
51 |
52 | Here's some help on how to work with this basic distillery project:
53 |
54 | - Test your application by compiling it and running ocsigenserver locally
55 | $ make test.byte (or test.opt)
56 |
57 | - Compile it only
58 | $ make all (or byte or opt)
59 |
60 | - Deploy your project on your system
61 | $ sudo make install (or install.byte or install.opt)
62 |
63 | - Run the server on the deployed project
64 | $ sudo make run.byte (or run.opt)
65 |
66 | If WWWUSER in the Makefile.options is you, you don't need the
67 | `sudo'. If Eliom isn't installed globally, however, you need to
68 | re-export some environment variables to make this work:
69 | $ sudo PATH=$PATH OCAMLPATH=$OCAMLPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH make run.byte/run.opt
70 |
71 | - If you need a findlib package in your project, add it to the
72 | variables SERVER_PACKAGES and/or CLIENT_PACKAGES. The configuration
73 | file will be automatically updated.
74 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/config.eliom:
--------------------------------------------------------------------------------
1 |
2 | let default_language = Language.En
3 |
4 | let default_iso4217 = Money.Iso4217.(Iso usd)
5 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/config.eliomi:
--------------------------------------------------------------------------------
1 |
2 | val default_language : Language.t
3 |
4 | val default_iso4217 : Money.Iso4217.iso
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/i18n.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t = Language.t * string
5 |
6 | let make lang s = (lang, s)
7 |
8 | let translate lang (l: t list) =
9 | try
10 | let s = List.assoc lang l in
11 | Some (lang, s)
12 | with
13 | | Not_found -> None
14 |
15 | let to_string (_, s) = s
16 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/i18n.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t
5 |
6 | val make : Language.t -> string -> t
7 | val translate : Language.t -> t list -> t option
8 | val to_string : t -> string
9 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/id.eliom:
--------------------------------------------------------------------------------
1 | type t = Int64.t
2 |
3 | let empty = Int64.zero
4 |
5 | let of_int64 v = v
6 |
7 | let to_string = Int64.to_string
8 |
9 | let compare = Int64.compare
10 |
11 | let succ = Int64.succ
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/id.eliomi:
--------------------------------------------------------------------------------
1 | type t
2 |
3 | val empty : t
4 | val of_int64 : Int64.t -> t
5 | val to_string : t -> string
6 | val compare : t -> t -> int
7 | val succ : t -> t
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/language.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | let of_string = function
11 | | "en" -> En
12 | | "fr" -> Fr
13 | | s -> raise(Unknown_language s)
14 |
15 | let to_string = function
16 | | En -> "en"
17 | | Fr -> "fr"
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/language.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | val of_string : string -> t
11 | val to_string : t -> string
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/model.eliom:
--------------------------------------------------------------------------------
1 |
2 | open Money
3 |
4 | let p1 =
5 | let price_usd = Vat.excl(Currency.(of_int 19 Iso4217.usd)) in
6 | let price_eur = Vat.exchange Iso4217.eur price_usd in
7 | Product.(
8 | empty
9 | |> set_id (Id.of_int64 1L)
10 | |> set_code "WG04"
11 | |> set_names I18n.(
12 | [ make Language.En "Waffle gun"; make Language.Fr "Pistolet à gaufre"])
13 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
14 | )
15 |
16 | let p2 =
17 | let price_eur = Vat.excl(Currency.(of_int 5 Iso4217.eur)) in
18 | let price_usd = Vat.exchange Iso4217.usd price_eur in
19 | Product.(
20 | empty
21 | |> set_id (Id.of_int64 2L)
22 | |> set_code "EW-2000"
23 | |> set_names I18n.(
24 | [ make Language.En "Electric whisker"; make Language.Fr "Tourniquette"])
25 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
26 | )
27 |
28 | let p3 =
29 | let price_usd = Vat.excl(Currency.(of_int 599 Iso4217.usd)) in
30 | let price_eur = Vat.exchange Iso4217.eur price_usd in
31 | Product.(
32 | empty
33 | |> set_id (Id.of_int64 3L)
34 | |> set_code "PROGLAGLA"
35 | |> set_names I18n.(
36 | [ make Language.En "Refrigerator"; make Language.Fr "Frigidaire"])
37 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
38 | )
39 |
40 | let products = [
41 | p1; p2; p3
42 | ]
43 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/model.eliomi:
--------------------------------------------------------------------------------
1 | val products : Product.t list
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/money.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 = struct
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t =
12 | | Usd : usd t
13 | | Eur : eur t
14 |
15 | type iso = Iso : 'a t -> iso
16 |
17 | let iso v = Iso v
18 |
19 | let usd : usd t = Usd
20 |
21 | let eur : eur t = Eur
22 |
23 | let of_string (type a) (s:string) : iso =
24 | match s with
25 | | "usd" -> Iso Usd
26 | | "eur" -> Iso Eur
27 | | _ -> raise(Unknown_code s)
28 |
29 | let to_string (type a) (v:a t) : string =
30 | match v with
31 | | Usd -> "usd"
32 | | Eur -> "eur"
33 |
34 | let one = Num.num_of_int 1
35 |
36 | let exchange_rate (type a b) ~(for_one:a t) ~(get:b t) : Num.num =
37 | match for_one, get with
38 | | Usd, Eur -> Num.num_of_string "934813/1000000"
39 | | Eur, Usd -> Num.num_of_string "106951/100000"
40 | | Usd, Usd -> one
41 | | Eur, Eur -> one
42 |
43 | let symbol (type a) (v:a t) : string =
44 | match v with
45 | | Eur -> "€"
46 | | Usd -> "$"
47 |
48 | let eq (type a b) (v1:a t) (v2:b t) : bool =
49 | match v1, v2 with
50 | | Eur, Eur -> true
51 | | Usd, Usd -> true
52 | | _ -> false
53 |
54 | end
55 |
56 | module Currency = struct
57 |
58 | type 'a t = Num.num * 'a Iso4217.t
59 |
60 | type amount = Amount : 'a t -> amount
61 |
62 | let of_num (v:Num.num) (iso:'a Iso4217.t) : 'a t =
63 | (v, iso)
64 |
65 | let of_int v iso =
66 | of_num (Num.num_of_int v) iso
67 |
68 | let of_int64 v iso =
69 | of_num (Num.num_of_string(Int64.to_string v)) iso
70 |
71 | let of_string v iso =
72 | let accuracy_float = 10000. in
73 | let accuracy_int = 10000 in
74 | let x =
75 | (float_of_string v) *. accuracy_float
76 | |> Int64.of_float
77 | |> Int64.to_string
78 | |> Num.num_of_string
79 | in
80 | let x = Num.div_num x (Num.num_of_int accuracy_int) in
81 | of_num x iso
82 |
83 | let (+) (v1:'a t) (v2:'a t) : 'a t =
84 | let x1, iso = v1 in
85 | let x2, iso = v2 in
86 | (Num.add_num x1 x2, iso)
87 |
88 | let op_num op (n:Num.num) (v:'a t) : 'a t =
89 | let x, iso = v in
90 | (op x n, iso)
91 |
92 | let mult_by_num n v =
93 | op_num Num.mult_num n v
94 |
95 | let div_by_num n v =
96 | op_num Num.div_num n v
97 |
98 | let exchange (type a b) (to_iso: b Iso4217.t) (v: a t) : b t =
99 | let x, from_iso = v in
100 | (Num.mult_num x (Iso4217.exchange_rate ~for_one:from_iso ~get:to_iso), to_iso)
101 |
102 | let to_string_x f (x, _) =
103 | f x
104 |
105 | let to_string v =
106 | let f v =
107 | let s = Num.approx_num_fix 2 v in
108 | let s = String.sub s 1 ((String.length s) - 1) in
109 | if Num.sign_num v < 0 then
110 | "- " ^ s
111 | else
112 | s
113 | in
114 | to_string_x f v
115 |
116 | let to_string_fractional v =
117 | to_string_x Num.string_of_num v
118 |
119 | let to_string_decimal digits v =
120 | to_string_x (Num.approx_num_fix digits) v
121 |
122 | let to_string_scientific digits v =
123 | to_string_x (Num.approx_num_exp digits) v
124 |
125 | let symbol (_, iso) =
126 | Iso4217.symbol iso
127 |
128 | let filter_one_currency iso l =
129 | let f v =
130 | match v with
131 | | Amount (_, x) -> Iso4217.eq x iso
132 | in
133 | List.filter f l
134 |
135 | let same_currency (_, iso1) (_, iso2) =
136 | Iso4217.eq iso1 iso2
137 |
138 | end
139 |
140 | module Vat = struct
141 |
142 | type excl
143 | type incl
144 |
145 | type ('a, 'b) t =
146 | | Excl : 'a Currency.t -> ('a, excl) t
147 | | Incl : 'a Currency.t -> ('a, incl) t
148 |
149 | type amount = Amount : ('a, 'b) t -> amount
150 |
151 | let apply1_x (type b) f (v:('a, b) t) =
152 | match v with
153 | | Incl v -> f v
154 | | Excl v -> f v
155 |
156 | let apply1 (type b) f (v:('a, b) t) : ('a, b) t =
157 | match v with
158 | | Incl v -> Incl(f v)
159 | | Excl v -> Excl(f v)
160 |
161 | let apply2 (type b) f (v1:('a, b) t) (v2:('a, b) t) : ('a, b) t =
162 | match v1, v2 with
163 | | Incl v1, Incl v2 -> Incl(f v1 v2)
164 | | Excl v1, Excl v2 -> Excl(f v1 v2)
165 |
166 | let (+) v1 v2 =
167 | apply2 Currency.(+) v1 v2
168 |
169 | let mult_by_num x v =
170 | apply1 (Currency.mult_by_num x) v
171 |
172 | let div_by_num x v =
173 | apply1 (Currency.div_by_num x) v
174 |
175 | let exchange (type a b c) iso (v:(a, b) t) : (c, b) t =
176 | match v with
177 | | Incl v -> Incl(Currency.exchange iso v)
178 | | Excl v -> Excl(Currency.exchange iso v)
179 |
180 | let to_string v =
181 | apply1_x Currency.to_string v
182 |
183 | let to_string_fractional v =
184 | apply1_x Currency.to_string_fractional v
185 |
186 | let to_string_decimal digits =
187 | apply1_x (Currency.to_string_decimal digits)
188 |
189 | let to_string_scientific digits =
190 | apply1_x (Currency.to_string_scientific digits)
191 |
192 | let excl v = Excl v
193 | let incl v = Incl v
194 |
195 | let tax_rate =
196 | Num.num_of_string "120/100"
197 |
198 | let to_excl (v:('a, incl) t) : ('a, excl) t =
199 | match v with
200 | | Incl v -> Excl(Currency.div_by_num tax_rate v)
201 |
202 | let to_incl (v:('a, excl) t) : ('a, incl) t =
203 | match v with
204 | | Excl v -> Incl(Currency.mult_by_num tax_rate v)
205 |
206 | let symbol_currency (type b) (v:('a, b) t) =
207 | apply1_x Currency.symbol v
208 |
209 | let filter_one_currency iso l =
210 | let f v =
211 | match v with
212 | | Amount a ->
213 | match a with
214 | | Incl i -> Currency.same_currency i (Currency.of_int 0 iso)
215 | | Excl e -> Currency.same_currency e (Currency.of_int 0 iso)
216 | in
217 | List.filter f l
218 |
219 | let idiom (type b) lang (v:('a, b) t) : I18n.t option =
220 | let l =
221 | match v with
222 | | Excl _ -> [
223 | I18n.make Language.En "excl. VAT" ;
224 | I18n.make Language.Fr "HT" ;
225 | ]
226 | | Incl _ -> [
227 | I18n.make Language.En "incl. VAT" ;
228 | I18n.make Language.Fr "TTC" ;
229 | ]
230 | in
231 | I18n.translate lang l
232 |
233 | end
234 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/money.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 : sig
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t
12 |
13 | type iso = Iso : 'a t -> iso
14 |
15 | val iso : 'a t -> iso
16 |
17 | val usd : usd t
18 | val eur : eur t
19 |
20 | val of_string : string -> iso
21 | val to_string : 'a t -> string
22 | val symbol : 'a t -> string
23 |
24 | end
25 |
26 | module Currency : sig
27 |
28 | type 'a t
29 |
30 | type amount = Amount : 'a t -> amount
31 |
32 | val of_num : Num.num -> 'a Iso4217.t -> 'a t
33 | val of_int : int -> 'a Iso4217.t -> 'a t
34 | val of_int64 : Int64.t -> 'a Iso4217.t -> 'a t
35 | val of_string : string -> 'a Iso4217.t -> 'a t
36 |
37 | val (+) : 'a t -> 'a t -> 'a t
38 | val mult_by_num : Num.num -> 'a t -> 'a t
39 | val div_by_num : Num.num -> 'a t -> 'a t
40 |
41 | val exchange : 'a Iso4217.t -> 'b t -> 'a t
42 |
43 | val to_string : 'a t -> string
44 | val to_string_fractional : 'a t -> string
45 | val to_string_decimal : int -> 'a t -> string
46 | val to_string_scientific : int -> 'a t -> string
47 |
48 | val symbol : 'a t -> string
49 |
50 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
51 | val same_currency : 'a t -> 'a t -> bool
52 |
53 | end
54 |
55 | module Vat : sig
56 |
57 | type excl
58 | type incl
59 |
60 | type ('a, 'b) t
61 |
62 | type amount = Amount : ('a, 'b) t -> amount
63 |
64 | val (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
65 | val mult_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
66 | val div_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
67 |
68 | val exchange : 'a Iso4217.t -> ('b, 'c) t -> ('a, 'c) t
69 |
70 | val to_string : ('a, 'b) t -> string
71 | val to_string_fractional : ('a, 'b) t -> string
72 | val to_string_decimal : int -> ('a, 'b) t -> string
73 | val to_string_scientific : int -> ('a, 'b) t -> string
74 |
75 | val excl : 'a Currency.t -> ('a, excl) t
76 | val incl : 'a Currency.t -> ('a, incl) t
77 |
78 | val to_excl : ('a, incl) t -> ('a, excl) t
79 | val to_incl : ('a, excl) t -> ('a, incl) t
80 |
81 | val symbol_currency : ('a, 'b) t -> string
82 |
83 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
84 |
85 | val idiom : Language.t -> ('a, 'b) t -> I18n.t option
86 |
87 | end
88 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/mysite.conf.in:
--------------------------------------------------------------------------------
1 | %%% This is the template for your configuration file. The %%VALUES%% below are
2 | %%% taken from the Makefile to generate the actual configuration files.
3 | %%% This comment will disappear.
4 |
5 |
6 |
7 | %%PORT%%
8 | %%% Only set for running, not for testing
9 | %%USERGROUP%%
10 | %%LOGDIR%%
11 | %%DATADIR%%
12 | utf-8
13 | %%% Only set when debugging
14 | %%DEBUGMODE%%
15 | %%CMDPIPE%%
16 |
17 |
18 |
19 | %%% This will include the packages defined as SERVER_PACKAGES in your Makefile:
20 | %%PACKAGES%%
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/mysite.eliom:
--------------------------------------------------------------------------------
1 | [%%shared
2 | open Eliom_lib
3 | open Eliom_content
4 | open Html.D
5 | ]
6 |
7 | module Mysite_app =
8 | Eliom_registration.App (
9 | struct
10 | let application_name = "mysite"
11 | let global_data_path = None
12 | end)
13 |
14 | let main_service =
15 | Eliom_service.create
16 | ~path:(Eliom_service.Path [])
17 | ~meth:(Eliom_service.Get Eliom_parameter.unit)
18 | ()
19 |
20 | let () =
21 | let css_bulma =
22 | Eliom_content.Html.F.(
23 | css_link
24 | ~uri:(
25 | make_uri
26 | ~service:(Eliom_service.static_dir ())
27 | ~https:true ~hostname:"cdnjs.cloudflare.com"
28 | ["ajax"; "libs"; "bulma"; "0.4.0"; "css"; "bulma.min.css"]
29 | ) ()
30 | )
31 | in
32 | let css_mysite =
33 | Eliom_content.Html.F.(
34 | css_link
35 | ~uri:(
36 | make_uri
37 | ~service:(Eliom_service.static_dir ())
38 | ~absolute:true
39 | ["css";"mysite.css"]
40 | ) ()
41 | )
42 | in
43 | Mysite_app.register
44 | ~service:main_service
45 | (fun () () ->
46 | Lwt.return
47 | (Eliom_tools.F.html
48 | ~title:"mysite"
49 | ~other_head:[css_bulma; css_mysite]
50 | View.body))
51 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/product.eliom:
--------------------------------------------------------------------------------
1 |
2 | type t = {
3 | id : Id.t ;
4 | code : string ;
5 | names : I18n.t list ;
6 | prices : Money.Vat.amount list ;
7 | }
8 |
9 | let empty = {
10 | id = Id.empty ;
11 | code = "" ;
12 | names = [] ;
13 | prices = [] ;
14 | }
15 |
16 | let get_id v = v.id
17 |
18 | let set_id id v = { v with id }
19 |
20 | let get_code v = v.code
21 |
22 | let set_code code v = { v with code }
23 |
24 | let get_names v = v.names
25 |
26 | let set_names names v = { v with names }
27 |
28 | let get_prices v = v.prices
29 |
30 | let set_prices prices v = { v with prices }
31 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/product.eliomi:
--------------------------------------------------------------------------------
1 |
2 | type t
3 |
4 | val empty : t
5 |
6 | val get_id : t -> Id.t
7 | val set_id : Id.t -> t -> t
8 |
9 | val get_code : t -> string
10 | val set_code : string -> t -> t
11 |
12 | val get_names : t -> I18n.t list
13 | val set_names : I18n.t list -> t -> t
14 |
15 | val get_prices : t -> Money.Vat.amount list
16 | val set_prices : Money.Vat.amount list -> t -> t
17 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/static/css/mysite.css:
--------------------------------------------------------------------------------
1 | .table th.price, .table td.price {
2 | text-align: right;
3 | }
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/view.eliom:
--------------------------------------------------------------------------------
1 | open Eliom_content
2 |
3 | type msg =
4 | | List of I18n.t list
5 | | Title
6 | | No_product
7 | | Col_code
8 | | Col_name
9 | | Col_price
10 |
11 | let price_to_string lang l iso4217 =
12 | let string_idiom p =
13 | match Money.Vat.idiom lang p with
14 | | None -> (
15 | match Money.Vat.idiom Config.default_language p with
16 | | None -> assert false
17 | | Some v -> I18n.to_string v
18 | )
19 | | Some v -> I18n.to_string v
20 | in
21 | match iso4217 with
22 | | Money.Iso4217.Iso v ->
23 | match Money.Vat.filter_one_currency v l with
24 | | [] -> "price not available"
25 | | e :: [] -> (
26 | let open Money.Vat in
27 | match e with
28 | | Amount p -> (
29 | Printf.sprintf "%s %s %s"
30 | (to_string p) (symbol_currency p) (string_idiom p)
31 | )
32 | )
33 | | _ -> "price not available" (* several prices !!?? *)
34 |
35 | let _t msg lang =
36 | let l =
37 | I18n.(
38 | match msg with
39 | | List v -> v
40 | | Title -> [
41 | make Language.En "List of products" ;
42 | make Language.Fr "Liste des produits" ;
43 | ]
44 | | No_product -> [
45 | make Language.En "No product" ;
46 | make Language.Fr "Aucun produit" ;
47 | ]
48 | | Col_code -> [
49 | make Language.En "Code" ;
50 | make Language.Fr "Code" ;
51 | ]
52 | | Col_name -> [
53 | make Language.En "Name" ;
54 | make Language.Fr "Nom" ;
55 | ]
56 | | Col_price -> [
57 | make Language.En "Unit price" ;
58 | make Language.Fr "Prix à l'unité" ;
59 | ]
60 | )
61 | in
62 | match (I18n.translate lang l) with
63 | | None -> "translation not available"
64 | | Some v -> I18n.to_string v
65 |
66 | let table_of_products lp =
67 | let products =
68 | let f acc e =
69 | let product_code = Product.get_code e in
70 | let product_name = _t (List(Product.get_names e)) Config.default_language in
71 | let product_price = price_to_string Config.default_language (Product.get_prices e) Config.default_iso4217 in
72 | let tr =
73 | Html.F.(
74 | tr [
75 | td [ pcdata product_code ] ;
76 | td [ pcdata product_name ] ;
77 | td ~a:[a_class ["price"]] [ pcdata product_price ] ;
78 | ]
79 | )
80 | in
81 | tr :: acc
82 | in
83 | List.rev(List.fold_left f [] lp)
84 | in
85 | Html.F.(
86 | tablex
87 | ~a:[a_class ["table"; "is-striped"]]
88 | ~thead:(thead [
89 | tr [
90 | th [ pcdata (_t Col_code Config.default_language) ] ;
91 | th [ pcdata (_t Col_name Config.default_language) ] ;
92 | th ~a:[a_class ["price"]] [ pcdata (_t Col_price Config.default_language) ] ;
93 | ]
94 | ]) [
95 | tbody products
96 | ]
97 | )
98 |
99 | let body =
100 | Html.F.(
101 | body [
102 | div ~a:[a_class ["columns"]] [
103 | div ~a:[a_class ["column is-8 is-offset-2"]] [
104 | h1 ~a:[a_class ["title"]] [ pcdata (_t Title Config.default_language) ] ;
105 | div [
106 | if List.length Model.products = 0 then (
107 | p [ pcdata (_t No_product Config.default_language)]
108 | ) else (
109 | table_of_products Model.products
110 | )
111 | ]
112 | ]
113 | ]
114 | ]
115 | )
116 |
--------------------------------------------------------------------------------
/eliom/tutorial/00200-static-table/src/mysite/view.eliomi:
--------------------------------------------------------------------------------
1 | val body : [ `Body ] Eliom_content.Html.elt
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/README.md:
--------------------------------------------------------------------------------
1 | # Use URL parameters
2 |
3 | ## What you will learn
4 |
5 | In this tutorial, we will see how to use URL parameters to change the language and the currency of our products list.
6 |
7 |
8 | ## Define service parameters
9 |
10 | We have two URL parameters:
11 |
12 | - `lang`
13 |
14 | `en` for the english translation, `fr` for the french translation.
15 |
16 | - `iso4217`
17 |
18 | `usd` for US dollar, `eur` for Euro.
19 |
20 | Each of these parameters is optional. The default values are defined in `config.eliom`.
21 |
22 | The service is created in `service.eliom`:
23 |
24 | ```ocaml
25 | Eliom_service.create
26 | ~path:(Eliom_service.Path [])
27 | ~meth:(Eliom_service.Get(
28 | Eliom_parameter.(
29 | (opt(
30 | user_type
31 | ~client_to_and_of:([%client { Eliom_parameter.of_string = Language.of_string; Eliom_parameter.to_string = Language.to_string } ])
32 | ~of_string:Language.of_string
33 | ~to_string:Language.to_string
34 | "lang"))
35 | **
36 | (opt(
37 | user_type
38 | ~client_to_and_of:([%client { Eliom_parameter.of_string = Money.Iso4217.of_string;
39 | Eliom_parameter.to_string = (fun v -> match v with Money.Iso4217.Iso iso -> Money.Iso4217.to_string iso) } ])
40 | ~of_string:Money.Iso4217.of_string
41 | ~to_string:(fun v -> match v with Money.Iso4217.Iso iso -> Money.Iso4217.to_string iso)
42 | "iso4217"))
43 | )
44 | ))
45 | ()
46 |
47 | ```
48 |
49 | We use 3 functions of `Eliom_parameter` module to define the URL parameters:
50 |
51 | - `opt` to specify that the parameter is optional
52 |
53 | - `**` to define a pair of parameters
54 |
55 | - `user_type` to be able to use our custom types `Language.t` and `Money.Iso4217.iso`. The last argument is the name of the parameter in the URL. The other arguments define the functions used to convert a value to a string and vice versa.
56 |
57 |
58 | ## Display a select element and set an onchanges handler
59 |
60 | We need to display a list of available currencies. And if the user chooses a new currency, we refresh the page to display the right prices.
61 |
62 | In `view.eliom`, we create the select element:
63 |
64 | ```ocaml
65 | let select_iso4217 iso4217 =
66 | let selected v =
67 | if (Money.Iso4217.iso v) = iso4217 then
68 | [Html.D.a_selected ()]
69 | else
70 | []
71 | in
72 | (*
73 | /!\ Use Html.D instead of Html.F
74 | Otherwise the event binding with Lwt_js_events.changes doesn't work
75 | *)
76 | Html.D.(Raw.select [
77 | option ~a:(Money.Iso4217.([a_value (to_string usd)] @ (selected usd))) (pcdata Money.Iso4217.(to_string usd)) ;
78 | option ~a:(Money.Iso4217.([a_value (to_string eur)] @ (selected eur))) (pcdata Money.Iso4217.(to_string eur)) ;
79 | ])
80 |
81 | ```
82 |
83 | Note that we use `Html.D` instead of `Html.F` because we bind the `onchanges` event to this select element:
84 |
85 | ```ocaml
86 | let select_element = select_iso4217 iso4217 in
87 | let _ = [%client
88 | (Lwt.async (fun () ->
89 | Lwt_js_events.changes (Eliom_content.Html.To_dom.of_element ~%select_element)
90 | (fun evt _ ->
91 | (* let _ = Lwt_log_js.log "Select changes!" in *)
92 | let iso4217 =
93 | let tgt = Dom_html.CoerceTo.select(Dom.eventTarget evt) in
94 | Js.Opt.case tgt
95 | (fun () -> assert false)
96 | (fun e ->
97 | let option = e##.options##item e##.selectedIndex in
98 | Js.Opt.case option
99 | (fun () -> assert false)
100 | (fun e ->
101 | try
102 | Money.Iso4217.(of_string (Js.to_string e##.value))
103 | with
104 | | _ -> ~%Config.default_iso4217))
105 | in
106 | let _ = Eliom_client.change_page ~replace:true ~service:~%Service.main (Some ~%lang, Some iso4217) () in
107 | Lwt.return ()))
108 | : unit)
109 | ] in
110 |
111 | ```
112 |
113 | As this binding is obviously only on the browser side, we put the code in a `[%client ... ]` section. Next, we use `Lwt_js_events.changes` to execute a function each time a change event happens on the select element. Note that, as `select_element` is defined on the server side and as this code section is on the client side, we get access to `select_element` with the `~%` syntax. In this function, we get the selected option value as a string and convert it to a value of type `Money.Iso4217.iso`. Then we use `Eliom_client.change_page` to refresh the page with the new URL parameter.
114 |
115 |
116 | ## Display a link with parameter
117 |
118 | To display a link to a service, we use the `Eliom_content.Html.F.a` function. The arguments are the service, the link text and the URL service parameters.
119 |
120 | ```ocaml
121 | let links_languages lang iso4217 =
122 | let l = [(Language.En, "EN"); (Language.Fr, "FR")] in
123 | let f acc (lang', s) =
124 | match lang' with
125 | | v when v = lang ->
126 | Html.F.(
127 | span ~a:[a_class ["level-item"]] [pcdata s]) :: acc
128 | | v ->
129 | Html.F.(
130 | span ~a:[a_class ["level-item"]] [a Service.main [pcdata s] (Some v, Some iso4217)])
131 | :: acc
132 | in
133 | List.rev(List.fold_left f [] l)
134 | ```
135 |
136 |
137 | ## Next step
138 |
139 | In the next tutorial, we will see how to add a new product.
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/.ocp-indent:
--------------------------------------------------------------------------------
1 | normal
2 | with=0
3 | syntax=lwt mll
4 | max_indent=2
5 | ppx_stritem_ext=0
6 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/Makefile.options:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------------------------------------------------
3 | # SETTINGS FOR THE ELIOM PROJECT mysite
4 | #----------------------------------------------------------------------
5 |
6 | PROJECT_NAME := mysite
7 |
8 | # Source files for the server
9 | SERVER_FILES := $(wildcard *.eliomi *.eliom)
10 | # Source files for the client
11 | CLIENT_FILES := $(wildcard *.eliomi *.eliom)
12 |
13 | # OCamlfind packages for the server
14 | SERVER_PACKAGES := num lwt.ppx js_of_ocaml.deriving.ppx
15 | # OCamlfind packages for the client
16 | CLIENT_PACKAGES := num lwt.ppx js_of_ocaml.ppx js_of_ocaml.deriving.ppx
17 |
18 | # Directory with files to be statically served
19 | LOCAL_STATIC = static
20 |
21 | # The backend for persistent data. Can be dbm or sqlite.
22 | # Make sure you have the following packages installed
23 | # - *dbm* if you use dbm --> opam install dbm.
24 | # - *sqlite3* if you use sqlite --> opam install sqlite3.
25 | PERSISTENT_DATA_BACKEND = sqlite
26 |
27 | # Debug application (yes/no): Debugging info in compilation,
28 | # JavaScript, ocsigenserver
29 | DEBUG := no
30 |
31 | # User to run server with (make run.*)
32 | WWWUSER := www-data
33 | WWWGROUP := www-data
34 |
35 | # Port for running the server (make run.*)
36 | PORT := 80
37 |
38 | # Port for testing (make test.*)
39 | TEST_PORT := 8080
40 |
41 | # Root of installation (must end with /)
42 | PREFIX := /usr/local/
43 |
44 | # Local folder for make test.* (must end with /)
45 | # Do not add files manually in this directory.
46 | # It is just here to test your installation before installing in /
47 | TEST_PREFIX := local/
48 |
49 | # The installation tree (relative to $(PREFIX) when
50 | # installing/running or $(TEST_PREFIX) when testing).
51 | # Configuration file $(PROJECT_NAME).conf
52 | ETCDIR := etc/${PROJECT_NAME}
53 | # Project's library $(PROJECT_NAME).cma (cmxs)
54 | LIBDIR := lib/${PROJECT_NAME}
55 | # Command pipe, eg. $ echo reload > $(INSTALL_PREFIX)$(CMDPIPE)
56 | CMDPIPE := var/run/${PROJECT_NAME}-cmd
57 | # Ocsigenserver's logging files
58 | LOGDIR := var/log/${PROJECT_NAME}
59 | # Ocsigenserver's persistent data files
60 | DATADIR := var/data/${PROJECT_NAME}
61 | # Copy of $(LOCAL_STATIC)
62 | STATICDIR := var/www/${PROJECT_NAME}/static
63 | # Project's JavaScript file
64 | ELIOMSTATICDIR := var/www/${PROJECT_NAME}/eliom
65 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/README:
--------------------------------------------------------------------------------
1 |
2 | Instructions
3 | ============
4 |
5 | This project is (initially) generated by eliom-distillery as the basic
6 | project "mysite".
7 |
8 | Generally, you can compile it and run ocsigenserver on it by
9 | $ make test.byte (or test.opt)
10 | See below for other useful targets for make.
11 |
12 | Generated files
13 | ---------------
14 |
15 | The following files in this directory have been generated by
16 | eliom-distillery:
17 |
18 | - mysite.eliom
19 | This is your initial source file.
20 |
21 | - static/
22 | The content of this folder is statically served. Put your CSS or
23 | additional JavaScript files here!
24 |
25 | - Makefile.options
26 | Configure your project here!
27 |
28 | - mysite.conf.in
29 | This file is a template for the configuration file for
30 | ocsigenserver. You will rarely have to edit itself - it takes its
31 | variables from the Makefile.options. This way, the installation
32 | rules and the configuration files are synchronized with respect to
33 | the different folders.
34 |
35 | - Makefile
36 | This contains all rules necessary to build, test, and run your
37 | Eliom application. You better don't touch it ;) See below for the
38 | relevant targets.
39 |
40 | - local/
41 | This directory is the target of the temporary installation of
42 | your application, to test locally before doing a system-wide
43 | installation in /. Do not put anything manually here.
44 |
45 | - README
46 | Not completely describable here.
47 |
48 |
49 | Makefile targets
50 | ----------------
51 |
52 | Here's some help on how to work with this basic distillery project:
53 |
54 | - Test your application by compiling it and running ocsigenserver locally
55 | $ make test.byte (or test.opt)
56 |
57 | - Compile it only
58 | $ make all (or byte or opt)
59 |
60 | - Deploy your project on your system
61 | $ sudo make install (or install.byte or install.opt)
62 |
63 | - Run the server on the deployed project
64 | $ sudo make run.byte (or run.opt)
65 |
66 | If WWWUSER in the Makefile.options is you, you don't need the
67 | `sudo'. If Eliom isn't installed globally, however, you need to
68 | re-export some environment variables to make this work:
69 | $ sudo PATH=$PATH OCAMLPATH=$OCAMLPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH make run.byte/run.opt
70 |
71 | - If you need a findlib package in your project, add it to the
72 | variables SERVER_PACKAGES and/or CLIENT_PACKAGES. The configuration
73 | file will be automatically updated.
74 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/config.eliom:
--------------------------------------------------------------------------------
1 |
2 | let default_language = Language.En
3 |
4 | let default_iso4217 = Money.Iso4217.(Iso usd)
5 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/config.eliomi:
--------------------------------------------------------------------------------
1 |
2 | val default_language : Language.t
3 |
4 | val default_iso4217 : Money.Iso4217.iso
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/i18n.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t = Language.t * string
5 |
6 | let make lang s = (lang, s)
7 |
8 | let translate lang (l: t list) =
9 | try
10 | let s = List.assoc lang l in
11 | Some (lang, s)
12 | with
13 | | Not_found -> None
14 |
15 | let to_string (_, s) = s
16 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/i18n.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t
5 |
6 | val make : Language.t -> string -> t
7 | val translate : Language.t -> t list -> t option
8 | val to_string : t -> string
9 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/id.eliom:
--------------------------------------------------------------------------------
1 | type t = Int64.t
2 |
3 | let empty = Int64.zero
4 |
5 | let of_int64 v = v
6 |
7 | let to_string = Int64.to_string
8 |
9 | let compare = Int64.compare
10 |
11 | let succ = Int64.succ
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/id.eliomi:
--------------------------------------------------------------------------------
1 | type t
2 |
3 | val empty : t
4 | val of_int64 : Int64.t -> t
5 | val to_string : t -> string
6 | val compare : t -> t -> int
7 | val succ : t -> t
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/language.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | let of_string = function
11 | | "en" -> En
12 | | "fr" -> Fr
13 | | s -> raise(Unknown_language s)
14 |
15 | let to_string = function
16 | | En -> "en"
17 | | Fr -> "fr"
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/language.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | val of_string : string -> t
11 | val to_string : t -> string
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/model.eliom:
--------------------------------------------------------------------------------
1 |
2 | open Money
3 |
4 | let p1 =
5 | let price_usd = Vat.excl(Currency.(of_int 19 Iso4217.usd)) in
6 | let price_eur = Vat.exchange Iso4217.eur price_usd in
7 | Product.(
8 | empty
9 | |> set_id (Id.of_int64 1L)
10 | |> set_code "WG04"
11 | |> set_names I18n.(
12 | [ make Language.En "Waffle gun"; make Language.Fr "Pistolet à gaufre"])
13 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
14 | )
15 |
16 | let p2 =
17 | let price_eur = Vat.excl(Currency.(of_int 5 Iso4217.eur)) in
18 | let price_usd = Vat.exchange Iso4217.usd price_eur in
19 | Product.(
20 | empty
21 | |> set_id (Id.of_int64 2L)
22 | |> set_code "EW-2000"
23 | |> set_names I18n.(
24 | [ make Language.En "Electric whisker"; make Language.Fr "Tourniquette"])
25 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
26 | )
27 |
28 | let p3 =
29 | let price_usd = Vat.excl(Currency.(of_int 599 Iso4217.usd)) in
30 | let price_eur = Vat.exchange Iso4217.eur price_usd in
31 | Product.(
32 | empty
33 | |> set_id (Id.of_int64 3L)
34 | |> set_code "PROGLAGLA"
35 | |> set_names I18n.(
36 | [ make Language.En "Refrigerator"; make Language.Fr "Frigidaire"])
37 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
38 | )
39 |
40 | let products = [
41 | p1; p2; p3
42 | ]
43 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/model.eliomi:
--------------------------------------------------------------------------------
1 | val products : Product.t list
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/money.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 = struct
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t =
12 | | Usd : usd t
13 | | Eur : eur t
14 |
15 | type iso = Iso : 'a t -> iso
16 |
17 | let iso v = Iso v
18 |
19 | let usd : usd t = Usd
20 |
21 | let eur : eur t = Eur
22 |
23 | let of_string (type a) (s:string) : iso =
24 | match s with
25 | | "usd" -> Iso Usd
26 | | "eur" -> Iso Eur
27 | | _ -> raise(Unknown_code s)
28 |
29 | let to_string (type a) (v:a t) : string =
30 | match v with
31 | | Usd -> "usd"
32 | | Eur -> "eur"
33 |
34 | let one = Num.num_of_int 1
35 |
36 | let exchange_rate (type a b) ~(for_one:a t) ~(get:b t) : Num.num =
37 | match for_one, get with
38 | | Usd, Eur -> Num.num_of_string "934813/1000000"
39 | | Eur, Usd -> Num.num_of_string "106951/100000"
40 | | Usd, Usd -> one
41 | | Eur, Eur -> one
42 |
43 | let symbol (type a) (v:a t) : string =
44 | match v with
45 | | Eur -> "€"
46 | | Usd -> "$"
47 |
48 | let eq (type a b) (v1:a t) (v2:b t) : bool =
49 | match v1, v2 with
50 | | Eur, Eur -> true
51 | | Usd, Usd -> true
52 | | _ -> false
53 |
54 | end
55 |
56 | module Currency = struct
57 |
58 | type 'a t = Num.num * 'a Iso4217.t
59 |
60 | type amount = Amount : 'a t -> amount
61 |
62 | let of_num (v:Num.num) (iso:'a Iso4217.t) : 'a t =
63 | (v, iso)
64 |
65 | let of_int v iso =
66 | of_num (Num.num_of_int v) iso
67 |
68 | let of_int64 v iso =
69 | of_num (Num.num_of_string(Int64.to_string v)) iso
70 |
71 | let of_string v iso =
72 | let accuracy_float = 10000. in
73 | let accuracy_int = 10000 in
74 | let x =
75 | (float_of_string v) *. accuracy_float
76 | |> Int64.of_float
77 | |> Int64.to_string
78 | |> Num.num_of_string
79 | in
80 | let x = Num.div_num x (Num.num_of_int accuracy_int) in
81 | of_num x iso
82 |
83 | let (+) (v1:'a t) (v2:'a t) : 'a t =
84 | let x1, iso = v1 in
85 | let x2, iso = v2 in
86 | (Num.add_num x1 x2, iso)
87 |
88 | let op_num op (n:Num.num) (v:'a t) : 'a t =
89 | let x, iso = v in
90 | (op x n, iso)
91 |
92 | let mult_by_num n v =
93 | op_num Num.mult_num n v
94 |
95 | let div_by_num n v =
96 | op_num Num.div_num n v
97 |
98 | let exchange (type a b) (to_iso: b Iso4217.t) (v: a t) : b t =
99 | let x, from_iso = v in
100 | (Num.mult_num x (Iso4217.exchange_rate ~for_one:from_iso ~get:to_iso), to_iso)
101 |
102 | let to_string_x f (x, _) =
103 | f x
104 |
105 | let to_string v =
106 | let f v =
107 | let s = Num.approx_num_fix 2 v in
108 | let s = String.sub s 1 ((String.length s) - 1) in
109 | if Num.sign_num v < 0 then
110 | "- " ^ s
111 | else
112 | s
113 | in
114 | to_string_x f v
115 |
116 | let to_string_fractional v =
117 | to_string_x Num.string_of_num v
118 |
119 | let to_string_decimal digits v =
120 | to_string_x (Num.approx_num_fix digits) v
121 |
122 | let to_string_scientific digits v =
123 | to_string_x (Num.approx_num_exp digits) v
124 |
125 | let symbol (_, iso) =
126 | Iso4217.symbol iso
127 |
128 | let filter_one_currency iso l =
129 | let f v =
130 | match v with
131 | | Amount (_, x) -> Iso4217.eq x iso
132 | in
133 | List.filter f l
134 |
135 | let same_currency (_, iso1) (_, iso2) =
136 | Iso4217.eq iso1 iso2
137 |
138 | end
139 |
140 | module Vat = struct
141 |
142 | type excl
143 | type incl
144 |
145 | type ('a, 'b) t =
146 | | Excl : 'a Currency.t -> ('a, excl) t
147 | | Incl : 'a Currency.t -> ('a, incl) t
148 |
149 | type amount = Amount : ('a, 'b) t -> amount
150 |
151 | let apply1_x (type b) f (v:('a, b) t) =
152 | match v with
153 | | Incl v -> f v
154 | | Excl v -> f v
155 |
156 | let apply1 (type b) f (v:('a, b) t) : ('a, b) t =
157 | match v with
158 | | Incl v -> Incl(f v)
159 | | Excl v -> Excl(f v)
160 |
161 | let apply2 (type b) f (v1:('a, b) t) (v2:('a, b) t) : ('a, b) t =
162 | match v1, v2 with
163 | | Incl v1, Incl v2 -> Incl(f v1 v2)
164 | | Excl v1, Excl v2 -> Excl(f v1 v2)
165 |
166 | let (+) v1 v2 =
167 | apply2 Currency.(+) v1 v2
168 |
169 | let mult_by_num x v =
170 | apply1 (Currency.mult_by_num x) v
171 |
172 | let div_by_num x v =
173 | apply1 (Currency.div_by_num x) v
174 |
175 | let exchange (type a b c) iso (v:(a, b) t) : (c, b) t =
176 | match v with
177 | | Incl v -> Incl(Currency.exchange iso v)
178 | | Excl v -> Excl(Currency.exchange iso v)
179 |
180 | let to_string v =
181 | apply1_x Currency.to_string v
182 |
183 | let to_string_fractional v =
184 | apply1_x Currency.to_string_fractional v
185 |
186 | let to_string_decimal digits =
187 | apply1_x (Currency.to_string_decimal digits)
188 |
189 | let to_string_scientific digits =
190 | apply1_x (Currency.to_string_scientific digits)
191 |
192 | let excl v = Excl v
193 | let incl v = Incl v
194 |
195 | let tax_rate =
196 | Num.num_of_string "120/100"
197 |
198 | let to_excl (v:('a, incl) t) : ('a, excl) t =
199 | match v with
200 | | Incl v -> Excl(Currency.div_by_num tax_rate v)
201 |
202 | let to_incl (v:('a, excl) t) : ('a, incl) t =
203 | match v with
204 | | Excl v -> Incl(Currency.mult_by_num tax_rate v)
205 |
206 | let symbol_currency (type b) (v:('a, b) t) =
207 | apply1_x Currency.symbol v
208 |
209 | let filter_one_currency iso l =
210 | let f v =
211 | match v with
212 | | Amount a ->
213 | match a with
214 | | Incl i -> Currency.same_currency i (Currency.of_int 0 iso)
215 | | Excl e -> Currency.same_currency e (Currency.of_int 0 iso)
216 | in
217 | List.filter f l
218 |
219 | let idiom (type b) lang (v:('a, b) t) : I18n.t option =
220 | let l =
221 | match v with
222 | | Excl _ -> [
223 | I18n.make Language.En "excl. VAT" ;
224 | I18n.make Language.Fr "HT" ;
225 | ]
226 | | Incl _ -> [
227 | I18n.make Language.En "incl. VAT" ;
228 | I18n.make Language.Fr "TTC" ;
229 | ]
230 | in
231 | I18n.translate lang l
232 |
233 | end
234 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/money.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 : sig
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t
12 |
13 | type iso = Iso : 'a t -> iso
14 |
15 | val iso : 'a t -> iso
16 |
17 | val usd : usd t
18 | val eur : eur t
19 |
20 | val of_string : string -> iso
21 | val to_string : 'a t -> string
22 | val symbol : 'a t -> string
23 |
24 | end
25 |
26 | module Currency : sig
27 |
28 | type 'a t
29 |
30 | type amount = Amount : 'a t -> amount
31 |
32 | val of_num : Num.num -> 'a Iso4217.t -> 'a t
33 | val of_int : int -> 'a Iso4217.t -> 'a t
34 | val of_int64 : Int64.t -> 'a Iso4217.t -> 'a t
35 | val of_string : string -> 'a Iso4217.t -> 'a t
36 |
37 | val (+) : 'a t -> 'a t -> 'a t
38 | val mult_by_num : Num.num -> 'a t -> 'a t
39 | val div_by_num : Num.num -> 'a t -> 'a t
40 |
41 | val exchange : 'a Iso4217.t -> 'b t -> 'a t
42 |
43 | val to_string : 'a t -> string
44 | val to_string_fractional : 'a t -> string
45 | val to_string_decimal : int -> 'a t -> string
46 | val to_string_scientific : int -> 'a t -> string
47 |
48 | val symbol : 'a t -> string
49 |
50 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
51 | val same_currency : 'a t -> 'a t -> bool
52 |
53 | end
54 |
55 | module Vat : sig
56 |
57 | type excl
58 | type incl
59 |
60 | type ('a, 'b) t
61 |
62 | type amount = Amount : ('a, 'b) t -> amount
63 |
64 | val (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
65 | val mult_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
66 | val div_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
67 |
68 | val exchange : 'a Iso4217.t -> ('b, 'c) t -> ('a, 'c) t
69 |
70 | val to_string : ('a, 'b) t -> string
71 | val to_string_fractional : ('a, 'b) t -> string
72 | val to_string_decimal : int -> ('a, 'b) t -> string
73 | val to_string_scientific : int -> ('a, 'b) t -> string
74 |
75 | val excl : 'a Currency.t -> ('a, excl) t
76 | val incl : 'a Currency.t -> ('a, incl) t
77 |
78 | val to_excl : ('a, incl) t -> ('a, excl) t
79 | val to_incl : ('a, excl) t -> ('a, incl) t
80 |
81 | val symbol_currency : ('a, 'b) t -> string
82 |
83 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
84 |
85 | val idiom : Language.t -> ('a, 'b) t -> I18n.t option
86 |
87 | end
88 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/mysite.conf.in:
--------------------------------------------------------------------------------
1 | %%% This is the template for your configuration file. The %%VALUES%% below are
2 | %%% taken from the Makefile to generate the actual configuration files.
3 | %%% This comment will disappear.
4 |
5 |
6 |
7 | %%PORT%%
8 | %%% Only set for running, not for testing
9 | %%USERGROUP%%
10 | %%LOGDIR%%
11 | %%DATADIR%%
12 | utf-8
13 | %%% Only set when debugging
14 | %%DEBUGMODE%%
15 | %%CMDPIPE%%
16 |
17 |
18 |
19 | %%% This will include the packages defined as SERVER_PACKAGES in your Makefile:
20 | %%PACKAGES%%
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/mysite.eliom:
--------------------------------------------------------------------------------
1 | [%%shared
2 | open Eliom_lib
3 | open Eliom_content
4 | open Html.D
5 | ]
6 |
7 | module Mysite_app =
8 | Eliom_registration.App (
9 | struct
10 | let application_name = "mysite"
11 | let global_data_path = None
12 | end)
13 |
14 | let () =
15 | let css_bulma =
16 | Eliom_content.Html.F.(
17 | css_link
18 | ~uri:(
19 | make_uri
20 | ~service:(Eliom_service.static_dir ())
21 | ~https:true ~hostname:"cdnjs.cloudflare.com"
22 | ["ajax"; "libs"; "bulma"; "0.4.0"; "css"; "bulma.min.css"]
23 | ) ()
24 | )
25 | in
26 | let css_mysite =
27 | Eliom_content.Html.F.(
28 | css_link
29 | ~uri:(
30 | make_uri
31 | ~service:(Eliom_service.static_dir ())
32 | ~absolute:true
33 | ["css";"mysite.css"]
34 | ) ()
35 | )
36 | in
37 |
38 | let page lang iso4217 =
39 | Eliom_tools.F.html
40 | ~title:"mysite"
41 | ~other_head:[css_bulma; css_mysite]
42 | (View.body lang iso4217)
43 | in
44 | Mysite_app.register
45 | ~service:Service.main
46 | ~error_handler:(fun l ->
47 | match l with
48 | | []
49 | | ("lang", Language.Unknown_language _) :: [] -> Lwt.return(page Config.default_language Config.default_iso4217)
50 | | ("iso4217", Money.Iso4217.Unknown_code _) :: [] -> Lwt.return(page Config.default_language Config.default_iso4217)
51 | | (_, e) :: _ -> raise e
52 | )
53 | (fun (lang, iso4217) () ->
54 | match lang, iso4217 with
55 | | None, None -> Lwt.return(page Config.default_language Config.default_iso4217)
56 | | Some vl, None -> Lwt.return(page vl Config.default_iso4217)
57 | | None, Some vi -> Lwt.return(page Config.default_language vi)
58 | | Some vl, Some vi -> Lwt.return(page vl vi))
59 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/product.eliom:
--------------------------------------------------------------------------------
1 |
2 | type t = {
3 | id : Id.t ;
4 | code : string ;
5 | names : I18n.t list ;
6 | prices : Money.Vat.amount list ;
7 | }
8 |
9 | let empty = {
10 | id = Id.empty ;
11 | code = "" ;
12 | names = [] ;
13 | prices = [] ;
14 | }
15 |
16 | let get_id v = v.id
17 |
18 | let set_id id v = { v with id }
19 |
20 | let get_code v = v.code
21 |
22 | let set_code code v = { v with code }
23 |
24 | let get_names v = v.names
25 |
26 | let set_names names v = { v with names }
27 |
28 | let get_prices v = v.prices
29 |
30 | let set_prices prices v = { v with prices }
31 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/product.eliomi:
--------------------------------------------------------------------------------
1 |
2 | type t
3 |
4 | val empty : t
5 |
6 | val get_id : t -> Id.t
7 | val set_id : Id.t -> t -> t
8 |
9 | val get_code : t -> string
10 | val set_code : string -> t -> t
11 |
12 | val get_names : t -> I18n.t list
13 | val set_names : I18n.t list -> t -> t
14 |
15 | val get_prices : t -> Money.Vat.amount list
16 | val set_prices : Money.Vat.amount list -> t -> t
17 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/service.eliom:
--------------------------------------------------------------------------------
1 | let main =
2 | Eliom_service.create
3 | ~path:(Eliom_service.Path [])
4 | ~meth:(Eliom_service.Get(
5 | Eliom_parameter.(
6 | (opt(
7 | user_type
8 | ~client_to_and_of:([%client { Eliom_parameter.of_string = Language.of_string; Eliom_parameter.to_string = Language.to_string } ])
9 | ~of_string:Language.of_string
10 | ~to_string:Language.to_string
11 | "lang"))
12 | **
13 | (opt(
14 | user_type
15 | ~client_to_and_of:([%client { Eliom_parameter.of_string = Money.Iso4217.of_string;
16 | Eliom_parameter.to_string = (fun v -> match v with Money.Iso4217.Iso iso -> Money.Iso4217.to_string iso) } ])
17 | ~of_string:Money.Iso4217.of_string
18 | ~to_string:(fun v -> match v with Money.Iso4217.Iso iso -> Money.Iso4217.to_string iso)
19 | "iso4217"))
20 | )
21 | ))
22 | ()
23 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/service.eliomi:
--------------------------------------------------------------------------------
1 | (*
2 | Trick to get the type:
3 | 1 - use (_, _, _, _, _, _, _, _, _, _, _) Eliom_service.t
4 | 2 - copy-paste the type from the compiler error message
5 | *)
6 |
7 | val main : (Language.t option * Money.Iso4217.iso option,
8 | unit,
9 | Eliom_service.get,
10 | Eliom_service.att,
11 | Eliom_service.non_co,
12 | Eliom_service.non_ext,
13 | Eliom_service.reg,
14 | [ `WithoutSuffix ],
15 | [ `One of Language.t ] Eliom_parameter.param_name * [ `One of Money.Iso4217.iso ] Eliom_parameter.param_name,
16 | unit,
17 | Eliom_service.non_ocaml)
18 | Eliom_service.t
19 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/static/css/mysite.css:
--------------------------------------------------------------------------------
1 | .table th.price, .table td.price {
2 | text-align: right;
3 | }
4 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/view.eliom:
--------------------------------------------------------------------------------
1 | open Eliom_content
2 |
3 | type msg =
4 | | List of I18n.t list
5 | | Title
6 | | No_product
7 | | Col_code
8 | | Col_name
9 | | Col_price
10 |
11 | let price_to_string lang l iso4217 =
12 | let string_idiom p =
13 | match Money.Vat.idiom lang p with
14 | | None -> (
15 | match Money.Vat.idiom Config.default_language p with
16 | | None -> assert false
17 | | Some v -> I18n.to_string v
18 | )
19 | | Some v -> I18n.to_string v
20 | in
21 | match iso4217 with
22 | | Money.Iso4217.Iso v ->
23 | match Money.Vat.filter_one_currency v l with
24 | | [] -> "price not available"
25 | | e :: [] -> (
26 | let open Money.Vat in
27 | match e with
28 | | Amount p -> (
29 | Printf.sprintf "%s %s %s"
30 | (to_string p) (symbol_currency p) (string_idiom p)
31 | )
32 | )
33 | | _ -> "price not available" (* several prices !!?? *)
34 |
35 | let _t msg lang =
36 | let l =
37 | I18n.(
38 | match msg with
39 | | List v -> v
40 | | Title -> [
41 | make Language.En "List of products" ;
42 | make Language.Fr "Liste des produits" ;
43 | ]
44 | | No_product -> [
45 | make Language.En "No product" ;
46 | make Language.Fr "Aucun produit" ;
47 | ]
48 | | Col_code -> [
49 | make Language.En "Code" ;
50 | make Language.Fr "Code" ;
51 | ]
52 | | Col_name -> [
53 | make Language.En "Name" ;
54 | make Language.Fr "Nom" ;
55 | ]
56 | | Col_price -> [
57 | make Language.En "Unit price" ;
58 | make Language.Fr "Prix à l'unité" ;
59 | ]
60 | )
61 | in
62 | match (I18n.translate lang l) with
63 | | None -> "translation not available"
64 | | Some v -> I18n.to_string v
65 |
66 | let select_iso4217 iso4217 =
67 | let selected v =
68 | if (Money.Iso4217.iso v) = iso4217 then
69 | [Html.D.a_selected ()]
70 | else
71 | []
72 | in
73 | (*
74 | /!\ Use Html.D instead of Html.F
75 | Otherwise the event binding with Lwt_js_events.changes doesn't work
76 | *)
77 | Html.D.(Raw.select [
78 | option ~a:(Money.Iso4217.([a_value (to_string usd)] @ (selected usd))) (pcdata Money.Iso4217.(to_string usd)) ;
79 | option ~a:(Money.Iso4217.([a_value (to_string eur)] @ (selected eur))) (pcdata Money.Iso4217.(to_string eur)) ;
80 | ])
81 |
82 | let table_of_products lp lang iso4217 =
83 | let products =
84 | let f acc e =
85 | let product_code = Product.get_code e in
86 | let product_name = _t (List(Product.get_names e)) lang in
87 | let product_price = price_to_string lang (Product.get_prices e) iso4217 in
88 | let tr =
89 | Html.F.(
90 | tr [
91 | td [ pcdata product_code ] ;
92 | td [ pcdata product_name ] ;
93 | td ~a:[a_class ["price"]] [ pcdata product_price ] ;
94 | ]
95 | )
96 | in
97 | tr :: acc
98 | in
99 | List.rev(List.fold_left f [] lp)
100 | in
101 | (* create only one select element and use it everywhere *)
102 | let select_element = select_iso4217 iso4217 in
103 | let _ = [%client
104 | (Lwt.async (fun () ->
105 | Lwt_js_events.changes (Eliom_content.Html.To_dom.of_element ~%select_element)
106 | (fun evt _ ->
107 | (* let _ = Lwt_log_js.log "Select changes!" in *)
108 | let iso4217 =
109 | let tgt = Dom_html.CoerceTo.select(Dom.eventTarget evt) in
110 | Js.Opt.case tgt
111 | (fun () -> assert false)
112 | (fun e ->
113 | let option = e##.options##item e##.selectedIndex in
114 | Js.Opt.case option
115 | (fun () -> assert false)
116 | (fun e ->
117 | try
118 | Money.Iso4217.(of_string (Js.to_string e##.value))
119 | with
120 | | _ -> ~%Config.default_iso4217))
121 | in
122 | let _ = Eliom_client.change_page ~replace:true ~service:~%Service.main (Some ~%lang, Some iso4217) () in
123 | Lwt.return ()))
124 | : unit)
125 | ] in
126 | Html.F.(
127 | tablex
128 | ~a:[a_class ["table"; "is-striped"]]
129 | ~thead:(thead [
130 | tr [
131 | th [ pcdata (_t Col_code lang) ] ;
132 | th [ pcdata (_t Col_name lang) ] ;
133 | th ~a:[a_class ["price"]] [
134 | pcdata (_t Col_price lang) ;
135 | select_element ;
136 | ] ;
137 | ]
138 | ]) [
139 | tbody products
140 | ]
141 | )
142 |
143 | let links_languages lang iso4217 =
144 | let l = [(Language.En, "EN"); (Language.Fr, "FR")] in
145 | let f acc (lang', s) =
146 | match lang' with
147 | | v when v = lang ->
148 | Html.F.(
149 | span ~a:[a_class ["level-item"]] [pcdata s]) :: acc
150 | | v ->
151 | Html.F.(
152 | span ~a:[a_class ["level-item"]] [a Service.main [pcdata s] (Some v, Some iso4217)])
153 | :: acc
154 | in
155 | List.rev(List.fold_left f [] l)
156 |
157 | let navigation lang iso4217 =
158 | Html.F.(
159 | nav ~a:[a_class ["level is-mobile"]] [
160 | div ~a:[a_class ["level-left"]] [
161 | div ~a:[a_class ["level-item"]] [
162 | h1 ~a:[a_class ["title"]] [ pcdata (_t Title lang) ]
163 | ]
164 | ] ;
165 | div ~a:[a_class ["level-right"]] (links_languages lang iso4217) ;
166 | ]
167 | )
168 |
169 | let body lang iso4217 =
170 | Html.F.(
171 | body [
172 | div ~a:[a_class ["container"]] [
173 | navigation lang iso4217 ;
174 | div [
175 | if List.length Model.products = 0 then (
176 | p [ pcdata (_t No_product lang)]
177 | ) else (
178 | table_of_products Model.products lang iso4217
179 | )
180 | ]
181 | ]
182 | ]
183 | )
184 |
--------------------------------------------------------------------------------
/eliom/tutorial/00300-url-param/src/mysite/view.eliomi:
--------------------------------------------------------------------------------
1 | val body : Language.t -> Money.Iso4217.iso -> [ `Body ] Eliom_content.Html.elt
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/README.md:
--------------------------------------------------------------------------------
1 | # Add a new product
2 |
3 | ## What you will learn
4 |
5 | In this tutorial, we will see how to use a basic form in order to add a new product.
6 |
7 |
8 | ## Define the services
9 |
10 | We need to define two new services in `service.eliom`:
11 |
12 | - One `GET` service to display the form.
13 |
14 | - One `POST` service to add the product after a form submit.
15 |
16 | Each of those services have the same access path `/product/add`. The POST service defines three parameters: the product code, the product name and the product price.
17 |
18 | ```ocaml
19 |
20 | let add =
21 | let path = Eliom_service.Path ["product"; "add"] in
22 | (Eliom_service.create ~path
23 | ~meth:(Eliom_service.Get lang_and_iso4217_parameters)
24 | (),
25 | Eliom_service.create ~path
26 | ~meth:(Eliom_service.Post (lang_and_iso4217_parameters,
27 | Eliom_parameter.(
28 | (string "code") ** (string "name") ** (string "price"))))
29 | ())
30 |
31 | ```
32 |
33 |
34 | ## Register the handlers
35 |
36 | Then we have to register the new services in `mysite.eliom`:
37 |
38 | ```ocaml
39 | Mysite_app.register
40 | ~service:(fst Service.add) (* GET to display the form *)
41 | ~error_handler:(error_handler View.form_add ())
42 | (register View.form_add) ;
43 | Mysite_app.register
44 | ~service:(snd Service.add) (* POST to add the product *)
45 | ~error_handler:(error_handler View.add ("", ("", "")))
46 | (register View.add) ;
47 | ```
48 |
49 |
50 | ## Form views
51 |
52 | We define a simple form in `view.eliom` with three input fields, a button to submit the form and a button to go back to the products list:
53 |
54 | ```ocaml
55 | let create_form_add lang iso4217 (product_code, (product_name, product_price)) =
56 | Html.F.([
57 | div ~a:[a_class ["field"]] [
58 | label ~a:[a_class ["label"]] [ pcdata (_t Field_code lang) ] ;
59 | p ~a:[a_class ["control"]] [
60 | Form.input ~a:[a_class ["input"]]
61 | ~input_type:`Text ~name:product_code
62 | Form.string
63 | ]
64 | ] ;
65 | div ~a:[a_class ["field"]] [
66 | label ~a:[a_class ["label"]] [ pcdata (_t Field_name lang) ] ;
67 | p ~a:[a_class ["control"]] [
68 | Form.input ~a:[a_class ["input"]]
69 | ~input_type:`Text ~name:product_name
70 | Form.string
71 | ]
72 | ] ;
73 | div ~a:[a_class ["field"]] [
74 | label ~a:[a_class ["label"]] [ pcdata (_t Field_price lang) ] ;
75 | p ~a:[a_class ["control"]] [
76 | Form.input ~a:[a_class ["input"]]
77 | ~input_type:`Text ~name:product_price
78 | Form.string
79 | ]
80 | ] ;
81 | div ~a:[a_class ["field"; "is-grouped"]] [
82 | div ~a:[a_class ["control"; "is-expanded"]] [
83 | Form.input ~a:[a_class ["input"]]
84 | ~input_type:`Submit ~value:(_t Save lang)
85 | Form.string ;
86 | ] ;
87 | div ~a:[a_class ["control"; "is-expanded"]] [
88 | a ~a:[a_class ["button"; "is-fullwidth"]] ~service:Service.main [
89 | pcdata (_t Quit lang)
90 | ] (Some lang, Some iso4217)
91 | ]
92 | ]
93 | ])
94 |
95 | ```
96 |
97 | When the user submits this form, the `POST` service is used:
98 |
99 | ```ocaml
100 | let form_add lang iso4217 () =
101 | Html.F.(
102 | body [
103 | div ~a:[a_class ["container"]] [
104 | navigation Title_add lang iso4217 ;
105 | div [
106 | (* Use the POST service to manage the form submit *)
107 | Form.post_form ~service:(snd Service.add) (create_form_add lang iso4217) (Some lang, Some iso4217)
108 | ]
109 | ]
110 | ]
111 | )
112 | ```
113 |
114 | And after adding the product, we display the new products list:
115 |
116 | ```ocaml
117 | let add lang iso4217 (product_code, (product_name, product_price)) =
118 | let () =
119 | let price =
120 | match iso4217 with
121 | | Money.Iso4217.Iso iso ->
122 | Money.(Vat.Amount(Vat.excl(Currency.of_string product_price iso)))
123 | in
124 | let p = Product.(empty
125 | |> set_code product_code
126 | |> set_names [ I18n.make lang product_name ]
127 | |> set_prices [ price ])
128 | in
129 | Model.add_product p
130 | in
131 | list lang iso4217 ()
132 | ```
133 |
134 |
135 | ## Products store
136 |
137 | For this tutorial, we simply store the products in a non persistent session variable initialized in `model.eliom`:
138 |
139 | ```ocaml
140 | let products = Eliom_reference.Volatile.eref
141 | ~scope:Eliom_common.default_session_scope
142 | [ p1; p2; p3 ]
143 | ```
144 |
145 | If you restart the server, the variable content is reset.
146 |
147 |
148 | ## Next step
149 |
150 | For now, the form is really raw. For instance, there is no field validation and you can't set several prices in different currencies. In the next tutorial, we will see how to add these features.
151 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/Makefile.options:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------------------------------------------------
3 | # SETTINGS FOR THE ELIOM PROJECT mysite
4 | #----------------------------------------------------------------------
5 |
6 | PROJECT_NAME := mysite
7 |
8 | # Source files for the server
9 | SERVER_FILES := $(wildcard *.eliomi *.eliom)
10 | # Source files for the client
11 | CLIENT_FILES := $(wildcard *.eliomi *.eliom)
12 |
13 | # OCamlfind packages for the server
14 | SERVER_PACKAGES := num lwt.ppx js_of_ocaml.deriving.ppx
15 | # OCamlfind packages for the client
16 | CLIENT_PACKAGES := num lwt.ppx js_of_ocaml.ppx js_of_ocaml.deriving.ppx
17 |
18 | # Directory with files to be statically served
19 | LOCAL_STATIC = static
20 |
21 | # The backend for persistent data. Can be dbm or sqlite.
22 | # Make sure you have the following packages installed
23 | # - *dbm* if you use dbm --> opam install dbm.
24 | # - *sqlite3* if you use sqlite --> opam install sqlite3.
25 | PERSISTENT_DATA_BACKEND = sqlite
26 |
27 | # Debug application (yes/no): Debugging info in compilation,
28 | # JavaScript, ocsigenserver
29 | DEBUG := no
30 |
31 | # User to run server with (make run.*)
32 | WWWUSER := www-data
33 | WWWGROUP := www-data
34 |
35 | # Port for running the server (make run.*)
36 | PORT := 80
37 |
38 | # Port for testing (make test.*)
39 | TEST_PORT := 8080
40 |
41 | # Root of installation (must end with /)
42 | PREFIX := /usr/local/
43 |
44 | # Local folder for make test.* (must end with /)
45 | # Do not add files manually in this directory.
46 | # It is just here to test your installation before installing in /
47 | TEST_PREFIX := local/
48 |
49 | # The installation tree (relative to $(PREFIX) when
50 | # installing/running or $(TEST_PREFIX) when testing).
51 | # Configuration file $(PROJECT_NAME).conf
52 | ETCDIR := etc/${PROJECT_NAME}
53 | # Project's library $(PROJECT_NAME).cma (cmxs)
54 | LIBDIR := lib/${PROJECT_NAME}
55 | # Command pipe, eg. $ echo reload > $(INSTALL_PREFIX)$(CMDPIPE)
56 | CMDPIPE := var/run/${PROJECT_NAME}-cmd
57 | # Ocsigenserver's logging files
58 | LOGDIR := var/log/${PROJECT_NAME}
59 | # Ocsigenserver's persistent data files
60 | DATADIR := var/data/${PROJECT_NAME}
61 | # Copy of $(LOCAL_STATIC)
62 | STATICDIR := var/www/${PROJECT_NAME}/static
63 | # Project's JavaScript file
64 | ELIOMSTATICDIR := var/www/${PROJECT_NAME}/eliom
65 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/README:
--------------------------------------------------------------------------------
1 |
2 | Instructions
3 | ============
4 |
5 | This project is (initially) generated by eliom-distillery as the basic
6 | project "mysite".
7 |
8 | Generally, you can compile it and run ocsigenserver on it by
9 | $ make test.byte (or test.opt)
10 | See below for other useful targets for make.
11 |
12 | Generated files
13 | ---------------
14 |
15 | The following files in this directory have been generated by
16 | eliom-distillery:
17 |
18 | - mysite.eliom
19 | This is your initial source file.
20 |
21 | - static/
22 | The content of this folder is statically served. Put your CSS or
23 | additional JavaScript files here!
24 |
25 | - Makefile.options
26 | Configure your project here!
27 |
28 | - mysite.conf.in
29 | This file is a template for the configuration file for
30 | ocsigenserver. You will rarely have to edit itself - it takes its
31 | variables from the Makefile.options. This way, the installation
32 | rules and the configuration files are synchronized with respect to
33 | the different folders.
34 |
35 | - Makefile
36 | This contains all rules necessary to build, test, and run your
37 | Eliom application. You better don't touch it ;) See below for the
38 | relevant targets.
39 |
40 | - local/
41 | This directory is the target of the temporary installation of
42 | your application, to test locally before doing a system-wide
43 | installation in /. Do not put anything manually here.
44 |
45 | - README
46 | Not completely describable here.
47 |
48 |
49 | Makefile targets
50 | ----------------
51 |
52 | Here's some help on how to work with this basic distillery project:
53 |
54 | - Test your application by compiling it and running ocsigenserver locally
55 | $ make test.byte (or test.opt)
56 |
57 | - Compile it only
58 | $ make all (or byte or opt)
59 |
60 | - Deploy your project on your system
61 | $ sudo make install (or install.byte or install.opt)
62 |
63 | - Run the server on the deployed project
64 | $ sudo make run.byte (or run.opt)
65 |
66 | If WWWUSER in the Makefile.options is you, you don't need the
67 | `sudo'. If Eliom isn't installed globally, however, you need to
68 | re-export some environment variables to make this work:
69 | $ sudo PATH=$PATH OCAMLPATH=$OCAMLPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH make run.byte/run.opt
70 |
71 | - If you need a findlib package in your project, add it to the
72 | variables SERVER_PACKAGES and/or CLIENT_PACKAGES. The configuration
73 | file will be automatically updated.
74 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/config.eliom:
--------------------------------------------------------------------------------
1 |
2 | let default_language = Language.En
3 |
4 | let default_iso4217 = Money.Iso4217.(Iso usd)
5 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/config.eliomi:
--------------------------------------------------------------------------------
1 |
2 | val default_language : Language.t
3 |
4 | val default_iso4217 : Money.Iso4217.iso
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/i18n.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t = Language.t * string
5 |
6 | let make lang s = (lang, s)
7 |
8 | let translate lang (l: t list) =
9 | try
10 | let s = List.assoc lang l in
11 | Some (lang, s)
12 | with
13 | | Not_found -> None
14 |
15 | let to_string (_, s) = s
16 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/i18n.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t
5 |
6 | val make : Language.t -> string -> t
7 | val translate : Language.t -> t list -> t option
8 | val to_string : t -> string
9 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/id.eliom:
--------------------------------------------------------------------------------
1 | type t = Int64.t
2 |
3 | let empty = Int64.zero
4 |
5 | let of_int64 v = v
6 |
7 | let to_string = Int64.to_string
8 |
9 | let compare = Int64.compare
10 |
11 | let succ = Int64.succ
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/id.eliomi:
--------------------------------------------------------------------------------
1 | type t
2 |
3 | val empty : t
4 | val of_int64 : Int64.t -> t
5 | val to_string : t -> string
6 | val compare : t -> t -> int
7 | val succ : t -> t
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/language.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | let of_string = function
11 | | "en" -> En
12 | | "fr" -> Fr
13 | | s -> raise(Unknown_language s)
14 |
15 | let to_string = function
16 | | En -> "en"
17 | | Fr -> "fr"
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/language.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | val of_string : string -> t
11 | val to_string : t -> string
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/model.eliom:
--------------------------------------------------------------------------------
1 |
2 | open Money
3 |
4 | let p1 =
5 | let price_usd = Vat.excl(Currency.(of_int 19 Iso4217.usd)) in
6 | let price_eur = Vat.exchange Iso4217.eur price_usd in
7 | Product.(
8 | empty
9 | |> set_id (Id.of_int64 1L)
10 | |> set_code "WG04"
11 | |> set_names I18n.(
12 | [ make Language.En "Waffle gun"; make Language.Fr "Pistolet à gaufre"])
13 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
14 | )
15 |
16 | let p2 =
17 | let price_eur = Vat.excl(Currency.(of_int 5 Iso4217.eur)) in
18 | let price_usd = Vat.exchange Iso4217.usd price_eur in
19 | Product.(
20 | empty
21 | |> set_id (Id.of_int64 2L)
22 | |> set_code "EW-2000"
23 | |> set_names I18n.(
24 | [ make Language.En "Electric whisker"; make Language.Fr "Tourniquette"])
25 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
26 | )
27 |
28 | let p3 =
29 | let price_usd = Vat.excl(Currency.(of_int 599 Iso4217.usd)) in
30 | let price_eur = Vat.exchange Iso4217.eur price_usd in
31 | Product.(
32 | empty
33 | |> set_id (Id.of_int64 3L)
34 | |> set_code "PROGLAGLA"
35 | |> set_names I18n.(
36 | [ make Language.En "Refrigerator"; make Language.Fr "Frigidaire"])
37 | |> set_prices Vat.([ Amount price_usd; Amount price_eur ])
38 | )
39 |
40 | let products = Eliom_reference.Volatile.eref
41 | ~scope:Eliom_common.default_session_scope
42 | [ p1; p2; p3 ]
43 |
44 | let get_new_id () =
45 | let l =
46 | Eliom_reference.Volatile.get products
47 | |> List.fast_sort (fun p1 p2 -> - (Id.compare (Product.get_id p1) (Product.get_id p2)))
48 | in
49 | match l with
50 | | [] -> Id.of_int64 Int64.one
51 | | v :: _ -> Id.succ (Product.get_id v)
52 |
53 | let get_products () =
54 | Eliom_reference.Volatile.get products
55 |
56 | let add_product p =
57 | let p =
58 | if Product.get_id p = Id.empty then
59 | Product.set_id (get_new_id ()) p
60 | else
61 | p
62 | in
63 | let l = Eliom_reference.Volatile.get products in
64 | Eliom_reference.Volatile.set products (p :: l)
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/model.eliomi:
--------------------------------------------------------------------------------
1 | val get_products : unit -> Product.t list
2 | val add_product : Product.t -> unit
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/money.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 = struct
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t =
12 | | Usd : usd t
13 | | Eur : eur t
14 |
15 | type iso = Iso : 'a t -> iso
16 |
17 | let iso v = Iso v
18 |
19 | let usd : usd t = Usd
20 |
21 | let eur : eur t = Eur
22 |
23 | let of_string (type a) (s:string) : iso =
24 | match s with
25 | | "usd" -> Iso Usd
26 | | "eur" -> Iso Eur
27 | | _ -> raise(Unknown_code s)
28 |
29 | let to_string (type a) (v:a t) : string =
30 | match v with
31 | | Usd -> "usd"
32 | | Eur -> "eur"
33 |
34 | let one = Num.num_of_int 1
35 |
36 | let exchange_rate (type a b) ~(for_one:a t) ~(get:b t) : Num.num =
37 | match for_one, get with
38 | | Usd, Eur -> Num.num_of_string "934813/1000000"
39 | | Eur, Usd -> Num.num_of_string "106951/100000"
40 | | Usd, Usd -> one
41 | | Eur, Eur -> one
42 |
43 | let symbol (type a) (v:a t) : string =
44 | match v with
45 | | Eur -> "€"
46 | | Usd -> "$"
47 |
48 | let eq (type a b) (v1:a t) (v2:b t) : bool =
49 | match v1, v2 with
50 | | Eur, Eur -> true
51 | | Usd, Usd -> true
52 | | _ -> false
53 |
54 | end
55 |
56 | module Currency = struct
57 |
58 | type 'a t = Num.num * 'a Iso4217.t
59 |
60 | type amount = Amount : 'a t -> amount
61 |
62 | let of_num (v:Num.num) (iso:'a Iso4217.t) : 'a t =
63 | (v, iso)
64 |
65 | let of_int v iso =
66 | of_num (Num.num_of_int v) iso
67 |
68 | let of_int64 v iso =
69 | of_num (Num.num_of_string(Int64.to_string v)) iso
70 |
71 | let of_string v iso =
72 | let accuracy_float = 10000. in
73 | let accuracy_int = 10000 in
74 | let x =
75 | (float_of_string v) *. accuracy_float
76 | |> Int64.of_float
77 | |> Int64.to_string
78 | |> Num.num_of_string
79 | in
80 | let x = Num.div_num x (Num.num_of_int accuracy_int) in
81 | of_num x iso
82 |
83 | let (+) (v1:'a t) (v2:'a t) : 'a t =
84 | let x1, iso = v1 in
85 | let x2, iso = v2 in
86 | (Num.add_num x1 x2, iso)
87 |
88 | let op_num op (n:Num.num) (v:'a t) : 'a t =
89 | let x, iso = v in
90 | (op x n, iso)
91 |
92 | let mult_by_num n v =
93 | op_num Num.mult_num n v
94 |
95 | let div_by_num n v =
96 | op_num Num.div_num n v
97 |
98 | let exchange (type a b) (to_iso: b Iso4217.t) (v: a t) : b t =
99 | let x, from_iso = v in
100 | (Num.mult_num x (Iso4217.exchange_rate ~for_one:from_iso ~get:to_iso), to_iso)
101 |
102 | let to_string_x f (x, _) =
103 | f x
104 |
105 | let to_string v =
106 | let f v =
107 | let s = Num.approx_num_fix 2 v in
108 | let s = String.sub s 1 ((String.length s) - 1) in
109 | if Num.sign_num v < 0 then
110 | "- " ^ s
111 | else
112 | s
113 | in
114 | to_string_x f v
115 |
116 | let to_string_fractional v =
117 | to_string_x Num.string_of_num v
118 |
119 | let to_string_decimal digits v =
120 | to_string_x (Num.approx_num_fix digits) v
121 |
122 | let to_string_scientific digits v =
123 | to_string_x (Num.approx_num_exp digits) v
124 |
125 | let symbol (_, iso) =
126 | Iso4217.symbol iso
127 |
128 | let filter_one_currency iso l =
129 | let f v =
130 | match v with
131 | | Amount (_, x) -> Iso4217.eq x iso
132 | in
133 | List.filter f l
134 |
135 | let same_currency (_, iso1) (_, iso2) =
136 | Iso4217.eq iso1 iso2
137 |
138 | end
139 |
140 | module Vat = struct
141 |
142 | type excl
143 | type incl
144 |
145 | type ('a, 'b) t =
146 | | Excl : 'a Currency.t -> ('a, excl) t
147 | | Incl : 'a Currency.t -> ('a, incl) t
148 |
149 | type amount = Amount : ('a, 'b) t -> amount
150 |
151 | let apply1_x (type b) f (v:('a, b) t) =
152 | match v with
153 | | Incl v -> f v
154 | | Excl v -> f v
155 |
156 | let apply1 (type b) f (v:('a, b) t) : ('a, b) t =
157 | match v with
158 | | Incl v -> Incl(f v)
159 | | Excl v -> Excl(f v)
160 |
161 | let apply2 (type b) f (v1:('a, b) t) (v2:('a, b) t) : ('a, b) t =
162 | match v1, v2 with
163 | | Incl v1, Incl v2 -> Incl(f v1 v2)
164 | | Excl v1, Excl v2 -> Excl(f v1 v2)
165 |
166 | let (+) v1 v2 =
167 | apply2 Currency.(+) v1 v2
168 |
169 | let mult_by_num x v =
170 | apply1 (Currency.mult_by_num x) v
171 |
172 | let div_by_num x v =
173 | apply1 (Currency.div_by_num x) v
174 |
175 | let exchange (type a b c) iso (v:(a, b) t) : (c, b) t =
176 | match v with
177 | | Incl v -> Incl(Currency.exchange iso v)
178 | | Excl v -> Excl(Currency.exchange iso v)
179 |
180 | let to_string v =
181 | apply1_x Currency.to_string v
182 |
183 | let to_string_fractional v =
184 | apply1_x Currency.to_string_fractional v
185 |
186 | let to_string_decimal digits =
187 | apply1_x (Currency.to_string_decimal digits)
188 |
189 | let to_string_scientific digits =
190 | apply1_x (Currency.to_string_scientific digits)
191 |
192 | let excl v = Excl v
193 | let incl v = Incl v
194 |
195 | let tax_rate =
196 | Num.num_of_string "120/100"
197 |
198 | let to_excl (v:('a, incl) t) : ('a, excl) t =
199 | match v with
200 | | Incl v -> Excl(Currency.div_by_num tax_rate v)
201 |
202 | let to_incl (v:('a, excl) t) : ('a, incl) t =
203 | match v with
204 | | Excl v -> Incl(Currency.mult_by_num tax_rate v)
205 |
206 | let symbol_currency (type b) (v:('a, b) t) =
207 | apply1_x Currency.symbol v
208 |
209 | let filter_one_currency iso l =
210 | let f v =
211 | match v with
212 | | Amount a ->
213 | match a with
214 | | Incl i -> Currency.same_currency i (Currency.of_int 0 iso)
215 | | Excl e -> Currency.same_currency e (Currency.of_int 0 iso)
216 | in
217 | List.filter f l
218 |
219 | let idiom (type b) lang (v:('a, b) t) : I18n.t option =
220 | let l =
221 | match v with
222 | | Excl _ -> [
223 | I18n.make Language.En "excl. VAT" ;
224 | I18n.make Language.Fr "HT" ;
225 | ]
226 | | Incl _ -> [
227 | I18n.make Language.En "incl. VAT" ;
228 | I18n.make Language.Fr "TTC" ;
229 | ]
230 | in
231 | I18n.translate lang l
232 |
233 | end
234 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/money.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 : sig
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t
12 |
13 | type iso = Iso : 'a t -> iso
14 |
15 | val iso : 'a t -> iso
16 |
17 | val usd : usd t
18 | val eur : eur t
19 |
20 | val of_string : string -> iso
21 | val to_string : 'a t -> string
22 | val symbol : 'a t -> string
23 |
24 | end
25 |
26 | module Currency : sig
27 |
28 | type 'a t
29 |
30 | type amount = Amount : 'a t -> amount
31 |
32 | val of_num : Num.num -> 'a Iso4217.t -> 'a t
33 | val of_int : int -> 'a Iso4217.t -> 'a t
34 | val of_int64 : Int64.t -> 'a Iso4217.t -> 'a t
35 | val of_string : string -> 'a Iso4217.t -> 'a t
36 |
37 | val (+) : 'a t -> 'a t -> 'a t
38 | val mult_by_num : Num.num -> 'a t -> 'a t
39 | val div_by_num : Num.num -> 'a t -> 'a t
40 |
41 | val exchange : 'a Iso4217.t -> 'b t -> 'a t
42 |
43 | val to_string : 'a t -> string
44 | val to_string_fractional : 'a t -> string
45 | val to_string_decimal : int -> 'a t -> string
46 | val to_string_scientific : int -> 'a t -> string
47 |
48 | val symbol : 'a t -> string
49 |
50 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
51 | val same_currency : 'a t -> 'a t -> bool
52 |
53 | end
54 |
55 | module Vat : sig
56 |
57 | type excl
58 | type incl
59 |
60 | type ('a, 'b) t
61 |
62 | type amount = Amount : ('a, 'b) t -> amount
63 |
64 | val (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
65 | val mult_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
66 | val div_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
67 |
68 | val exchange : 'a Iso4217.t -> ('b, 'c) t -> ('a, 'c) t
69 |
70 | val to_string : ('a, 'b) t -> string
71 | val to_string_fractional : ('a, 'b) t -> string
72 | val to_string_decimal : int -> ('a, 'b) t -> string
73 | val to_string_scientific : int -> ('a, 'b) t -> string
74 |
75 | val excl : 'a Currency.t -> ('a, excl) t
76 | val incl : 'a Currency.t -> ('a, incl) t
77 |
78 | val to_excl : ('a, incl) t -> ('a, excl) t
79 | val to_incl : ('a, excl) t -> ('a, incl) t
80 |
81 | val symbol_currency : ('a, 'b) t -> string
82 |
83 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
84 |
85 | val idiom : Language.t -> ('a, 'b) t -> I18n.t option
86 |
87 | end
88 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/mysite.conf.in:
--------------------------------------------------------------------------------
1 | %%% This is the template for your configuration file. The %%VALUES%% below are
2 | %%% taken from the Makefile to generate the actual configuration files.
3 | %%% This comment will disappear.
4 |
5 |
6 |
7 | %%PORT%%
8 | %%% Only set for running, not for testing
9 | %%USERGROUP%%
10 | %%LOGDIR%%
11 | %%DATADIR%%
12 | utf-8
13 | %%% Only set when debugging
14 | %%DEBUGMODE%%
15 | %%CMDPIPE%%
16 |
17 |
18 |
19 | %%% This will include the packages defined as SERVER_PACKAGES in your Makefile:
20 | %%PACKAGES%%
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/mysite.eliom:
--------------------------------------------------------------------------------
1 | [%%shared
2 | open Eliom_lib
3 | open Eliom_content
4 | open Html.D
5 | ]
6 |
7 | module Mysite_app =
8 | Eliom_registration.App (
9 | struct
10 | let application_name = "mysite"
11 | let global_data_path = None
12 | end)
13 |
14 | let () =
15 | let css_bulma =
16 | Eliom_content.Html.F.(
17 | css_link
18 | ~uri:(
19 | make_uri
20 | ~service:(Eliom_service.static_dir ())
21 | ~https:true ~hostname:"cdnjs.cloudflare.com"
22 | ["ajax"; "libs"; "bulma"; "0.4.0"; "css"; "bulma.min.css"]
23 | ) ()
24 | )
25 | in
26 | let css_mysite =
27 | Eliom_content.Html.F.(
28 | css_link
29 | ~uri:(
30 | make_uri
31 | ~service:(Eliom_service.static_dir ())
32 | ~absolute:true
33 | ["css";"mysite.css"]
34 | ) ()
35 | )
36 | in
37 |
38 | let page f lang iso4217 params =
39 | Eliom_tools.F.html
40 | ~title:"mysite"
41 | ~other_head:[css_bulma; css_mysite]
42 | (f lang iso4217 params)
43 | in
44 | let error_handler f params l =
45 | let html =
46 | match l with
47 | | []
48 | | ("lang", Language.Unknown_language _) :: [] ->
49 | page f Config.default_language Config.default_iso4217 params
50 | | ("iso4217", Money.Iso4217.Unknown_code _) :: [] ->
51 | page f Config.default_language Config.default_iso4217 params
52 | | (_, e) :: _ -> raise e
53 | in
54 | Lwt.return html
55 | in
56 | let register f (lang, iso4217) params =
57 | let html =
58 | match lang, iso4217 with
59 | | None, None -> page f Config.default_language Config.default_iso4217 params
60 | | Some vl, None -> page f vl Config.default_iso4217 params
61 | | None, Some vi -> page f Config.default_language vi params
62 | | Some vl, Some vi -> page f vl vi params
63 | in
64 | Lwt.return html
65 | in
66 | Mysite_app.register
67 | ~service:Service.main
68 | ~error_handler:(error_handler View.list ())
69 | (register View.list) ;
70 | Mysite_app.register
71 | ~service:(fst Service.add) (* GET to display the form *)
72 | ~error_handler:(error_handler View.form_add ())
73 | (register View.form_add) ;
74 | Mysite_app.register
75 | ~service:(snd Service.add) (* POST to add the product *)
76 | ~error_handler:(error_handler View.add ("", ("", "")))
77 | (register View.add) ;
78 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/product.eliom:
--------------------------------------------------------------------------------
1 |
2 | type t = {
3 | id : Id.t ;
4 | code : string ;
5 | names : I18n.t list ;
6 | prices : Money.Vat.amount list ;
7 | }
8 |
9 | let empty = {
10 | id = Id.empty ;
11 | code = "" ;
12 | names = [] ;
13 | prices = [] ;
14 | }
15 |
16 | let get_id v = v.id
17 |
18 | let set_id id v = { v with id }
19 |
20 | let get_code v = v.code
21 |
22 | let set_code code v = { v with code }
23 |
24 | let get_names v = v.names
25 |
26 | let set_names names v = { v with names }
27 |
28 | let get_prices v = v.prices
29 |
30 | let set_prices prices v = { v with prices }
31 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/product.eliomi:
--------------------------------------------------------------------------------
1 |
2 | type t
3 |
4 | val empty : t
5 |
6 | val get_id : t -> Id.t
7 | val set_id : Id.t -> t -> t
8 |
9 | val get_code : t -> string
10 | val set_code : string -> t -> t
11 |
12 | val get_names : t -> I18n.t list
13 | val set_names : I18n.t list -> t -> t
14 |
15 | val get_prices : t -> Money.Vat.amount list
16 | val set_prices : Money.Vat.amount list -> t -> t
17 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/service.eliom:
--------------------------------------------------------------------------------
1 | let lang_and_iso4217_parameters =
2 | Eliom_parameter.(
3 | (opt(
4 | user_type
5 | ~client_to_and_of:([%client { Eliom_parameter.of_string = Language.of_string; Eliom_parameter.to_string = Language.to_string } ])
6 | ~of_string:Language.of_string
7 | ~to_string:Language.to_string
8 | "lang"))
9 | **
10 | (opt(
11 | user_type
12 | ~client_to_and_of:([%client { Eliom_parameter.of_string = Money.Iso4217.of_string;
13 | Eliom_parameter.to_string = (fun v -> match v with Money.Iso4217.Iso iso -> Money.Iso4217.to_string iso) } ])
14 | ~of_string:Money.Iso4217.of_string
15 | ~to_string:(fun v -> match v with Money.Iso4217.Iso iso -> Money.Iso4217.to_string iso)
16 | "iso4217"))
17 | )
18 |
19 | let main =
20 | Eliom_service.create
21 | ~path:(Eliom_service.Path [])
22 | ~meth:(Eliom_service.Get lang_and_iso4217_parameters)
23 | ()
24 |
25 | let add =
26 | let path = Eliom_service.Path ["product"; "add"] in
27 | (Eliom_service.create ~path
28 | ~meth:(Eliom_service.Get lang_and_iso4217_parameters)
29 | (),
30 | Eliom_service.create ~path
31 | ~meth:(Eliom_service.Post (lang_and_iso4217_parameters,
32 | Eliom_parameter.(
33 | (string "code") ** (string "name") ** (string "price"))))
34 | ())
35 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/service.eliomi:
--------------------------------------------------------------------------------
1 | (*
2 | Trick to get the type:
3 | 1 - use (_, _, _, _, _, _, _, _, _, _, _) Eliom_service.t
4 | 2 - copy-paste the type from the compiler error message
5 | *)
6 |
7 | val main : (Language.t option * Money.Iso4217.iso option,
8 | unit,
9 | Eliom_service.get,
10 | Eliom_service.att,
11 | Eliom_service.non_co,
12 | Eliom_service.non_ext,
13 | Eliom_service.reg,
14 | [ `WithoutSuffix ],
15 | [ `One of Language.t ] Eliom_parameter.param_name * [ `One of Money.Iso4217.iso ] Eliom_parameter.param_name,
16 | unit,
17 | Eliom_service.non_ocaml)
18 | Eliom_service.t
19 |
20 | val add : (Language.t option * Money.Iso4217.iso option, unit,
21 | Eliom_service.get, Eliom_service.att, Eliom_service.non_co,
22 | Eliom_service.non_ext, Eliom_service.reg, [ `WithoutSuffix ],
23 | [ `One of Language.t ] Eliom_parameter.param_name *
24 | [ `One of Money.Iso4217.iso ] Eliom_parameter.param_name,
25 | unit, Eliom_service.non_ocaml)
26 | Eliom_service.t *
27 | (Language.t option * Money.Iso4217.iso option,
28 | string * (string * string), Eliom_service.post,
29 | Eliom_service.att, Eliom_service.non_co, Eliom_service.non_ext,
30 | Eliom_service.reg, [ `WithoutSuffix ],
31 | [ `One of Language.t ] Eliom_parameter.param_name *
32 | [ `One of Money.Iso4217.iso ] Eliom_parameter.param_name,
33 | [ `One of string ] Eliom_parameter.param_name *
34 | ([ `One of string ] Eliom_parameter.param_name *
35 | [ `One of string ] Eliom_parameter.param_name),
36 | Eliom_service.non_ocaml)
37 | Eliom_service.t
38 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/static/css/mysite.css:
--------------------------------------------------------------------------------
1 | .table th.price, .table td.price {
2 | text-align: right;
3 | }
4 |
--------------------------------------------------------------------------------
/eliom/tutorial/00400-add-product/src/mysite/view.eliomi:
--------------------------------------------------------------------------------
1 | val list : Language.t -> Money.Iso4217.iso -> unit -> [ `Body ] Eliom_content.Html.elt
2 | val form_add : Language.t -> Money.Iso4217.iso -> unit -> [ `Body ] Eliom_content.Html.elt
3 | val add : Language.t -> Money.Iso4217.iso -> (string * (string * string)) -> [ `Body ] Eliom_content.Html.elt
--------------------------------------------------------------------------------
/eliom/tutorial/Makefile:
--------------------------------------------------------------------------------
1 | copy:
2 | cp -f shared/* 00200-static-table/src/mysite/
3 | cp -f shared/* 00300-url-param/src/mysite/
4 | cp -f shared/* 00400-add-product/src/mysite/
5 |
6 | all: copy
7 | (cd 00100-hello-world/src/mysite/ && $(MAKE))
8 | (cd 00200-static-table/src/mysite/ && $(MAKE))
9 | (cd 00300-url-param/src/mysite/ && $(MAKE))
10 | (cd 00400-add-product/src/mysite/ && $(MAKE))
11 |
12 | distclean:
13 | (cd 00100-hello-world/src/mysite/ && $(MAKE) distclean)
14 | (cd 00200-static-table/src/mysite/ && $(MAKE) distclean)
15 | (cd 00300-url-param/src/mysite/ && $(MAKE) distclean)
16 | (cd 00400-add-product/src/mysite/ && $(MAKE) distclean)
17 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/config.eliom:
--------------------------------------------------------------------------------
1 |
2 | let default_language = Language.En
3 |
4 | let default_iso4217 = Money.Iso4217.(Iso usd)
5 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/config.eliomi:
--------------------------------------------------------------------------------
1 |
2 | val default_language : Language.t
3 |
4 | val default_iso4217 : Money.Iso4217.iso
--------------------------------------------------------------------------------
/eliom/tutorial/shared/i18n.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t = Language.t * string
5 |
6 | let make lang s = (lang, s)
7 |
8 | let translate lang (l: t list) =
9 | try
10 | let s = List.assoc lang l in
11 | Some (lang, s)
12 | with
13 | | Not_found -> None
14 |
15 | let to_string (_, s) = s
16 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/i18n.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | type t
5 |
6 | val make : Language.t -> string -> t
7 | val translate : Language.t -> t list -> t option
8 | val to_string : t -> string
9 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/id.eliom:
--------------------------------------------------------------------------------
1 | type t = Int64.t
2 |
3 | let empty = Int64.zero
4 |
5 | let of_int64 v = v
6 |
7 | let to_string = Int64.to_string
8 |
9 | let compare = Int64.compare
10 |
11 | let succ = Int64.succ
--------------------------------------------------------------------------------
/eliom/tutorial/shared/id.eliomi:
--------------------------------------------------------------------------------
1 | type t
2 |
3 | val empty : t
4 | val of_int64 : Int64.t -> t
5 | val to_string : t -> string
6 | val compare : t -> t -> int
7 | val succ : t -> t
--------------------------------------------------------------------------------
/eliom/tutorial/shared/language.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | let of_string = function
11 | | "en" -> En
12 | | "fr" -> Fr
13 | | s -> raise(Unknown_language s)
14 |
15 | let to_string = function
16 | | En -> "en"
17 | | Fr -> "fr"
--------------------------------------------------------------------------------
/eliom/tutorial/shared/language.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | exception Unknown_language of string
5 |
6 | type t =
7 | | En
8 | | Fr
9 |
10 | val of_string : string -> t
11 | val to_string : t -> string
--------------------------------------------------------------------------------
/eliom/tutorial/shared/money.eliom:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 = struct
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t =
12 | | Usd : usd t
13 | | Eur : eur t
14 |
15 | type iso = Iso : 'a t -> iso
16 |
17 | let iso v = Iso v
18 |
19 | let usd : usd t = Usd
20 |
21 | let eur : eur t = Eur
22 |
23 | let of_string (type a) (s:string) : iso =
24 | match s with
25 | | "usd" -> Iso Usd
26 | | "eur" -> Iso Eur
27 | | _ -> raise(Unknown_code s)
28 |
29 | let to_string (type a) (v:a t) : string =
30 | match v with
31 | | Usd -> "usd"
32 | | Eur -> "eur"
33 |
34 | let one = Num.num_of_int 1
35 |
36 | let exchange_rate (type a b) ~(for_one:a t) ~(get:b t) : Num.num =
37 | match for_one, get with
38 | | Usd, Eur -> Num.num_of_string "934813/1000000"
39 | | Eur, Usd -> Num.num_of_string "106951/100000"
40 | | Usd, Usd -> one
41 | | Eur, Eur -> one
42 |
43 | let symbol (type a) (v:a t) : string =
44 | match v with
45 | | Eur -> "€"
46 | | Usd -> "$"
47 |
48 | let eq (type a b) (v1:a t) (v2:b t) : bool =
49 | match v1, v2 with
50 | | Eur, Eur -> true
51 | | Usd, Usd -> true
52 | | _ -> false
53 |
54 | end
55 |
56 | module Currency = struct
57 |
58 | type 'a t = Num.num * 'a Iso4217.t
59 |
60 | type amount = Amount : 'a t -> amount
61 |
62 | let of_num (v:Num.num) (iso:'a Iso4217.t) : 'a t =
63 | (v, iso)
64 |
65 | let of_int v iso =
66 | of_num (Num.num_of_int v) iso
67 |
68 | let of_int64 v iso =
69 | of_num (Num.num_of_string(Int64.to_string v)) iso
70 |
71 | let of_string v iso =
72 | let accuracy_float = 10000. in
73 | let accuracy_int = 10000 in
74 | let x =
75 | (float_of_string v) *. accuracy_float
76 | |> Int64.of_float
77 | |> Int64.to_string
78 | |> Num.num_of_string
79 | in
80 | let x = Num.div_num x (Num.num_of_int accuracy_int) in
81 | of_num x iso
82 |
83 | let (+) (v1:'a t) (v2:'a t) : 'a t =
84 | let x1, iso = v1 in
85 | let x2, iso = v2 in
86 | (Num.add_num x1 x2, iso)
87 |
88 | let op_num op (n:Num.num) (v:'a t) : 'a t =
89 | let x, iso = v in
90 | (op x n, iso)
91 |
92 | let mult_by_num n v =
93 | op_num Num.mult_num n v
94 |
95 | let div_by_num n v =
96 | op_num Num.div_num n v
97 |
98 | let exchange (type a b) (to_iso: b Iso4217.t) (v: a t) : b t =
99 | let x, from_iso = v in
100 | (Num.mult_num x (Iso4217.exchange_rate ~for_one:from_iso ~get:to_iso), to_iso)
101 |
102 | let to_string_x f (x, _) =
103 | f x
104 |
105 | let to_string v =
106 | let f v =
107 | let s = Num.approx_num_fix 2 v in
108 | let s = String.sub s 1 ((String.length s) - 1) in
109 | if Num.sign_num v < 0 then
110 | "- " ^ s
111 | else
112 | s
113 | in
114 | to_string_x f v
115 |
116 | let to_string_fractional v =
117 | to_string_x Num.string_of_num v
118 |
119 | let to_string_decimal digits v =
120 | to_string_x (Num.approx_num_fix digits) v
121 |
122 | let to_string_scientific digits v =
123 | to_string_x (Num.approx_num_exp digits) v
124 |
125 | let symbol (_, iso) =
126 | Iso4217.symbol iso
127 |
128 | let filter_one_currency iso l =
129 | let f v =
130 | match v with
131 | | Amount (_, x) -> Iso4217.eq x iso
132 | in
133 | List.filter f l
134 |
135 | let same_currency (_, iso1) (_, iso2) =
136 | Iso4217.eq iso1 iso2
137 |
138 | end
139 |
140 | module Vat = struct
141 |
142 | type excl
143 | type incl
144 |
145 | type ('a, 'b) t =
146 | | Excl : 'a Currency.t -> ('a, excl) t
147 | | Incl : 'a Currency.t -> ('a, incl) t
148 |
149 | type amount = Amount : ('a, 'b) t -> amount
150 |
151 | let apply1_x (type b) f (v:('a, b) t) =
152 | match v with
153 | | Incl v -> f v
154 | | Excl v -> f v
155 |
156 | let apply1 (type b) f (v:('a, b) t) : ('a, b) t =
157 | match v with
158 | | Incl v -> Incl(f v)
159 | | Excl v -> Excl(f v)
160 |
161 | let apply2 (type b) f (v1:('a, b) t) (v2:('a, b) t) : ('a, b) t =
162 | match v1, v2 with
163 | | Incl v1, Incl v2 -> Incl(f v1 v2)
164 | | Excl v1, Excl v2 -> Excl(f v1 v2)
165 |
166 | let (+) v1 v2 =
167 | apply2 Currency.(+) v1 v2
168 |
169 | let mult_by_num x v =
170 | apply1 (Currency.mult_by_num x) v
171 |
172 | let div_by_num x v =
173 | apply1 (Currency.div_by_num x) v
174 |
175 | let exchange (type a b c) iso (v:(a, b) t) : (c, b) t =
176 | match v with
177 | | Incl v -> Incl(Currency.exchange iso v)
178 | | Excl v -> Excl(Currency.exchange iso v)
179 |
180 | let to_string v =
181 | apply1_x Currency.to_string v
182 |
183 | let to_string_fractional v =
184 | apply1_x Currency.to_string_fractional v
185 |
186 | let to_string_decimal digits =
187 | apply1_x (Currency.to_string_decimal digits)
188 |
189 | let to_string_scientific digits =
190 | apply1_x (Currency.to_string_scientific digits)
191 |
192 | let excl v = Excl v
193 | let incl v = Incl v
194 |
195 | let tax_rate =
196 | Num.num_of_string "120/100"
197 |
198 | let to_excl (v:('a, incl) t) : ('a, excl) t =
199 | match v with
200 | | Incl v -> Excl(Currency.div_by_num tax_rate v)
201 |
202 | let to_incl (v:('a, excl) t) : ('a, incl) t =
203 | match v with
204 | | Excl v -> Incl(Currency.mult_by_num tax_rate v)
205 |
206 | let symbol_currency (type b) (v:('a, b) t) =
207 | apply1_x Currency.symbol v
208 |
209 | let filter_one_currency iso l =
210 | let f v =
211 | match v with
212 | | Amount a ->
213 | match a with
214 | | Incl i -> Currency.same_currency i (Currency.of_int 0 iso)
215 | | Excl e -> Currency.same_currency e (Currency.of_int 0 iso)
216 | in
217 | List.filter f l
218 |
219 | let idiom (type b) lang (v:('a, b) t) : I18n.t option =
220 | let l =
221 | match v with
222 | | Excl _ -> [
223 | I18n.make Language.En "excl. VAT" ;
224 | I18n.make Language.Fr "HT" ;
225 | ]
226 | | Incl _ -> [
227 | I18n.make Language.En "incl. VAT" ;
228 | I18n.make Language.Fr "TTC" ;
229 | ]
230 | in
231 | I18n.translate lang l
232 |
233 | end
234 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/money.eliomi:
--------------------------------------------------------------------------------
1 |
2 | [%%shared.start]
3 |
4 | module Iso4217 : sig
5 |
6 | exception Unknown_code of string
7 |
8 | type usd
9 | type eur
10 |
11 | type 'a t
12 |
13 | type iso = Iso : 'a t -> iso
14 |
15 | val iso : 'a t -> iso
16 |
17 | val usd : usd t
18 | val eur : eur t
19 |
20 | val of_string : string -> iso
21 | val to_string : 'a t -> string
22 | val symbol : 'a t -> string
23 |
24 | end
25 |
26 | module Currency : sig
27 |
28 | type 'a t
29 |
30 | type amount = Amount : 'a t -> amount
31 |
32 | val of_num : Num.num -> 'a Iso4217.t -> 'a t
33 | val of_int : int -> 'a Iso4217.t -> 'a t
34 | val of_int64 : Int64.t -> 'a Iso4217.t -> 'a t
35 | val of_string : string -> 'a Iso4217.t -> 'a t
36 |
37 | val (+) : 'a t -> 'a t -> 'a t
38 | val mult_by_num : Num.num -> 'a t -> 'a t
39 | val div_by_num : Num.num -> 'a t -> 'a t
40 |
41 | val exchange : 'a Iso4217.t -> 'b t -> 'a t
42 |
43 | val to_string : 'a t -> string
44 | val to_string_fractional : 'a t -> string
45 | val to_string_decimal : int -> 'a t -> string
46 | val to_string_scientific : int -> 'a t -> string
47 |
48 | val symbol : 'a t -> string
49 |
50 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
51 | val same_currency : 'a t -> 'a t -> bool
52 |
53 | end
54 |
55 | module Vat : sig
56 |
57 | type excl
58 | type incl
59 |
60 | type ('a, 'b) t
61 |
62 | type amount = Amount : ('a, 'b) t -> amount
63 |
64 | val (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
65 | val mult_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
66 | val div_by_num : Num.num -> ('a, 'b) t -> ('a, 'b) t
67 |
68 | val exchange : 'a Iso4217.t -> ('b, 'c) t -> ('a, 'c) t
69 |
70 | val to_string : ('a, 'b) t -> string
71 | val to_string_fractional : ('a, 'b) t -> string
72 | val to_string_decimal : int -> ('a, 'b) t -> string
73 | val to_string_scientific : int -> ('a, 'b) t -> string
74 |
75 | val excl : 'a Currency.t -> ('a, excl) t
76 | val incl : 'a Currency.t -> ('a, incl) t
77 |
78 | val to_excl : ('a, incl) t -> ('a, excl) t
79 | val to_incl : ('a, excl) t -> ('a, incl) t
80 |
81 | val symbol_currency : ('a, 'b) t -> string
82 |
83 | val filter_one_currency : 'a Iso4217.t -> amount list -> amount list
84 |
85 | val idiom : Language.t -> ('a, 'b) t -> I18n.t option
86 |
87 | end
88 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/product.eliom:
--------------------------------------------------------------------------------
1 |
2 | type t = {
3 | id : Id.t ;
4 | code : string ;
5 | names : I18n.t list ;
6 | prices : Money.Vat.amount list ;
7 | }
8 |
9 | let empty = {
10 | id = Id.empty ;
11 | code = "" ;
12 | names = [] ;
13 | prices = [] ;
14 | }
15 |
16 | let get_id v = v.id
17 |
18 | let set_id id v = { v with id }
19 |
20 | let get_code v = v.code
21 |
22 | let set_code code v = { v with code }
23 |
24 | let get_names v = v.names
25 |
26 | let set_names names v = { v with names }
27 |
28 | let get_prices v = v.prices
29 |
30 | let set_prices prices v = { v with prices }
31 |
--------------------------------------------------------------------------------
/eliom/tutorial/shared/product.eliomi:
--------------------------------------------------------------------------------
1 |
2 | type t
3 |
4 | val empty : t
5 |
6 | val get_id : t -> Id.t
7 | val set_id : Id.t -> t -> t
8 |
9 | val get_code : t -> string
10 | val set_code : string -> t -> t
11 |
12 | val get_names : t -> I18n.t list
13 | val set_names : I18n.t list -> t -> t
14 |
15 | val get_prices : t -> Money.Vat.amount list
16 | val set_prices : Money.Vat.amount list -> t -> t
17 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/.depend:
--------------------------------------------------------------------------------
1 | _server/mixvdomandeliom.cmo :
2 | _server/mixvdomandeliom.cmx :
3 | _server/mixvdomandeliom.cmo : _server/mixvdomandeliom.type_mli
4 | _server/mixvdomandeliom.cmx : _server/mixvdomandeliom.type_mli
5 | _client/mixvdomandeliom.cmo :
6 | _client/mixvdomandeliom.cmx :
7 | _client/mixvdomandeliom.cmo : _server/mixvdomandeliom.type_mli
8 | _client/mixvdomandeliom.cmx : _server/mixvdomandeliom.type_mli
9 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/.ocp-indent:
--------------------------------------------------------------------------------
1 | normal
2 | with=0
3 | syntax=lwt mll
4 | max_indent=2
5 | ppx_stritem_ext=0
6 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/Makefile.options:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------------------------------------------------
3 | # SETTINGS FOR THE ELIOM PROJECT mixvdomandeliom
4 | #----------------------------------------------------------------------
5 |
6 | PROJECT_NAME := mixvdomandeliom
7 |
8 | # Source files for the server
9 | SERVER_FILES := $(wildcard *.eliomi *.eliom)
10 | # Source files for the client
11 | CLIENT_FILES := $(wildcard *.eliomi *.eliom)
12 |
13 | # OCamlfind packages for the server
14 | SERVER_PACKAGES :=
15 | # OCamlfind packages for the client
16 | CLIENT_PACKAGES := lwt.ppx ocaml-vdom
17 |
18 | # Directory with files to be statically served
19 | LOCAL_STATIC = static
20 |
21 | # The backend for persistent data. Can be dbm or sqlite.
22 | # Make sure you have the following packages installed
23 | # - *dbm* if you use dbm --> opam install dbm.
24 | # - *sqlite3* if you use sqlite --> opam install sqlite3.
25 | PERSISTENT_DATA_BACKEND = sqlite
26 |
27 | # Debug application (yes/no): Debugging info in compilation,
28 | # JavaScript, ocsigenserver
29 | DEBUG := no
30 |
31 | # User to run server with (make run.*)
32 | WWWUSER := www-data
33 | WWWGROUP := www-data
34 |
35 | # Port for running the server (make run.*)
36 | PORT := 80
37 |
38 | # Port for testing (make test.*)
39 | TEST_PORT := 8080
40 |
41 | # Root of installation (must end with /)
42 | PREFIX := /usr/local/
43 |
44 | # Local folder for make test.* (must end with /)
45 | # Do not add files manually in this directory.
46 | # It is just here to test your installation before installing in /
47 | TEST_PREFIX := local/
48 |
49 | # The installation tree (relative to $(PREFIX) when
50 | # installing/running or $(TEST_PREFIX) when testing).
51 | # Configuration file $(PROJECT_NAME).conf
52 | ETCDIR := etc/${PROJECT_NAME}
53 | # Project's library $(PROJECT_NAME).cma (cmxs)
54 | LIBDIR := lib/${PROJECT_NAME}
55 | # Command pipe, eg. $ echo reload > $(INSTALL_PREFIX)$(CMDPIPE)
56 | CMDPIPE := var/run/${PROJECT_NAME}-cmd
57 | # Ocsigenserver's logging files
58 | LOGDIR := var/log/${PROJECT_NAME}
59 | # Ocsigenserver's persistent data files
60 | DATADIR := var/data/${PROJECT_NAME}
61 | # Copy of $(LOCAL_STATIC)
62 | STATICDIR := var/www/${PROJECT_NAME}/static
63 | # Project's JavaScript file
64 | ELIOMSTATICDIR := var/www/${PROJECT_NAME}/eliom
65 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/README:
--------------------------------------------------------------------------------
1 |
2 | Instructions
3 | ============
4 |
5 | This project is (initially) generated by eliom-distillery as the basic
6 | project "mixvdomandeliom".
7 |
8 | Generally, you can compile it and run ocsigenserver on it by
9 | $ make test.byte (or test.opt)
10 | See below for other useful targets for make.
11 |
12 | Generated files
13 | ---------------
14 |
15 | The following files in this directory have been generated by
16 | eliom-distillery:
17 |
18 | - mixvdomandeliom.eliom
19 | This is your initial source file.
20 |
21 | - static/
22 | The content of this folder is statically served. Put your CSS or
23 | additional JavaScript files here!
24 |
25 | - Makefile.options
26 | Configure your project here!
27 |
28 | - mixvdomandeliom.conf.in
29 | This file is a template for the configuration file for
30 | ocsigenserver. You will rarely have to edit itself - it takes its
31 | variables from the Makefile.options. This way, the installation
32 | rules and the configuration files are synchronized with respect to
33 | the different folders.
34 |
35 | - Makefile
36 | This contains all rules necessary to build, test, and run your
37 | Eliom application. You better don't touch it ;) See below for the
38 | relevant targets.
39 |
40 | - local/
41 | This directory is the target of the temporary installation of
42 | your application, to test locally before doing a system-wide
43 | installation in /. Do not put anything manually here.
44 |
45 | - README
46 | Not completely describable here.
47 |
48 |
49 | Makefile targets
50 | ----------------
51 |
52 | Here's some help on how to work with this basic distillery project:
53 |
54 | - Test your application by compiling it and running ocsigenserver locally
55 | $ make test.byte (or test.opt)
56 |
57 | - Compile it only
58 | $ make all (or byte or opt)
59 |
60 | - Deploy your project on your system
61 | $ sudo make install (or install.byte or install.opt)
62 |
63 | - Run the server on the deployed project
64 | $ sudo make run.byte (or run.opt)
65 |
66 | If WWWUSER in the Makefile.options is you, you don't need the
67 | `sudo'. If Eliom isn't installed globally, however, you need to
68 | re-export some environment variables to make this work:
69 | $ sudo PATH=$PATH OCAMLPATH=$OCAMLPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH make run.byte/run.opt
70 |
71 | - If you need a findlib package in your project, add it to the
72 | variables SERVER_PACKAGES and/or CLIENT_PACKAGES. The configuration
73 | file will be automatically updated.
74 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/README.md:
--------------------------------------------------------------------------------
1 | How to use Eliom services with OCaml Vdom. Tested with Eliom 6.1 and OCaml Vdom 0.1.
2 | It's mainly a proof of concept. Nonetheless, it could be an interesting lighter approach compared to a classic Eliom project. One downside is that you loose the safety given by TyXML. But the client code is simpler.
3 | Note that `Makefile` and `Makefile.options` are different from the default files:
4 | - In `Makefile` you must add `-no-check-prims -jsopt +gen_js_api/ojs_runtime.js` to `JS_OF_ELIOM`
5 | - In `Makefile.options` you must add `ocaml-vdom` to `CLIENT_PACKAGES`
6 |
7 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/mixvdomandeliom.conf.in:
--------------------------------------------------------------------------------
1 | %%% This is the template for your configuration file. The %%VALUES%% below are
2 | %%% taken from the Makefile to generate the actual configuration files.
3 | %%% This comment will disappear.
4 |
5 |
6 |
7 | %%PORT%%
8 | %%% Only set for running, not for testing
9 | %%USERGROUP%%
10 | %%LOGDIR%%
11 | %%DATADIR%%
12 | utf-8
13 | %%% Only set when debugging
14 | %%DEBUGMODE%%
15 | %%CMDPIPE%%
16 |
17 |
18 |
19 | %%% This will include the packages defined as SERVER_PACKAGES in your Makefile:
20 | %%PACKAGES%%
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/mixvdomandeliom.eliom:
--------------------------------------------------------------------------------
1 |
2 | module Service = struct
3 |
4 | let random_value =
5 | Eliom_registration.Ocaml.create
6 | (* having a path is better to avoid broken links if the user doesn't refresh the page after a server restart *)
7 | ~path:(Eliom_service.Path ["rnd"])
8 | ~meth:(Eliom_service.Get (Eliom_parameter.unit))
9 | (fun () () ->
10 | let i = Random.int 200 in
11 | if i > 100 then
12 | Lwt.return_ok(Printf.sprintf "Ok: random value %u > 100" i)
13 | else
14 | Lwt.return_error(Printf.sprintf "Error: random value %u <= 100" i)
15 | )
16 |
17 | end
18 |
19 | [%%client
20 |
21 | type 'msg Vdom.Cmd.t +=
22 | | Service of { on_ok: (string -> 'msg); on_error: (string -> 'msg) }
23 |
24 | let create_service ~on_ok ~on_error = Service { on_ok; on_error }
25 |
26 | let run_service ~on_ok ~on_error () =
27 | let%lwt v = Eliom_client.call_ocaml_service ~service:~%Service.random_value () () in
28 | match v with
29 | | Ok v -> on_ok v
30 | | Error v -> on_error v
31 |
32 | let cmd_handler ctx = function
33 | | Service { on_ok; on_error } ->
34 | let _ = run_service
35 | ~on_ok:(fun s -> Lwt.return(Vdom_blit.Cmd.send_msg ctx (on_ok s)))
36 | ~on_error:(fun s -> Lwt.return(Vdom_blit.Cmd.send_msg ctx (on_error s)))
37 | ()
38 | in
39 | true
40 |
41 | let () = Vdom_blit.(register (cmd {Vdom_blit.Cmd.f = cmd_handler}))
42 |
43 | type model = {
44 | counter_ok : int ;
45 | counter_error : int ;
46 | message : string ;
47 | }
48 |
49 | type action =
50 | | Call_service
51 | | Service_called_ok of string
52 | | Service_called_error of string
53 |
54 | let update m = function
55 | | Call_service -> (
56 | Vdom.return m ~c:[create_service ~on_ok:(fun r -> Service_called_ok r) ~on_error:(fun r -> Service_called_error r)]
57 | )
58 | | Service_called_ok v -> Vdom.return { m with counter_ok = m.counter_ok + 1; message = v }
59 | | Service_called_error v -> Vdom.return { m with counter_error = m.counter_error + 1; message = v }
60 |
61 | let init = Vdom.return { counter_ok = 0; counter_error = 0; message = "No message" }
62 |
63 | let button txt msg =
64 | Vdom.(input [] ~a:[onclick msg; type_button; value txt])
65 |
66 | let view m =
67 | Vdom.(
68 | div [
69 | div [
70 | button "Fetch a random value from server" Call_service
71 | ] ;
72 | div [
73 | text (Printf.sprintf "%u OK value(s)" m.counter_ok) ;
74 | text " / " ;
75 | text (Printf.sprintf "%u ERROR value(s)" m.counter_error) ;
76 | ] ;
77 | div [
78 | text m.message
79 | ] ;
80 | ]
81 | )
82 |
83 | let app = Vdom.app ~init ~view ~update ()
84 |
85 | let run () =
86 | Vdom_blit.run app
87 | |> Vdom_blit.dom
88 | |> Js_browser.Element.append_child (Js_browser.Document.body Js_browser.document)
89 | let () = Js_browser.Window.set_onload Js_browser.window run
90 | ]
91 |
92 | module Mixvdomandeliom_app =
93 | Eliom_registration.App (
94 | struct
95 | let application_name = "mixvdomandeliom"
96 | let global_data_path = None
97 | end)
98 |
99 | let main_service =
100 | Eliom_service.create
101 | ~path:(Eliom_service.Path [])
102 | ~meth:(Eliom_service.Get Eliom_parameter.unit)
103 | ()
104 |
105 | let () =
106 | Mixvdomandeliom_app.register
107 | ~service:main_service
108 | (fun () () ->
109 | Lwt.return
110 | (Eliom_tools.F.html
111 | ~title:"mixvdomandeliom"
112 | ~css:[["css";"mixvdomandeliom.css"]]
113 | Eliom_content.Html.F.(body [
114 | h1 [pcdata "Demo mix Vdom / Eliom"];
115 | ])))
116 |
--------------------------------------------------------------------------------
/eliom/with-ocaml-vdom/simple/static/css/mixvdomandeliom.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: sans-serif;
3 | }
4 |
5 | h1, div {
6 | text-align: center;
7 | padding-bottom: 2em;
8 | }
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/blob.ml:
--------------------------------------------------------------------------------
1 | open Js_of_ocaml
2 |
3 | class type options = object
4 | method _type : string Js.readonly_prop
5 | end
6 |
7 | let blob_uint8 = Js.Unsafe.global##._Blob
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/blob.mli:
--------------------------------------------------------------------------------
1 | open Js_of_ocaml
2 |
3 | class type options = object
4 | method _type : string Js.readonly_prop
5 | end
6 |
7 | val blob_uint8 : (Typed_array.uint8Array Js.t Js.js_array Js.t -> options Js.t -> File.blob Js.t) Js.constr
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/build.sh:
--------------------------------------------------------------------------------
1 | for f in `find . -name '*.ml*'` ; \
2 | do ( \
3 | ocp-indent -i $f; \
4 | ); \
5 | done
6 |
7 | ocamlbuild -use-ocamlfind \
8 | -pkgs lwt_ppx,js_of_ocaml-lwt,js_of_ocaml-ppx,js_of_ocaml-tyxml,tyxml,react,reactiveData \
9 | main.byte ;
10 |
11 | js_of_ocaml --opt 3 -o www/js/main.js main.byte
12 |
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/main.ml:
--------------------------------------------------------------------------------
1 |
2 | (*
3 | Polyfill for the Encoding Living Standard's API:
4 | https://github.com/inexorabletash/text-encoding
5 | *)
6 |
7 | open Lwt.Infix
8 | open Js_of_ocaml
9 |
10 | module Model = struct
11 |
12 | type t =
13 | (* tiny href * big href *)
14 | Js.js_string Js.t option * Js.js_string Js.t option
15 |
16 | let csv_sep = ","
17 |
18 | let nb_lines_tiny = 3
19 | let nb_cols_tiny = 4
20 |
21 | let nb_lines_big = 400
22 | let nb_cols_big = 20
23 |
24 | let make_data nb_lines nb_cols =
25 | let data = Array.make_matrix nb_lines nb_cols "" in
26 | for i = 0 to (nb_lines - 1) do
27 | for j = 0 to (nb_cols - 1) do
28 | data.(i).(j) <- Printf.sprintf "Cell é è ç à ù € ∀ ◉ %u.%u" i j
29 | done ;
30 | done ;
31 | data
32 |
33 | let data_tiny = make_data nb_lines_tiny nb_cols_tiny
34 |
35 | let data_big = make_data nb_lines_big nb_cols_big
36 |
37 | let empty = (None, None)
38 |
39 | let csv_headers data =
40 | let nb_cols = Array.length data.(0) in
41 | let s = ref "" in
42 | for i = 0 to (nb_cols - 1) do
43 | if i = (nb_cols - 1) then
44 | s := !s ^ (Printf.sprintf "Column %u" i)
45 | else
46 | s := !s ^ (Printf.sprintf "Column %u%s" i csv_sep)
47 | done ;
48 | !s
49 |
50 | let csv_body data =
51 | let nb_cols = Array.length data.(0) in
52 | let s = ref "" in
53 | let column i v =
54 | if i > 0 && i < nb_cols then
55 | s := !s ^ csv_sep ^ v
56 | else
57 | s := !s ^ v
58 | in
59 | let line l =
60 | Array.iteri column l ;
61 | s := !s ^ "\n"
62 | in
63 | Array.iter line data ;
64 | !s
65 |
66 | let csv data =
67 | (csv_headers data) ^ "\n" ^ (csv_body data)
68 |
69 | end
70 |
71 | type rs = Model.t React.signal
72 | type rf = ?step:React.step -> Model.t -> unit
73 | type rp = rs * rf
74 |
75 | module Action = struct
76 |
77 | type t =
78 | | Download_tiny_csv
79 | | Download_big_csv
80 |
81 | end
82 |
83 | module Controller = struct
84 |
85 | open Action
86 |
87 | let encode_href href v =
88 | let blob = Blob.blob_uint8 in
89 | let encoder = TextEncoder.textEncoder in
90 | let url = (Dom_html.window)##._URL in
91 |
92 | let () =
93 | match href with
94 | | None -> ()
95 | | Some h -> url##revokeObjectURL h
96 | in
97 |
98 | (* add BOM as first character to force the right encoding
99 | when the url is opened by the spreadsheet software *)
100 | let utf8_bom = new%js Typed_array.uint8Array 3 in
101 | let () = Typed_array.set utf8_bom 0 0xEF in
102 | let () = Typed_array.set utf8_bom 1 0xBB in
103 | let () = Typed_array.set utf8_bom 2 0xBF in
104 |
105 | let opt = object%js
106 | val _type = "text/csv;charset=utf-8"
107 | end in
108 | let e = new%js encoder in
109 | let data = e##encode_uint8 (Js.string v) in
110 | let o = new%js blob (Js.array [| utf8_bom; data |]) opt in
111 | let url = url##createObjectURL o in
112 | url
113 |
114 | let update a ((rs, rf) : rp) =
115 | let href_tiny, href_big = React.S.value rs in
116 | let m =
117 | match a with
118 | | Download_tiny_csv ->
119 | let csv = Model.(csv data_tiny) in
120 | let href_tiny = encode_href href_tiny csv in
121 | (Some href_tiny, href_big)
122 | | Download_big_csv ->
123 | let csv = Model.(csv data_big) in
124 | let href_big = encode_href href_big csv in
125 | (href_tiny, Some href_big)
126 | in
127 | rf m
128 |
129 | end
130 |
131 | module View = struct
132 |
133 | open Action
134 | open Js_of_ocaml_tyxml.Tyxml_js
135 |
136 | let link_tiny_csv (rs, rf) =
137 | let onclick evt =
138 | Controller.update Download_tiny_csv (rs, rf) ;
139 | true (* return true because we must follow the link *)
140 | in
141 | let href = React.S.map (fun (v, _) -> match v with None -> "#" | Some h -> Js.to_string h) rs in
142 | Html.(a ~a:[a_onclick onclick; a_download (Some "tinyfile.csv");
143 | R.Html.a_href href] [txt "Get CSV from above"])
144 |
145 | let link_big_csv (rs, rf) =
146 | let onclick evt =
147 | Controller.update Download_big_csv (rs, rf) ;
148 | true (* return true because we must follow the link *)
149 | in
150 | let href = React.S.map (fun (_, v) -> match v with None -> "#" | Some h -> Js.to_string h) rs in
151 | Html.(a ~a:[a_onclick onclick; a_download (Some "bigfile.csv");
152 | R.Html.a_href href] [txt "Get bigger CSV (not displayed)"])
153 |
154 | let table (rs, rf) =
155 | let cells data =
156 | let line (acc, i) l =
157 | let f (acc, j) v =
158 | let td = Html.(td [txt v]) in
159 | (td :: acc, j + 1)
160 | in
161 | let (ltd, _) = Array.fold_left f ([], 0) l in
162 | let ltd = List.rev ltd in
163 | (Html.(tr ltd) :: acc, i + 1)
164 | in
165 | let (ltd, _) = Array.fold_left line ([], 0) data in
166 | List.rev ltd
167 | in
168 | Html.(tablex [ Html.tbody (cells Model.data_tiny) ])
169 |
170 | let view (rs, rf) =
171 | let cells = table (rs, rf) in
172 | Html.(
173 | div [
174 | p ~a:[a_class ["comments"]] [
175 | txt "How to build and download a CSV file without any server processing and only from client side data." ;
176 | br () ;
177 | txt "This demo has been tested with Firefox, Chrome and Edge. But please note that a " ;
178 | a ~a:[a_href "https://github.com/inexorabletash/text-encoding"] [ txt "polyfill" ] ;
179 | txt " is necessary for Edge." ;
180 | ] ;
181 | div ~a:[a_class ["cells"]] [ cells ] ;
182 | div ~a:[a_class ["tiny-csv"]] [ link_tiny_csv (rs, rf) ] ;
183 | div ~a:[a_class ["big-csv"]] [ link_big_csv (rs, rf) ] ;
184 | ]
185 | )
186 |
187 | end
188 |
189 | let main _ =
190 | let doc = Dom_html.document in
191 | let parent =
192 | Js.Opt.get (doc##getElementById(Js.string "main"))
193 | (fun () -> assert false)
194 | in
195 | let m = Model.empty in
196 | let rp = React.S.create m in
197 | Dom.appendChild parent (Js_of_ocaml_tyxml.Tyxml_js.To_dom.of_div (View.view rp)) ;
198 | Lwt.return ()
199 |
200 | let _ = Js_of_ocaml_lwt.Lwt_js_events.onload () >>= main
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/textEncoder.ml:
--------------------------------------------------------------------------------
1 | open Js_of_ocaml
2 |
3 | class type t = object
4 | method encode_uint8 : Js.js_string Js.t -> Typed_array.uint8Array Js.t Js.meth
5 | end
6 |
7 | let textEncoder = Js.Unsafe.global##._TextEncoder
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/textEncoder.mli:
--------------------------------------------------------------------------------
1 | open Js_of_ocaml
2 |
3 | class type t = object
4 | method encode_uint8 : Js.js_string Js.t -> Typed_array.uint8Array Js.t Js.meth
5 | end
6 |
7 | val textEncoder : t Js.t Js.constr
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/www/css/style.css:
--------------------------------------------------------------------------------
1 | table {
2 | margin: auto;
3 | color: #333;
4 | font-family: Helvetica, Arial, sans-serif;
5 | border-spacing: 0;
6 | }
7 |
8 | tbody td {
9 | padding: 1em;
10 | border: 1px solid transparent;
11 | transition: all 0.3s;
12 | }
13 |
14 | tbody td {
15 | background: #FAFAFA;
16 | text-align: right;
17 | width: 5.25em;
18 | }
19 |
20 | tbody tr:nth-child(even) td {
21 | background: #F1F1F1;
22 | }
23 |
24 | tbody tr:nth-child(odd) td {
25 | background: #FEFEFE;
26 | }
27 |
28 | tfoot td {
29 | font-weight: bold;
30 | text-align: right;
31 | padding: 1em;
32 | border-top: 1px solid black;
33 | }
34 |
35 | .tiny-csv, .big-csv {
36 | margin-top: 2em;
37 | text-align: center;
38 | }
39 |
40 | .comments {
41 | text-align: center;
42 | margin: 2em;
43 | }
--------------------------------------------------------------------------------
/jsoo/build-local-csv-file/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |