pax_global_header00006660000000000000000000000064152162310020014502gustar00rootroot0000000000000052 comment=8ee8054121de0ad96117ff97e3dac7239a63f582 eliom-12.1.0/000077500000000000000000000000001521623100200126705ustar00rootroot00000000000000eliom-12.1.0/.github/000077500000000000000000000000001521623100200142305ustar00rootroot00000000000000eliom-12.1.0/.github/dependabot.yml000066400000000000000000000001601521623100200170550ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: weekly eliom-12.1.0/.github/workflows/000077500000000000000000000000001521623100200162655ustar00rootroot00000000000000eliom-12.1.0/.github/workflows/doc.yml000066400000000000000000000124731521623100200175640ustar00rootroot00000000000000# Build the Eliom documentation (manual + server/client API) with odoc_driver, # theme it with the Ocsigen chrome (wodoc), and publish it on the project's # gh-pages branch, served at https://ocsigen.org/eliom/. See doc/README.md. # # Two entry points: # - push to master (or "Run workflow" with no version) -> rebuild & deploy the # "dev" docs only. Each run replaces only dev/; the other version directories # already on gh-pages are PRESERVED. # - "Run workflow" with a version -> the `release` job freezes the dev docs now # on gh-pages as // and repoints `latest` at it (refreshing # versions.json). No rebuild: a release is exactly the dev docs at that point. # # PREREQUISITE: wodoc builds the API with `odoc_driver eliom --remap` on the # INSTALLED eliom, and eliom's cross-library links need the odoc_driver `base_args` # fix, which is not yet upstreamed. Until it lands, the "Install dependencies" step # must pin odoc / odoc-driver from the patched source (see below). name: Documentation on: push: branches: [master] workflow_dispatch: inputs: version: description: "Publish a stable version: freeze the dev docs now on gh-pages as // and repoint `latest` (e.g. 12.0). Leave empty to just rebuild dev." required: false default: "" concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build-deploy: # push, or a manual run with no version (just rebuild dev). NOT a release run. if: github.repository == 'ocsigen/eliom' && (github.event_name != 'workflow_dispatch' || github.event.inputs.version == '') runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up OCaml uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: "5.4.0" - name: Set up Binaryen # eliom depends on wasm_of_ocaml-compiler, whose build needs a recent # binaryen (wasm-merge); distro binaryen is too old. Same action as # js_of_ocaml's own CI. uses: Aandreba/setup-binaryen@v1.0.0 with: token: ${{ github.token }} - name: Install dependencies run: | # Install eliom (from the ref being built) so odoc_driver documents # these sources. eliom is installed WITHOUT --with-doc: its server and # client libraries share module names, so building eliom's own @doc # collides ("Multiple rules for .../eliom/Eliom_bus/index.html"). wodoc # builds the docs via `odoc_driver --remap` instead. opam install . --deps-only --with-doc opam install . # wodoc themes the pages. Its opam pin-depends pulls odoc / odoc-driver # from the balat/odoc fork (the cross-library link fix, not yet upstream), # so we do NOT pin odoc ourselves here. opam pin add -n wodoc https://github.com/ocsigen/wodoc.git opam install wodoc - name: Build documentation (dev) # wodoc build runs `odoc_driver eliom --remap` on the installed package # (after preprocessing the installed manual .mld), assembles the themed # site from doc/wodoc, and fetches the shared menu from ocsigen.github.io. run: >- opam exec -- wodoc build --config doc/wodoc --menu https://ocsigen.org/doc/menu.html --out "$PWD/_doc-site/dev" --label dev - name: Deploy to gh-pages (replace only dev/, keep the rest) uses: JamesIves/github-pages-deploy-action@v4 with: branch: gh-pages folder: _doc-site/dev target-folder: dev clean: true release: # Manual run with a version: freeze the dev docs currently on gh-pages as the # stable // and repoint `latest` at it (refreshing versions.json). # No rebuild — the docs of a release are exactly the dev docs at that point. if: github.repository == 'ocsigen/eliom' && github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' runs-on: ubuntu-latest permissions: contents: write steps: - name: Set up OCaml uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: "5.4.0" - name: Install wodoc run: | opam pin add -n wodoc https://github.com/ocsigen/wodoc.git opam install wodoc - name: Checkout gh-pages uses: actions/checkout@v6 with: ref: gh-pages path: site fetch-depth: 0 - name: Freeze dev as the stable version and repoint latest # wodoc release: cp -a site/dev site/, ln -sfn latest, # write the root index.html redirect if missing, refresh versions.json. run: >- opam exec -- wodoc release --site site --from dev --version "${{ github.event.inputs.version }}" - name: Commit and push gh-pages run: | cd site git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add -A git diff --cached --quiet \ || git commit -m "Release doc ${{ github.event.inputs.version }} (freeze dev, repoint latest)" git push eliom-12.1.0/.github/workflows/workflow.yml000066400000000000000000000030551521623100200206650ustar00rootroot00000000000000name: Main workflow on: pull_request: push: branches: - master schedule: # Prime the caches every Monday - cron: 0 1 * * MON concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build: strategy: fail-fast: false matrix: os: - macos-latest - ubuntu-latest ocaml-compiler: - 5 - 4 runs-on: ${{ matrix.os }} steps: - name: Checkout tree uses: actions/checkout@v6 - name: Set-up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-pin: false - name: Set-up Binaryen uses: Aandreba/setup-binaryen@v1.0.0 with: token: ${{ github.token }} - run: opam pin -y git+https://github.com/ocsigen/ocsigenserver#master # Published js_of_ocaml (6.3.2) caps at ocaml < 5.5; pin master for # 5.5 support (also pulls wasm_of_ocaml, hence the binaryen set-up). - run: opam pin -y git+https://github.com/ocsigen/js_of_ocaml#master - run: opam install . --deps-only -y - run: opam exec -- dune build -p eliom lint-fmt: runs-on: ubuntu-latest steps: - name: Checkout tree uses: actions/checkout@v6 - name: Set-up OCaml uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: 4 dune-cache: true - uses: ocaml/setup-ocaml/lint-fmt@v3 eliom-12.1.0/.gitignore000066400000000000000000000001531521623100200146570ustar00rootroot00000000000000*~ #* *# .#* _build eliom.install src/**/*.mllib src/**/*.mldylib src/**/*.odocl ocsigen.org-data _opam eliom-12.1.0/.ocamlformat000066400000000000000000000016341521623100200152010ustar00rootroot00000000000000version=0.28.1 parse-docstrings = false wrap-comments = false break-cases = fit break-collection-expressions = fit-or-vertical break-fun-decl = wrap break-fun-sig = wrap break-infix = wrap break-infix-before-func = false break-sequences = false break-separators = before break-string-literals = never break-struct = force cases-matching-exp-indent = compact doc-comments = after-when-possible dock-collection-brackets = false indicate-multiline-delimiters = no infix-precedence = indent let-and = compact let-binding-spacing = compact module-item-spacing = compact parens-tuple = multi-line-only parens-tuple-patterns = multi-line-only sequence-style = terminator sequence-blank-line = compact single-case = compact type-decl = compact if-then-else = keyword-first field-space = loose space-around-arrays = false space-around-records = false space-around-lists = false space-around-variants = false ocp-indent-compat = true eliom-12.1.0/.ocp-indent000066400000000000000000000000521521623100200147260ustar00rootroot00000000000000normal with=0 syntax=lwt mll max_indent=2 eliom-12.1.0/CHANGES000066400000000000000000001276111521623100200136730ustar00rootroot00000000000000===== 12.1.0 ===== * Compatibility with OCaml 5.5 (Outcometree's Otyp_object replaced open_row with a row field, and out_type gained Otyp_external and Otyp_functor) * Compatibility with js_of_ocaml 6.4 (getElementsByTagName now returns a Dom.collection, and Dom_html gained a `listener` value that shadowed a local binding) ===== 12.0 ===== * Eliom_lib ** Deprecated logging functions were removed (#821) These are: debug_exn, debug, error, error_any, jsdebug * Full WASM support: build system and sending either js or wasm depending on the browser * Use Log instead of Lwt_logs ===== 11.1 ===== * Compatibility with WASM ===== 11.0 ===== * Building Eliom app as executables, with Ocsigen Server as a library, without config file ===== 10.4.1 ===== * Update to OCaml 5.2 (thanks to Kate Deplaix) ===== 10.4.0 (2024-03-22) ===== * Basic client-server distillery template: sqlite is now the default backend * Basic template now has license unlicense * Basic template fixes * Compatibility with Tyxml >= 4.6.0 (by Vincent Laporte) ===== 10.3.1 (2024-02-21) ===== * Fixing Problem with browser navigation https://github.com/ocsigen/eliom/issues/781 ===== 10.3.0 (2024-02-18) ===== * Adding raw events handlers in Eliom_content.Html.F.Raw for server-side only programming ===== 10.2.0 (2023-12-08) ===== * Adding Dune-based basic template client-server.basic ===== 10.1.0 (2023-09-25) ===== * Fix compilation with OCaml 5.1 ===== 10.0 (2022-03-08) ===== * Make Eliom_state.Ext.discard_state more robust ===== 9.0 (2021-10-16) ===== * compatibility with ocsigenserver.5.0.0 (outsourced Ocsipersist) ===== 8.8 (2021-10-05) ===== * rework Eliom_client * more debugging info * restore functions to access uploaded file info ===== 8.6 (2021-07-26) ===== * improvements of history handling and DOM caching ===== 8.5 (2020-10-28) ==== * Compatibility with ocsigenserver 4.0 ===== 6.9 (2019-11-12) ===== * fix scrolling bug: scrolling to top happens before page change * Server functions: log function argument on uncaught exceptions * Compile Eliom libraries with the linkall option ===== 6.8 (2019-09-11) ===== * Use directly Pcre instead of relying on Netstring_pcre * Improved async exception handler * Remove camlp4 syntax * Adapt to OCaml 4.08 * PPX extension: use migrate-parse-tree ===== 6.7 (2018-02-08) ===== * Eliom_client.Page_status: function while_active; ?stop parameter * DOM caching: treat history as linear and keep only reachable pages in cache ===== 6.6 (2018-01-17) ===== * Add missing function Eliom_request_info.get_remote_port * Eliom_client.Page_status.oninactive * Some documentation on DOM caching * Eliom_wrap: fix compilation on 32-bit architectures ===== 6.5 (2018-12-18) ===== * Compatibility with tyxml.4.3.x and js_of_caml.3.3.x ===== 6.4 (2018-12-17) ===== * Compatibility with Lwt 4.x * New configuration option to set html content type * Comet: deactivate when window does not have the focus; deactivate polling when idle * eliom_request_info.in_request_handler * Client-side non-localized parameters * Provide a way to serve global data separately from the initial page ===== 6.3 (2018-02-08) ===== * Compatibility with OCaml 4.06, js_of_ocaml 3.0 and Lwt 3.0 * DOM caching (preview) * Various small fixes and improvements ===== 6.2 (2017-02-07) ===== * Implement Eliom_client.onchangepage * Fix URL-related bugs arising in mobile apps ===== 6.1 (2016-12-20) ===== * Remove redundant dependency on calendar * Permit client routing to "./" * -y parameter for eliom-distillery (do not ask) * Eliom_client: do not execute onload after OCaml services * Permit suffix params in Eliom_service.create_attached_post ===== 6.0 (2016-12-08) ===== * Improve Eliom_service and Eliom_registration APIs using GADTs * Implement client-side services, useful for mobile apps. This includes ** client-side service registration (Eliom_registration) ** client-side service routing (Eliom_client.change_page_uri) * Transition to PPX internally * Compatibility with ** OCaml 4.03.0 and 4.04.0 ** Js_of_ocaml 2.8.2 and newer ** TyXML 4.0 and newer * Various bugfixes and improvements ===== 5.0 (2016-01-28) ===== * Add Eliom_shared and server-side Eliom_content.{Html5,Svg}.R modules * Add PPX syntax extension * Clean-up form APIs, now available under Eliom_content.Html5.{D,F}.Form * Patches to significantly reduce the size of request data * Compatibility with TyXML 3.6, Js_of_ocaml 2.7, and reactiveData 0.2 * Various bugfixes and enhancements ===== 4.2 (2015-07-21) ===== * Add Manip.children, to get the html children of an element. * Simplify Html5 and Svg signatures using tyxml's signature functors. * Various logging improvements, in particular in Eliom_client. * Fix eliomdep's exit code when calling option "-sort". * Fix #168: call_ocaml_service always sends boolean true. * Makes server function return types covariant. * Restore compatibility with ocsigenserver 2.6 and lwt 2.4.7. * Various bugfixes and wiki updates. ===== 4.1 (2014-10-01) ===== * Compatibility with js_of_ocaml-2.5 and tyxml-3.3.0 * Improvements: ** Simplify typing of services ** Use GADT to encode service parameters (Eliom_parameter) Eliom need OCaml 4.00 at least. ** bugfixes for service parameters ** Various bugfixes and enhancement ===== 4.0 (2014-05) ===== * Main new features: ** Reactive DOM elements (thanks to a contribution by besport.com) ** PUT and DELETE services for RESTful Eliom Applications (Contribution by Domoco) ** EXPERIMENTAL: Mobile apps with Eliom. By making it possible to start the application from client-side. ** Documentation improvements * Improvements: ** Improvements in typing of services (Contribution by Jacques-Pascal Deplaix) ** Filtering data from Eliom buses ** "caml" in value or type names now replaced by "OCaml" everywhere ** New module {{{Eliom_registration.File_ct}}} to make easier to send files with their content type ** Now possible to define services in shared sections ** Adding function {{{Eliom_client.change_url}}} to change the URL without doing a request ** Adding function {{{Eliom_service.attach_coservice'}}} to attach a non-attached coservice to an URL ** Improving comet configurations: now possible to stay idle instead of being completely inactive ** Now using defaultprotocol option (from ocsigenserver) to compute URLs ** The default expiration date for cookies on client side is now 10 years ** Now possible to send files in {{{Eliom_client.call_ocaml_service}}} ** Various additions and bugfixes in {{{Manip}}}, {{{Eliom_request_info}}}, {{{Eliom_content.html5}}} (events) (contributions by besport.com) ** eliom-destillery renamed into eliom-distillery ** Templates for eliom-distillery ** New programs: eliomdoc and eliompp ** Ocamlbuild package ** Various bugfixes And many other things (see git commits) ===== 3.0.0 (2012-12-10) ===== * Language ** Generalized client values in server code ** Injections into client sections * Tools ** Added eliom-destillery for generating project scaffolds ** Support Eliom interface files (.eliomi) in eliomc, eliomdep ** eliomdep: Generate dependencies between eliom-modules ** eliomc: infer only with option -infer, drop option -noinfer ** eliomc: Basic support for -i on eliom-files ** eliom{c,dep,opt},js_of_eliom: -dump to output the intermediate code ** eliomc,js_of_eliom: always open Eliom_pervasives in eliom files * API ** Eliom_pervasives.server_function to easily access the from the client ** Get current state of a scope ** Module to access values of Eliom references in external states (Eliom_state.Ext) ** Scope names are now named scope hierarchies ** Iterate the scope hierarchy (group > session > client > request) ** Adding Eliom_parameter.(type_checker, neopt) ** Add functions to insert html5 in atom feeds ** Eliom_tools.{F,D}.html to ease creation of head-tag ** Eliom_tools.wrap_handler as an easy alernative to Eliom_registration.Customize ** Test for initial request of a client on the server * Changed server behaviour ** Eliom_state.discard_everything now also discards request state ** Don't send nodes as data when they are part of the document * Changed client behaviour ** Show progress cursor during navigation with change_page ** Improved error messages ** Fail on the client when a [server_function] or call_caml_service fails on the server * Bugfixes ** Allow % for injections directly after parentheses * Support dropped for ** Xhtml ** OCamlDuce ** Eliom_compatibility_2_1 * A myriade of bugfixes ===== 2.2.2 (2012-06-11) ===== * Fix (re-disable) generation of API doc for ocamlduce modules ===== 2.2.1 (2012-06-07) ===== Addendum to the great renaming * Eliom_cookies -> Eliom_cookie * Eliom_comet.Channels -> Eliom_comet.Channel ===== 2.2 (2012-06-02) ===== The great renaming: Make Eliom's API more coherent * Dropped Eliom_pervasives, split into ** Eliom_lib (extension of stdlib) ** Eliom_content (content creation) * Moved all Html5,Xhtml content function to Eliom_content.{Html5,Xhtml} ** {HTML5,SVG,XHTML}.M -> Eliom_content.{Html5,Svg,Xhtml}.F ** {HTML5,SVG,XHTML}.DOM -> Eliom_content.{Html5,Svg,Xhtml}.D ** Eliom_output.{Html5,Xhtml}: form functions to Eliom_content.{Html5,Xhtml} ** Eliom_client.Html5 -> Eliom_content.Html5.To_dom ** Eliom_dom -> Eliom_content.Html5.Manip ** HTML5.of_element -> Eliom_content.Html5.Of_dom.of_element * Removed default implementations in Eliom_content.{Html5,Svg,Xhtml} (was inclusion of DOM) * Rename Eliom_output to Eliom_registration * Rename Eliom_registration.Eliom_appl to Eliom_registration.App * Conform naming conventions for modules (singular, lower-case) ** XML -> Xml ** SVG -> Svg ** XML_types -> Xml_types ** XHTML_types -> Xhtml_types ** HTML5_types -> Html5_types ** Eliom_services -> Eliom_service ** Eliom_parameters -> Eliom_parameter ** Eliom_references -> Eliom_reference ** Eliom_extensions -> Eliom_extension * Approximating compatibility module for Eliom 2.1: Eliom_compatibility_2_1 ===== 2.1.1 (2012-03-20) ===== * Server: Add Eliom references of scope site * Server: Add an non-Lwt interface to volatile Eliom references * Client: Add an option to restrict usage of XHR. * Bugfixes: ** Client: More robust and portable history handling ** Client: More robust CSS preloading (change_page) ** Client: Better compatibility with Opera ===== 2.1.0 (2012-02-24) ===== * Page loading rewritten. ** We do not traverse the HTML tree anymore for retrieving links and putting events handlers, it use querySelectorAll instead. (with a fallback for older browsers) *** We do not send a sparse tree along the page anymore. *** It is faster and more resilient to ugly browser plugins ** Add css loading optimisations: It fetch the css included using @include with XHR before changing page and construct only one concatenated css. It avoids page blinking when the css change and is faster (browsers tend to render the page once per css). ** Unwrapping hand rewritten in js. ** And a lot of other optimisations. * Rework of unique nodes: ** Introduce new lightweight 'unique' node of scope request, a.k.a. DOM nodes: use it through HTML5.DOM.{div,...} or its alias HTML5.{div,...}. ** To create unique node of scope application, the function 'HTML5.M.unique ?copy' has been renamed 'HTML5.create_global_elt ?id' ** Abstract id for application node are created with "HTML.new_global_elt_id" ** Node representation now differs between the client and the server. They are unwrapped on the client to simplify the XML representation: "unique" node that have already a "Javascript DOM representation" only contains a reference to the DOM representation. * Server API Changes: ** Add Eliom_state.get_*_session_group_size to know the number of session in a session group ** Parameters of type any can't be suffixes (semantic problem) ** Add Eliom_comet.wait_timeout to notify when a client isn't active ** Allow services to setup "Cache-policy: max-age" with Text, CssText and Files modules. ** Eliom_state.discard and discard_data now accept scope request. * Client API Changes: ** add an Eliom_dom module provinding imperative function on unique HTML5.elt. (for instance {{{appendChild: ?before:'a elt -> 'b elt -> 'c elt -> unit}}}). ** HTML5.M.a_on{click,...} functions directly expect a function as parameter (XML.event_handler_of_function isn't required anymore). ** Eliom_comet: configuration option for time until timeout after the focus is lost ** Handling wrapping of int32 type. ** Onload event handlers on HTML elements are executed when they are converted to DOM (using Eliom_client.of_ functions). This allows them to be added to nodes not sent in the page. ** Calls to {{{Eliom_services.onload}}} are taken into account for services sending caml values. The handlers are executed when the call to {{{Eliom_client.caml_call_service}}} terminates. * Eliom syntax: ** Event handlers can now access the current event using the _ev variable inside {{ ... }} typed according to the attribute. ** Allow modules to be used inside client expr {{ ... }} and client_expr {{ ... }} to be used inside modules ** Add -ppopt option to eliomc * And a lot of bugfixes. ===== 2.0.2 (2001-11-30) ===== * Compatibility with js_of_ocaml-1.0.8 and tyxml-2.0.1 * API Changes: ** Eliom_output.Redirection: use 302 as default and introduce abstract nome for all the 30x HTTP code ** Eliom_output.Customize: remove unrequired Functor parameters. * Delayed URl computation: this allows relative links to be created outside of service handler. * Client: do not ignore "onload" events attached on Dom elements other than window. * Bugfixes: ** Client: do not execute change_page_uri when the middle button or any modifier key is pressed. ** Client: update correctly the location when changing page with forms. ** Server: displaying better dynlink error messages. ** Syntax extension: avoid infinite loop while parsing first-order module expression ** Eliom_parameters: fixing wrong parameter names for list of list ** Eliom_parameters: fixing bug causing stack overflow when lists parameter names were wrong ** Fixing bug with non-localized parameters and service not keeping non-attached params ** Eliom_comet: stateless channels can be registered multiple times (close #220) ===== 2.0.1 (2011-09-28) ===== * Bugfixes in eliom appl.: ** Preload css when changing page to avoid a disgraceful "flash" effect ** Use to preserve links in unique node when changing page ** Fragment in URL aren't ignored anymore ** Proper exception handling in eliomc, js_of_eliom, ... ** Display a correct URL after submitting a
===== 2.0 (2011-09-20) ===== * Bugfixes: ** Correct handling of HTTP redirections hidden by XHR ** Fix links from http to https on client application ===== 2.0-rc2 (2011-09-06) ===== * Feature: ** api history: when the api history is available, application URLs are more readable (no more {{{#!}}}) * Bugfixes: ** fix browser compatibility: it should work with Firefox (>= 3.0), Chrome (all recent version we tested), Safari 5, Opera (tested 11.51), Internet explorer (9, ie 8 almost work) ** in forms declared outside of the sp's scope, hrefs were incorrect: url calculation is now done for each request ** the function {{{Eliom_client.caml_call_service}}} was broken with some kind of services ** application cookies are now always sent ** fix incorrect sharing of type variables in the syntax extension (thanks to William Le Ferrand) ** 404 and 403 errors are not dropped by applications anymore (This fix is partial: when the page content is application content, this still does not work when an exception handler returns something else) ** Fix problems with url generation for non-attached coservices inside an application ** tail recursive marshalling and demarshalling: no more limit on the depth of data sent along the page ** Fix problems with redirection and action with `NoReload exiting the application ** Handle correctly 204 code in application (do nothing with change page, fail with caml_call_service) ** Fix escape character '>': problem with strings containing "]]>" ===== 2.0-rc1 (2011-07-15) ===== * Eliom_client.change_page type now correctly forbid to call it on non-eliom application services * Stateless implementation of comet channels * Scope and state names parameters of functions are now fused in one parameter (called scope) * Change the type of 'send' functions: forbid use of incompatible output with Eliom_output.Any together. * Change the format of page loaded by an application: all pages are sent in HTML5 format. (first answers were in HTML5 subsequent were marshalled caml tree) * XML representation is now shared by client and server: conversion to Dom nodes is made by Eliom_client.Html5.of_ functions * Remove the need for an application container: application pages are now classical HTML5 pages. * Add Eliom_pervasives.{HTML5/XML/SVG}.unique function to declare XML node handled "by reference" * Fix ocamldoc generation with Eliom_output * Eliom_appl are now stateless if no data is stored in session * Allow dynamic wrapping with values to be sent to client with caml services and comet channel * Add Eliom_react.S: server to client React signals * Add lazy site values: lazy evaluated one time per site * Option to compile without preemptive threads * And lots of simplifications and bug fixes ===== 1.91 (2011-04-08) ===== * Split the ocsigen package in three : tyxml, ocsigenserver and eliom * Rename into eliom * It's now possible to set a //priority// on services registered on the same path, to choose in which order Eliom will try them. * New implementation of internal application forms with formData when available * New build system for eliom application * No type restriction in value sent from the server to the client ===== 1.90 ===== * New module {{{Eliom_client}}} for client/server Eliom programs using js_of_ocaml. * Eliom client: calling a service from client side code. * Eliom: Services taking Caml values as parameters. * Eliom: services sending Caml values. * Eliom: new implementation of forms and links to be compatible with client side programs. * Eliom: sp parameter has been removed (now using Lwt's thread storage) * Eliom: {{{Eliom_predefmod}}} renamed {{{Eliom_output}}} * Eliom: New module {{{Eliom_output.Customize}}} to create your own register functions from another registration module * Eliom: Module {{{Eliom_sessions}}} replaced by {{{Eliom_state}}}, {{{Eliom_request_info}}} and {{{Eliom_config}}}. * Eliom: new implementation of user cookies. * Eliom: Client process cookies. Like usual browser cookies but for one client side process. * Eliom: Client process server side state data. Like session data but for one client side process. * Eliom: Client process session services. * Eliom: Session group data. Like session data but for all sessions in a session group. * Eliom: Session group services. * Eliom: session replaced by a more general concept of "server side state". States have a scope: client process, session or group of sessions. * Eliom: session tables and request cache now deprecated, replaced by //Eliom references// * Eliom client: Possible to call another service without stopping the client side program, with bookmark support and back button support. * New extension Comet to allow server -> client communication. * Eliom: client/server communication channels. * Eliom: client/server reactive programming using React. * Eliom client: syntax extension for separating client and server code. * Eliom: New module Eliom_output.Eliom_appl for registering pages that belong to the same Eliom application. * Eliom client: syntax extension and wrapping/unwrapping mechanism to access server side values in client side code. * Eliom client: Relinking the DOM on client side after loading a (portion of) page. This allows nodes created on server side to be directly used in client side code. * Eliom client: XHR redirections for Eliom applications. * Eliom: safe unmarshaling of caml values sent by client side programs * Xhtml: Xhtml5 support * Atom module and Pubsubhubbub * OpenID support * SVG module * Documentation: New tutorial * Documentation: New Eliom manual * //and many other things ...// ===== 1.3.4 ===== * Eliom: Now supporting list of lists in service parameters ===== 1.3.3 (2010-06-13) ===== * Eliom: Fix some typos in Eliom's tutorial stylesheet * Server: Fix usage of {{{accept_n}}} to avoid file descriptor leakage * XHTML: Adding missing elements and attributes in XHTML.M * Cleaning Ocsigen_cache * Eliom: Fixing actions with uploads ===== 1.3.2 (2010-04-30) ===== * Add dummy findlib packages ocsigen.xhtml*, that depend on ocsigen_xhtml*, for compatibility with previous versions. These packages might be removed in a future (major) release. * Port to Lwt 2.1.0 ===== 1.3.1 (2010-04-23) ===== * Split out ocsigen_xhtml findlib package * Configuration file: when no protocol is specified in {{{}}}, listen on IPv6 (if possible) and IPv4 (always) ===== 1.3.0 (2010-01-22) ===== * Server: Each request now has a polymorphic data table (called //request cache//), where you can store the data you want to keep during the whole page generation. * Eliom: actions now return {{{()}}}. Use the request cache to send information to fallbacks. * Eliom: CSRF-safe coservices * Eliom: the number of sessions without group by IP address is now limited * Eliom: efficient implementation of limitation of sessions by groups (or now IP) for large limits * Eliom: the number of anonymous coservices by session is now limited * Eliom: the number of anonymous coservices without session by IP address is now limited * Eliom: now possible to unregister services * Eliom: New (very) experimental module {{{Eliom_obrowser}}} to use Eliom with Obrowser * Eliom: Now possible to have constant parts in suffixes to allow URLS like {{{/param1/something/param2}}} * Eliom: services with optional suffixes * Eliom: form towards a service with suffix: it is now possible to choose whether you want the redirection towards the suffix form or not * Eliom: lists and sets in suffixes * Eliom: Now possible to create services sending custom HTTP header values or customizing the content-type * Eliom: New notion: "Non localized parameters". Can be sent to any service. * Eliom: changing the order of parameters for user type form widgets * Eliom: changing the order of parameters for user type form widgets * Eliom: Eliom_tools.menu and hierarchical_menu now compare the URL strings (was: compare the service) * Eliom: textarea now take a string (was pcdata) * Eliom: The type of the iterator for lists in parameters has changed * Eliom: New options in configuration file to set session timeouts * Server: now possible to give the config file name to reload server command * Server: now possible to do a "graceful shutdown" of the server using the "shutdown" server command * Server: now possible to add custom commands for the command pipe * Server: EXPERIMENTAL now possible to observe HTTP headers before sending the result * Xhtmlpp: the parsing now fails if a quotation for an Xhtml element contains superfluous elements. (This can cause the parsing of previously incorrect code to fail) * Staticmod/Eliom: attempting to access a file whose name contains a NULL character will result in a 403. * Server: HTTP headers containing newlines are now properly escaped. * Server: correct missing xmlns in Xhtml DTD * Server: now send last-modified and etag headers when returning a 403 * Server: Now accepting several requests at a time (as explained in "Accept()able strategies" by Tim Brecht & all) * Rewritemod: more rewriting possibilities (still very basic) * Eliom menus are now more robust when finding which item is active * Fixed handling of incorrectly-sized multipart requests. Thanks to Mauricio Fernandez for noticing the bug * Upload directory and maximum file size can now be configured on a per-site basis * Renamed the field of Ocsigen_http_frame.t * Javascript events support in Xhtml.M ; Thanks to john @ 5070.info for the patch ===== 1.2.2 (2009-10-17) ===== * Add react and lwt.unix to the list of built-in findlib packages ===== 1.2.1 (2009-09-26) ===== * Lwt 2.0 compatibility: ** Adapt to Lwt.t/Lwt.u splitting ** fix Makefile to deal with lwt.unix findlib package * Fix ocsipersist-dbm Makefile * Fix for pcre-ocaml 6.0.0 * Fix typo regarding --stubdir in configure script ===== 1.2.0 (2009-03-25) ===== * Native code version now compiled by default * Now possible to link extensions and Eliom modules statically, for example to use a native code server on platforms where native dynlink is not supported * Server: Partial requests implemented (Range HTTP header) * Build C stubs into a shared library so that bytecode executables may be not linked in custom mode; new {{{--stubdir}}} option in {{{configure}}} script * Eliom: non-attached services now called "named non-attached coservices" and created using {{{Eliom_services.new_coservice'}}} with the optional {{{name}}} parameter * Eliom: now possible to create named attached coservices using the optional {{{name}}} parameter * Eliom: now possible to write libraries for Eliom sites, loaded inside {{{}}}, but not generating any page * Eliom and server: EXPERIMENTAL now possible to make extensions that can use Eliom's data * XHTML.M's pretty printer: now possible to set the DOCTYPE manually * Eliom: now possible to set manually the DOCTYPE when registering an XHTML.M service * Redirectmod and Revproxy: now possible to do more complex rewriting * Accesscontrol: add support for {{{}}} and {{{}}} conditions * Config file: {{{aliases}}} attribute now called {{{hostfilter}}} * Revproxy and Redirectmod: now possible to filter on server, port and protocol * New extension extendconfiguration to allow dynamic changes in the configuration (mimetypes, charsets, followsymnlink, directory listing, ...) * Server: new module {{{LocalFiles}}} factoring the common parts for sending static files (with Eliom and staticmod for example), while checking that the files can safely be sent. * Now possible to use XHTML pretty printers without Ocsigen, using the {{{xhtmlpretty.cma}}} library * Add {{{Ocsigen_lib.register_exn_printer}}}, better error messages * Now possible to use the same configuration file in native code and in bytecode (.cmo/.cma filenames are internally translated to .cmxs) * Signature of Ocsigen_extensions.register_extension is now more complete and more lightweight * Userconf: the options set in the local .userconf file are kept in the enclosing {{{}}} tag * Server: possibility to ignore or to supply an alternative command-line * Ocsigen_http_client: timeout when the distant server does not exists * OCaml versions < 3.10 are not supported anymore * Extensions are now much more strict w.r.t. the syntax of configuration files * Staticmod: accessing a directory for which indexing is disallowed returns an error 404 (instead of a 403 previously) ===== 1.1.0 (2008-07-15) ===== * Lwt removed (now distributed separately) * {{{XHTML.M}}} pretty printer: fixing pretty printing of empty tags (for browser compatibility) * Eliom_duce: New pretty printer for XHTML fixing pretty printing of empty tags * Eliom: secure sessions, secure services, (absolute) https links/forms, and using secure cookies * Eliom: Adding special "void action", without any parameters * Eliom: {{{Eliom_predefmod.Redirections}}} now called {{{Eliom_predefmod.String_redirection}}}, and adding new module {{{Eliom_predefmod.Redirection}}} that use GET services without parameters as data type. * Eliom and XHTML.M: Small changes of types in interfaces * Eliom: New session ID generator * Eliom: Adding types {{{int32}}} and {{{int64}}} for parameters and forms * Eliom: Adding functions {{{lwt_get_form}}} and {{{lwt_post_form}}} for creating forms using cooperative functions * Eliom and Staticmod: now possible to give GET parameters to static pages * Eliom: Bugfix in Makefiles for native code version * Eliom forms: Adding missing types in interfaces * Eliom_tools: current page is now optional in menus * Userconf and Eliom: there was a bug when loading both Eliom and Userconf together * Reverse Proxy: Now sending content length when available * Web server: The default content-type is now {{{application/octet-stream}}} * Creating and installing a cma file for all Ocsigen libraries not installed elsewhere * Ocsipersist-dbm: fixing bug when removing data * Deflatemod: fixing memory leak * And small bugfixes in XHTML.M, Eliom, ... ===== 1.0.0 (2008-04-01) ===== * Config file: findlib integration * Eliom and Ocsigen: changing namespace convention for modules * Access control: simplification of config file syntax * Eliom: Allowing (module dependent) parameters for registration functions * New xhtml output without pretty printing * Web server: Bugfix in HTTP/1.0 with keep-alive * Reverse proxy: Bugfix GET parameters were wrong * Reverse proxy: Bugfix memory consumption when the connection was closed by client ===== 0.99.5 (2008-01-11) ===== * Revproxy: pipelining of requests * Access control: simplification, generalization of filters and syntax changes in config file * Eliom: EXPERIMENTAL session groups * Eliom: non-attached services * Eliomduce: new functor {{{SubXhtml}}} for creating registration modules * Eliomduce: new module Eliomducetools with same features as Eliomtools, but for Eliomduce * Web server: now possible to split the configuration file into several files using the {{{}}} option. * Web server: now possible to have {{{}}} option inside another {{{}}} in configuration files, and the the first one is optional * Web server: EXPERIMENTAL user configuration files, with restricted possibilities (for security reasons) * Web server: IPv6 support * Deflatemod: now possible to filter on file extensions * Eliom: new option to keep GET non-attached parameters or not when doing a POST form towards a non-attached coservice. * Eliom: bugfix path of session cookies * Eliom: bugfix POST non-attached coservices called from a page with non-attached GET parameters were not found. * Lwt: now catching exceptions raised after timeouts * Cgimod: bufixes in path handling * Web server: bugfix - some files were not closed ===== 0.99.4 (2007-11-21) ===== * Ocsigen: Changes in the extension mechanism. The extensions are not tried in the order in which they are loaded any more, but in the order in which the options are specified for each site in the configuration file. * New experimental extension: access control * A few small enhancements ** Eliom: internal cookie management rewritten (+ bugfix) ** Eliom: Small changes in function names ** Eliom: now retry all extensions after actions (not only Eliom) ** Eliom: cleaning {{{Eliommod}}} interface ** Ocsigen server: Internal changes in server (removing "send" functions, debug messages lazier) ** Lwt: Adding a few functions in {{{Lwt_chan}}} interface ** Staticmod: Allowing default error pages for HTTP errors to be customized ** Ocsipersist (dbm and sqlite): better handling of database errors ** XHTML: New pretty printer for xhtml using streams (up to 15% speedup on requests) ** XHTML: Allowing any value for {{{}}} rel attribute (for example {{{shortcut icon}}}). ===== 0.99.3 (2007-11-07) ===== * Ocsigen: New module Deflatemod to compress data before sending to the browser. * Ocsigen: EXPERIMENTAL - New module Revproxy (reverse proxy). * Eliom: New session mechanism, with the ability to name the sessions and thus have several sessions for the same site. * Eliom: Now possible to have one site with session inside a subdirectory of another one. * Lwt: New module {{{Lwt_timeout}}} to implement timeouts, new module {{{Lwt_chan}}}, new module {{{Lwt_mutex}}}, new function {{{Lwt_unix.abort}}} to make all threads waiting on a file descriptor abort with an exception. * Ocsigen: New implementation of lots of Web server internals. Better management of Ocsigen's streams, file descriptors, exceptions, timeouts ... * A lot of enhancements and bug fixes: ** Eliom: Single {{{}}} in forms, by Stéphane Dalmas * EXPERIMENTAL: The Web server is now extensible. It means that you can add modules (like Apache modules) for generating pages, filters of requests, extensions of config files. For now there are two modules, one for static pages, and one for dynamic pages. The only changes for users is that they need to dynlink staticmod.cmo and ocsigenmod.cma from the configuration file. The syntax of config file for modules and staticdir also changed. * It is now possible to specify the encoding of characters for each sub-site. * Now usable with Ocamlnet 2.2 or 1.1.2. * EXPERIMENTAL: If OCamlDuce is installed on your system, you can now use it to do the type-checking of your pages (see the documentation). Warning: This is broken with OCamlDuce 3.09.2 patch level 2. You need at least OCamlDuce 3.09.3 patch level 1. * Removing Ocsimore from the default distribution. That version of Ocsimore is not supported anymore. Ocsimore has been rewritten from scratch by Piero Furiesi. ===== 0.5.1 (2006-12-14) ===== * Bugfix Konqueror: Multipart forms with now work correctly with Konqueror * Bugfix Internet Explorer: getting around a bug of Internet Explorer while displaying page * Bugfix NetBSD: Makefile * Bugfix Debian for HPPA, Mips, Alpha: Makefile * Bugfix: preemptive.cmi was missing in the installation directory * Adding manpage (S. Mimram) * Adding logrotate configuration * Daemon mode: adding setsid and redirect stdout/stderr to /dev/null. Closing stdin. ===== 0.5.0 (2006-11-23) ===== * HTTP 1.1 improved (HEAD, ETag, keep-alive implemented, If-Modified-Since, ...) * HTTPS support * Pipelining of requests * Server can listen on several ports * Multiple servers: you can now define several servers in the config file. * Virtual hosts: filtering on hostnames/ports (with wildcards) * Asynchronous file upload with multipart support * Large file transfer improved. * MIME types are now parsed from a file * Changes in the syntax of config file * Accessors for server parameters. Use ({{{get_user_agent sp}}}) instead of {{{sp.user_agent}}}. * Page generation is now using {{{Lwt}}} cooperative threads, to make it possible the generation of several pages at the same time. Practically, add {{{Lwt.return}}} before the page you want to send, and use cooperative input/output functions. * Typing errors of parameters are now catchable. * {{{static_dir}}} is now a function * Most of global references have been removed. You now need to give sp as parameter to {{{register_for_session}}}, {{{static_dir}}}, {{{close_session}}}, etc. * Links and forms now take {{{server_params}}} instead of {{{current_url}}} ({{{sp}}} is shorter than {{{sp.current_url}}}) * EXPERIMENTAL: Use of preemptive threads for non cooperative libraries ({{{detach}}} function). * EXPERIMENTAL: The {{{Ocsigen}}} module now contains a functor {{{Make}}} to allows the use of several ways of generating XHTML. The default way (using {{{XHTML.M}}} or the syntax extension) can be used by doing {{{open Ocsigen.Xhtml}}}. There is also an untyped xhtml generation module called {{{Ocsigen.Text}}}. * EXPERIMENTAL: extension of forms. * Reorganisation of the code * Bugfixes in makefiles * Bugfix: escaping of strings in xhtml with syntax extension (thanks to David Mentre) ===== 0.4.0 (2006-06-06) ===== * Full reimplementation of the core using Generalized Algebraic Data Types, * {{{_int}}}, {{{_string}}}, etc. are now called {{{int}}}, {{{string}}}, etc. * The type {{{_http_params}}} is now called {{{server_params}}}, * Services functions now all take 3 parameters, one for server parameters, one for GET parameters, and one for POST parameters. Note that {{{**}}} is used to create **pairs** and not tuples. * The {{{a}}} and {{{post_form}}} functions now take a fix number of parameters, corresponding to GET parameters. * //URLs// are now called //services//, * //state URLs// are now called //auxiliary services//, * {{{register_post_url}}} does not exist anymore. use {{{register_service}}} instead (idem for all other {{{register_post_*}}} functions). * Changes for prefix URLs * Small changes for actions * EXPERIMENTAL: sums, bool and list types for services and forms * small bugfixes ===== 0.3.27 (2006-04-27) ===== * Change the way to get server parameters ===== 0.3.26 ===== * Load unsafe modules * Other small changes ===== 0.3.25-2 (2006-02-24) ===== * Small bugfix for 64 bits processors * bugfix for static files * {{{action_link}}} is now called {{{action_a}}} ===== 0.3.24 (2006-02-07) ===== * More documentation * Change types {{{internal_url}}} and {{{external_service}}} to use polymorphic variants ===== 0.3.23 (2006-02-07) ===== * Better handling of static files and "403 Forbidden" message eliom-12.1.0/CODE_OF_CONDUCT.md000066400000000000000000000007741521623100200154770ustar00rootroot00000000000000# Code of Conduct This project has adopted the [OCaml Code of Conduct](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md). # Enforcement This project follows the OCaml Code of Conduct [enforcement policy](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md#enforcement). To report any violations, please contact Jérôme Vouillon, Raphael Proust, Vincent Balat, Hugo Heuzard and Gabriel Radanne at (or some of them individually). eliom-12.1.0/COPYING000066400000000000000000000654371521623100200137420ustar00rootroot00000000000000This program is released under the LGPL version 2.1 (see the text below) with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. As a special exception to the GNU Library General Public License, you may also link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Library General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Library General Public License. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! eliom-12.1.0/LICENSE000066400000000000000000000004131521623100200136730ustar00rootroot00000000000000The Ocsigen application core, and other portions of the official Ocsigen distribution not explicitly licensed otherwise, are licensed under the GNU LESSER GENERAL PUBLIC LICENSE with openssl linking exception -- see the 'COPYING' file in this directory for details. eliom-12.1.0/Makefile000066400000000000000000000033511521623100200143320ustar00rootroot00000000000000### Building BEST=$(if $(shell command -v ocamlopt 2> /dev/null),native,byte) BUILDER=_build/build/build.$(BEST) .PHONY: all all: dune build ### Doc .PHONY: doc wikidoc doc man alldoc DOCS_DIR=src/lib/client src/lib/server src/ocamlbuild src/ppx DOCS_HTML=$(addsuffix /api.docdir/index.html,$(DOCS_DIR)) DOCS_WIKI=$(addsuffix /api.wikidocdir/index.wiki,$(DOCS_DIR)) DOCS_MAN= src/lib/client/api.mandocdir/man.3oc \ src/lib/server/api.mandocdir/man.3os \ src/ocamlbuild/api.mandocdir/man.3o \ src/ppx/api.mandocdir/man.3o API_DIR=_build/doc/dev/api doc: $(BUILDER) $(BUILDER) $(DOCS_HTML) wikidoc: $(BUILDER) $(BUILDER) $(DOCS_WIKI) man: $(BUILDER) $(BUILDER) $(DOCS_MAN) alldoc: man wikidoc doc doccp: alldoc mkdir -p $(API_DIR)/server $(API_DIR)/client $(API_DIR)/ocamlbuild $(API_DIR)/ppx cp -Rf _build/src/lib/server/api.wikidocdir/*.wiki $(API_DIR)/server/ cp -Rf _build/src/lib/client/api.wikidocdir/*.wiki $(API_DIR)/client/ cp -Rf _build/src/ocamlbuild/api.wikidocdir/*.wiki $(API_DIR)/ocamlbuild/ cp -Rf _build/src/ppx/api.wikidocdir/*.wiki $(API_DIR)/ppx/ cp -Rf doc/index.wiki $(API_DIR)/ $(BUILDER): $(wildcard build/*.ml) ocamlbuild -no-plugin -I src/ocamlbuild -no-links -use-ocamlfind build/build.$(BEST) 1> /dev/null ocaml pkg/build.ml manpage=false native=true native-dynlink=true builder: $(BUILDER) ### Cleaning ### .PHONY: clean distclean clean: dune clean distclean: clean -find ./ -name "*\#*" | xargs rm -f ### Installation #### .PHONY: install uninstall reinstall install uninstall: eliom.install ifneq ($(PREFIX),) opam-installer --$@ --prefix $(PREFIX) eliom.install else @echo you must provide a prefix with : make PREFIX=myprefix $@ endif reinstall: ${MAKE} uninstall ${MAKE} install eliom-12.1.0/README.build000066400000000000000000000015011521623100200146430ustar00rootroot00000000000000== internal description of compilation/installation steps 1 - build custom ocamlbuild (build/build.native) this custom ocamlbuld uses src/ocamlbuild/ocamlbuild_eliom.ml to avoid duplication 2 - call `ocaml pkg/build.ml` (topkg) that will call the custom builder (from 1) to build request targets 3 - eliom.install is generated and can be used by opam-installer == How to update the build system - add new files => update pkg/filelist.ml client only files can be : src/lib/client/*.ml, src/lib/*.client.ml server only files can be : src/lib/server/*.ml, src/lib/*.server.ml common files can be : src/common/*.ml, src/*.eliom (with shared_section) - add packages requirement => update pkg/META (if needed) and src/_tags "package(new_package)" - change version update VERSION and pkg/META eliom-12.1.0/README.doc000066400000000000000000000004351521623100200143160ustar00rootroot00000000000000To update the API documentation: make doccp git checkout wikidoc rm -rf doc/dev/api cp -r _build/doc/dev/api doc/dev/api git add doc/dev/api git commit To update manual: Edit files in doc/dev/manual To create a new version: cp -a doc/dev doc/ Then commit and push on Github eliom-12.1.0/README.md000066400000000000000000000071411521623100200141520ustar00rootroot00000000000000# Eliom - the full-stack OCaml Web and mobile framework Eliom is a framework for building client/server Web and mobile applications in OCaml. It can be used both as a traditional server-side Web framework or to implement complex client-server applications. Eliom transforms OCaml into a multi-tier language, making it possible to implement both the server and client parts of a Web and mobile app as a single program. This simplifies a lot the communication between server and client. Applications can run on any Web browser or mobile device (iOS, Android), saving from the need to develop one version for each platform. Eliom has support for reactive pages (generated on server or client), advanced session mechanism, server to client communication, continuation based Web programming, etc. Eliom is part of the [Ocsigen project][ocsigen]. ## Installation Instructions ``` opam install eliom ``` ## Getting started Defining a service on path `/foo`, taking any GET parameters: ```ocaml let myservice = Eliom_service.create ~path:(Eliom_service.Path ["foo"]) ~meth:(Eliom_service.Get (Eliom_parameter.any)) () let () = Eliom_registration.Html.register ~service:myservice (fun get_params () -> Lwt.return Eliom_content.Html.F.(html (head (title (txt ""))) (body [h1 [txt "Hello"]]))) ``` Inserting a link towards that service, with parameters: ```ocaml Eliom_content.Html.D.a ~service:myservice [txt "Home"] [("param1", "v1"); ("param2", "v2")] ``` Event handlers are written in OCaml: ```ocaml div ~a:[a_onclick [%client (fun ev -> ... )]] [ ... ] ``` The client-side and server sides are written as a single program: ```ocaml let%server a = ... (* code for the server part of the application *) let%client b = ... (* code for the client part of the application *) let%shared c = ... (* code that will be included in both parts *) ``` Using a server-side value in client-side code: ```ocaml let%server a = ... let%client f () = print_endline ~%a ; (* print in browser console *) ... ``` Calling a server function from the client program: ```ocaml let%rpc f (x : int) : string Lwt.t = ... (* server-side code *) let%client () = let%lwt r = f 4 in ... ``` Saving session data on the server using Eliom references: ```ocaml let%server r = Eliom_reference.eref ~scope:Eliom_common.default_session_scope 0 let%server f () = let%lwt v = Eliom_reference.get r in Eliom_reference.set r (v + 1); ... ``` Where scope can be: * `Eliom_common.default_session_scope` (different value for each browser), * `Eliom_common.default_process_scope` (different value for each tab), * `Eliom_common.default_group_scope` (different value for each user), * `Eliom_common.site_scope` (value for the whole site), * `Eliom_common.global_scope` (global value for the whole server). Eliom references are persistant if you add optional parameter `~persistent` to function `Eliom_reference.eref`. ## Learning Eliom More documentation [here](https://ocsigen.org/tuto/latest/manual/basics). Write your first Web and mobile application with Eliom using [Ocsigen Start](https://ocsigen.org/ocsigen-start) ## Authors * Vincent Balat * Jérôme Vouillon * Grégoire Henry * Pierre Chambart * Benedikt Becker * Vasilis Papavasileiou * Boris Yakobowski * Hugo Heuzard * Raphaël Proust * Jan Rochel * Idir Lankri * Stéphane Glondu * Gabriel Radanne * Gabriel Kerneis * Denis Berthod * Jaap Boender * Simon Castellan * Mauricio Fernandez * Archibald Pontier * Simon Castellan * Kate Deplaix [ocsigen]: https://www.ocsigen.org/ [main documentation page]: https://ocsigen.org/tuto/latest/manual/basics eliom-12.1.0/VERSION000066400000000000000000000000071521623100200137350ustar00rootroot0000000000000011.1.1 eliom-12.1.0/_tags000066400000000000000000000000131521623100200137020ustar00rootroot00000000000000true:debug eliom-12.1.0/build/000077500000000000000000000000001521623100200137675ustar00rootroot00000000000000eliom-12.1.0/build/_tags000066400000000000000000000001051521623100200150030ustar00rootroot00000000000000<*.{ml,byte,native}>:package(unix,ocamlbuild,js_of_ocaml-ocamlbuild) eliom-12.1.0/build/build.ml000066400000000000000000000065301521623100200154240ustar00rootroot00000000000000open Ocamlbuild_plugin module Pack = Ocamlbuild_pack let best = if Sys.command "command -v ocamlopt > /dev/null" = 0 then "native" else "byte" module Conf = struct let server_dir = "server" let client_dir = "client" let type_dir = "type_dir" end module Intern = struct let with_eliom_ppx = Some (function | `Client -> "src/ppx/ppx_eliom_client_ex." ^ best | `Server -> "src/ppx/ppx_eliom_server_ex." ^ best) let with_package = function | "eliom.ppx.type" -> "pkg_ppx_eliom_types" | "eliom.ppx.client" | "eliom.ppx.server" | "eliom.syntax.predef" | "eliom.client" | "eliom.server" -> (* do noting in this case *) "pkg_dummy" | _ -> assert false end module Eliom_plugin = Ocamlbuild_eliom.MakeIntern (Intern) (Conf) let _ = dispatch (fun x -> Eliom_plugin.dispatcher x; match x with | After_rules -> Doc.init (); let link source dest = rule (Printf.sprintf "%s -> %s" source dest) ~dep:source ~prod:dest (fun env _ -> Cmd (S [A "ln"; A "-f"; P (env source); P (env dest)])) in (* add I pflag *) pflag ["ocaml"; "compile"] "I" (fun x -> S [A "-I"; A x]); pflag ["ocaml"; "infer_interface"] "I" (fun x -> S [A "-I"; A x]); pflag ["ocaml"; "doc"] "I" (fun x -> S [A "-I"; A x]); (* add syntax extension *) let add_syntax name path = let bytes_dep = Findlib.(link_flags_byte [query "bytes"]) in (* hack : not dep when "compile" to avoid the extension syntax to be link with binaries *) (* the dep with ocamldep make sure the extension syntax is compiled before *) flag ["ocaml"; "compile"; "pkg_" ^ name] (S [ A "-ppx" ; Quote (S [P (path ^ name ^ "_ex." ^ best); A "-as-ppx"]) ]); flag ["ocaml"; "ocamldep"; "pkg_" ^ name] (S [ A "-ppx" ; Quote (S [P (path ^ name ^ "_ex." ^ best); A "-as-ppx"]) ]); dep ["ocaml"; "ocamldep"; "pkg_" ^ name] [path ^ name ^ "_ex." ^ best]; flag_and_dep ["ocaml"; "infer_interface"; "pkg_" ^ name] (S [ A "-ppx" ; Quote (S [P (path ^ name ^ "_ex." ^ best); A "-as-ppx"]) ]); dep ["ocaml"; "infer_interface"; "pkg_" ^ name] [path ^ name ^ "_ex." ^ best]; flag_and_dep ["doc"; "pkg_" ^ name] (S [ A "-ppx" ; Quote (S [P (path ^ name ^ "_ex." ^ best); A "-as-ppx"]) ]); dep ["doc"; "pkg_" ^ name] [path ^ name ^ "_ex." ^ best] in add_syntax "ppx_eliom_utils" "src/ppx/"; add_syntax "ppx_eliom_types" "src/ppx/"; (* link executable aliases *) let link_exec f t = link (Printf.sprintf "src/tools/%s.byte" f) (Printf.sprintf "src/tools/%s.byte" t); link (Printf.sprintf "src/tools/%s.native" f) (Printf.sprintf "src/tools/%s.native" t) in List.iter (link_exec "eliomc") ["eliomopt"; "eliomcp"; "js_of_eliom"]; link_exec "distillery" "eliom-distillery" | _ -> ()) let _ = Options.make_links := false; Options.plugin := false; Options.use_ocamlfind := true; Ocamlbuild_unix_plugin.setup (); Ocamlbuild_pack.Main.main () eliom-12.1.0/build/doc.ml000066400000000000000000000037471521623100200151010ustar00rootroot00000000000000open Ocamlbuild_plugin module Pack = Ocamlbuild_pack (* Doc should move in its own ocamlbuild plugin *) (* Compile the wiki version of the Ocamldoc. Thanks to Till Varoquaux on usenet: http://www.digipedia.pl/usenet/thread/14273/231/ *) let ocamldoc_wiki tags deps docout docdir = let tags = tags -- "extension:html" in Ocamlbuild_pack.Ocaml_tools.ocamldoc_l_dir tags deps docout docdir let ocamldoc_man tags deps docout docdir = let tags = tags (* -- "extension:html" *) in Ocamlbuild_pack.Ocaml_tools.ocamldoc_l_dir tags deps docout docdir let init_wikidoc () = try let wikidoc_dir = let base = Ocamlbuild_pack.My_unix.run_and_read "ocamlfind query wikidoc" in String.sub base 0 (String.length base - 1) in Ocamlbuild_pack.Rule.rule "ocamldoc: document ocaml project odocl & *odoc -> wikidocdir" ~insert:`top ~prod:"%.wikidocdir/index.wiki" ~stamp:"%.wikidocdir/wiki.stamp" ~dep:"%.odocl" (Ocamlbuild_pack.Ocaml_tools.document_ocaml_project ~ocamldoc:ocamldoc_wiki "%.odocl" "%.wikidocdir/index.wiki" "%.wikidocdir"); flag ["wikidoc"] & S [A "-colorize-code"; A "-i"; A wikidoc_dir; A "-g"; A "odoc_wiki.cma"]; pflag ["wikidoc"] "subproject" (fun sub -> S [A "-passopt"; A "-subproject"; A sub]) with Failure e -> () (* Silently fail if the package wikidoc isn't available *) let init_mandoc () = Ocamlbuild_pack.Rule.rule "ocamldoc: document ocaml project odocl & *odoc -> mandocdir" ~insert:`top ~prod:"%.mandocdir/man.%(ext)" ~stamp:"%.mandocdir/man.%(ext).stamp" ~dep:"%.odocl" (Ocamlbuild_pack.Ocaml_tools.document_ocaml_project ~ocamldoc:ocamldoc_man "%.odocl" "%.mandocdir/man.%(ext)" "%.mandocdir"); pflag ["apiref"] "man_ext" (fun ext -> S [A "-man-mini"; A "-man-section"; A ext; A "-man-suffix"; A ext]) let init () = init_wikidoc (); init_mandoc (); (* ocamldoc intro *) pflag_and_dep ["doc"] "with_intro" (fun f -> S [A "-intro"; P f]) eliom-12.1.0/doc/000077500000000000000000000000001521623100200134355ustar00rootroot00000000000000eliom-12.1.0/doc/README.md000066400000000000000000000076551521623100200147310ustar00rootroot00000000000000# How the Eliom documentation is generated The Eliom documentation published at is built with **odoc** and themed with the Ocsigen site chrome by [**wodoc**](https://github.com/ocsigen/wodoc) (an odoc driver). The same odoc sources are also what ocaml.org renders. Eliom is a **client/server** project: it ships its server and client APIs as two libraries of the same package (`eliom.server` / `eliom.client`) with the **same module names**, so `dune build @doc` collides. The API is therefore built with `odoc_driver eliom --remap` (the engine ocaml.org uses) on the **installed** package — i.e. the documented version is whatever the build switch has installed. ## Sources | What | Where | Format | |---|---|---| | Manual | the package's `(documentation)` `.mld` (built with the API by odoc_driver) | odoc pages | | Manual nav order | `doc/manual/menu.wiki` (overlaid from the `wikidoc` branch by CI) | wiki menu | | Server / client API | `doc/server.indexdoc` / `doc/client.indexdoc` (curated module index) | odoc index | | API (`.mli`) | the installed `eliom` package | odoc comments | | Site configuration | [`doc/wodoc`](wodoc) | wodoc config (S-expression) | The whole site — per-side API navs, the client/server switch, the body colour, the shared manual nav, and the rewriting of sibling Ocsigen projects' cross-refs to relative links — is declared in [`doc/wodoc`](wodoc) and produced by a single `wodoc build`. There is **no `build.sh`** and no Python. ## Build `wodoc build` documents the **installed** `eliom`, so install it first (from the ref you want to document) in a switch that also has `odoc_driver` and `wodoc`: ``` opam install ./eliom.opam # the version to document opam install wodoc # the odoc driver / theming # overlay the manual menu (lives on the wikidoc branch): git show origin/wikidoc:doc/dev/manual/menu.wiki > doc/manual/menu.wiki wodoc build --config doc/wodoc --label dev --out _doc-site/dev \ --menu https://ocsigen.org/doc/menu.html ``` `wodoc build` runs `odoc_driver eliom --remap` (after preprocessing the installed manual `.mld`), assembles every page into the Ocsigen site and writes the version redirect. Add `--local` to fetch the shared `/css//img/` assets and preview offline. > **Prerequisite (CI).** Eliom's cross-library links need the `odoc_driver` > `base_args` fix, which is not yet upstreamed. CI must pin `odoc` / `odoc-driver` > from the patched source until that lands; see the workflow comments. ## Deployment (CI) [`.github/workflows/doc.yml`](../.github/workflows/doc.yml) builds and publishes to the project's **`gh-pages`** branch (served at `ocsigen.org/eliom/`). The CI triggers **only on `master`**, so pushing any other branch never deploys: - **push to `master`** → rebuilds and deploys the **`dev`** docs only. Stable versions are **not** built by CI: they are generated by hand and committed to `gh-pages` (with the `latest` symlink repointed) at release time. Each CI run replaces only the `dev/` directory; the other version directories already on `gh-pages` are preserved. ## Releasing a stable version The CI builds only `dev/`. To publish a stable version, trigger the **Documentation** workflow's `release` job with the **version** input — either: - **CLI** (from a clone of the repo): `gh workflow run doc.yml -f version=1.2.3` - **GitHub UI**: repo → *Actions* → *Documentation* (left sidebar) → *Run workflow* (top-right) → set **version** (e.g. `1.2.3`) → *Run workflow*. The `release` job freezes the current `dev/` docs as `//`, repoints the `latest` symlink, writes the root redirect and refreshes `versions.json` — via `wodoc release --from dev --version `. No rebuild: the docs of a release are exactly the `dev` docs at that point. Equivalently, by hand on a `gh-pages` checkout: ``` wodoc release --site . --from dev --version git add -A && git commit -m "Release doc " && git push ``` eliom-12.1.0/doc/client.indexdoc000066400000000000000000000005271521623100200164360ustar00rootroot00000000000000{1 Client API} {!modules: Eliom_lib Eliom_client Eliom_client_value } {2 Content and form creation} {!modules: Eliom_content.Html Eliom_content.Html.Manip Eliom_content.Svg Eliom_content.Xml } {2 Client/server communication} {!modules: Eliom_service Eliom_shared Eliom_cscache Eliom_bus Eliom_comet Eliom_react } {2 Index} {!indexlist} eliom-12.1.0/doc/clientserver-applications.mld000066400000000000000000000155641521623100200213370ustar00rootroot00000000000000{0 Programming client-server applications with Eliom} {1 General principles} {2 What is a client/server Eliom application} An Eliom application is a distributed application that runs partly on the server, partly on a browser. The program is written entirely in OCaml, with a syntax extension to distinguish between server and client code. During the compilation process, the program is decoupled into server and client parts. The server part is compiled as is standard for OCaml code, while the client part is compiled to Javascript to be run in the browser. For developing an Eliom application, we recommend that you use our {{!page-"ppx-syntax"}PPX syntax extension}. An interesting feature of Eliom applications is that the client-side process does not stop when you click on a link or send a form, and it is possible to keep the traditional Web interaction (with URLs, bookmarks, back button, etc). For example, if the page is playing music, the music won't stop when the user proceeds to other pages on the Web site. Client-side parts are using {!Lwt}, enabling concurrency in the browser very easily. As both parts are implemented in OCaml, it is very easy to use client-side OCaml data on server side, and vice-versa. Eliom handles the communication between client and server automatically in both directions. For example, it is possible to use a server-side variable in the client program. Eliom also implements an "HTTP-push" mechanism, allowing the server to send messages to a client. Client-side parts of the program can use most Eliom features, just as usual, for example to create HTML, links, and forms from services. On the server, it is possible to save data (some state) for each client process (that is, one tab in a browser), simply by using Eliom references with scope [`Client_process]. You can register services for one client process, or even set cookies for one tab. {2 How it works} The code of an Eliom application is written in OCaml, with {{!page-"clientserver-language"}a syntax extension} to distinguish between server and client code. The files using this syntax usually have the extension [.eliom]. As the compling process is quite complex, we provide commands called [eliomc], [eliomopt] and [js_of_eliom] that do everything for you: separating client and server parts, calling [ocamlc], [ocamlopt], [js_of_ocaml], etc. Services belonging to the application are registered using the module {!Eliom_registration.App}. More precisely, this is a functor that needs to be applied for each application you create. These services just return HTML pages as usual (using {!Eliom_content.Html}) The client-side program (compiled in JavaScript) is added automatically by Eliom, with all its data, and run automatically when the page is loaded. Module {!Eliom_client} provides useful functions for client side programming with Eliom: e.g. {!Eliom_client.change_page} permits switching to another page. Module {!Eliom_comet} allows the server to send notifications to the client (even if the client is not explicitely doing a request). Use module {!Eliom_react} to make client-server reactive programming (using the {{:http://erratique.ch/software/react} React} external library). {2 The App functor} For each Eliom application, you must create a service registration module by applying the [App] functor: {@ocaml[ module My_app = Eliom_registration.App ( struct let application_name = "the name of your application" let global_data_path = None end) ]} the [application_name] parameter is the name of the JavaScript file containing the application. [global_data_path] specifies the path of an auxilliary service needed for mobile apps, and you can provide [None] for standard Web applications. Then you can do for example: {@ocaml[ let my_service = My_app.create ~path:(Eliom_service.Path [""]) ~meth:(Eliom_service.Get Eliom_parameter.unit) (fun () () -> Lwt.return (html (head (title (txt "Hi!")) []) (body [p [txt "Hey."]]))) ]} Eliom will add automatically the required headers to send the client side program and all its data. {1 Application, life and death} When an user enters the page of a service registered by an application module (created with the [App] functor), the application is started. During the life of the application, a single OCaml program will run on the browser: Lwt threads will keep running, global variables will stay available, etc. until the application is closed. The application will keep running when the user clicks on links to pages inside the same application. This application will be closed when: - the user closes the browser tab containing the application, - the user goes to a page outside of the application, - the user changes the current url by another mean than the application interaction (reload the page with [F5], manual typing of URL, ...), - the application call the {!Eliom_client.exit_to} function. It is possible to prevent the application from starting when visiting an application page by setting the [do_not_launch] to [true] at the service registration: {@ocaml[ let no_launch_service = My_app.register ~option:{ Eliom_registration.default_appl_service_options with do_not_launch = true } ~path:(Eliom_service.Path [""]) ~meth:(Eliom_service.Get Eliom_parameter.unit) (fun () () -> Lwt.return (html (head (title (txt "Hi!")) []) (body [p [txt "Hey."]]))) ]} That way, you can delay JavaScript loading until it is really needed. Visiting a service registered with [do_not_launch=true] will not stop a running application. By default, every link of form towards another service of the same application is reimplemented by Eliom so that it does not stop the application. Instead of asking the browser to load a new page, Eliom does an XML HTTP request (XHR). You can avoid this and insert regular links or forms by adding the optional parameter [~xhr:false]. This will force reloading the application when the link is clicked. {2 Navigating in and out of the application.} Two functions are available client-side for changing the current page without interaction from the user. The function {!Eliom_client.change_page} goes to the service taken as parameter. If the service is in another application or not in an application, the current application will be stopped. The function {!Eliom_client.window_open} opens an Eliom service in a new browser window (cf. JavaScript's [window.open]). {!Eliom_client.exit_to} changes the current page and always leaves the application. {2 Leaving application and going back} Usually, when going to an application page, a new client process is launched on the server, but there are situations where an old client process is used instead: Browsers tend to take the result from their cache when using the back button even if the page was marked (by HTTP headers) as non-cacheable. eliom-12.1.0/doc/clientserver-cache.mld000066400000000000000000000010761521623100200177050ustar00rootroot00000000000000{0 Caching the data and off-line applications} {1 Caching the data} Module {!Eliom_cscache} implements a cache of data that can be used both for keeping data on client side or keeping values in memory during a request. Have look at the documentation of this module for more information. Also have a look to {!Ocsigen_cache} for a local (client or server) cache that keeps the most recently used values. {1 Off-line applications} In the near future, Eliom_cscache will make it possible to save persistent data, which is useful for implementing off-line applications. eliom-12.1.0/doc/clientserver-communication.mld000066400000000000000000000324461521623100200215140ustar00rootroot00000000000000{0:communication Communication between the client and the server} Besides injections and client values, as described in the documentation of our {{!page-"ppx-syntax"}PPX syntax extension}, there are multiple ways for the client and the server to exchange values. {1:rpc Remote Procedure Calls} Eliom provides an easy way to call server functions from the client using a PPX syntax extension (provided by opam package [ocsigen-ppx-rpc]). Example: {%wodoc:@ class=shared%} {@ocaml[ let%rpc log (str : string) : unit Lwt.t = Lwt_io.write_line Lwt_io.stdout str ]} {%wodoc:@ class=client%} {@ocaml[ let%client () = Eliom_client.onload (* NB The service underlying the server_function isn't available on the client before loading the page. *) (fun () -> Lwt.async (fun () -> log "Hello from the client to the server!")) ]} The type annotations are mandatory, for the syntax extension to be able to inject the correct serialisation functions. The serialisation functions for each types must be derivable using {!Deriving_Json}. To do that, just add [[@@deriving json]] after your type definitions. Example: {%wodoc:@ class=shared%} {@ocaml[ type%shared t = A | B [@@deriving json] ]} Exceptions raised in the server-side function cannot be handled directly on the client; it is impossible to marshal them in OCaml to send them to the client. Instead, if an exception is raised in the server function, the function application fails (in Lwt) on the client with the exception {!exception Eliom_client_value.Exception_on_server} whose argument describes the original exception (according to [Printexc.to_string]). {1:rpc Remote Procedure Calls: low level API} RPCs are just syntactic sugar for pathless services returning OCaml values. The PPX syntax extension translates the function definition into a {!Eliom_client.server_function}. For example, the example above produces the following code: {@ocaml[ let%server log str = Lwt_io.write_line Lwt_io.stdout str let%client log = ~%(Eliom_client.server_function [%derive.json: string] log) ]} A function ['a -> 'b Lwt.t] can be wrapped on the server by {!Eliom_client.server_function}. When the result is injected to the client, it appears as a plain function ['a -> 'b Lwt]. It is necessary to provide an instance of {!Deriving_Json} for the argument type, to safely send the argument from the client to the server. Syntax [[%json: t]] corresponds to the serialisation functions of type [t] Every call to [server_function] creates a new pathless POST service. If you want to use a server function in multiple places, it is thus advisable to only apply [server_function] once and bind it to an identifier. {1:notifs Notifications from server to clients} Module {!Eliom_notif} makes it possible for a server to send values to clients with a very simple interface. See later in this chapter for lower level server push notifications. Create a module for each kind of notifications you want using the {!Eliom_notif.Make_Simple} functor. For example, there would be a module for new message notifications in a chat. Each client process must subscribe to each resource for which it wants to receive the notifications (each chat currently opened, in our example), using function {!Eliom_notif.Make_Simple.listen}. Then, the server can send a notification to all the clients listening on a resource, using function {!Eliom_notif.Make_Simple.notify}. More details in the API documentation of module {!Eliom_notif}. {1 Services returning OCaml values} Server functions are implemented using special services that take and return OCaml values. In this section we will see how to define services returning OCaml data. This is a lower level interface to do remote procedure calls, and you won't probably need this for basic use. These services are registered using {!Eliom_registration.Ocaml} and can be called using {!Eliom_client.call_ocaml_service}. Such services cannot be visited by the browser as Web pages. You usually want POST pathless services for this use case. This corresponds to remote function calls, that are typically handled by pathless POST services. Example of use: {@ocaml[ open Eliom_content let pi_service = Eliom_registration.Ocaml.create ~path:Eliom_service.No_path ~meth:(Eliom_service.Post (Eliom_parameter.unit, Eliom_parameter.unit)) (fun () () -> Lwt.return 3.1415926535) let _ = My_appl.create ~path:(Eliom_service.Path ["pi"]) ~meth:(Eliom_service.Get Eliom_parameter.unit) (fun () () -> ignore [%client (Lwt.ignore_result ( let%lwt pi = Eliom_client.call_ocaml_service ~service:~%pi_service () () in Lwt.return ( Dom_html.window##alert (Js.string ("pi = "^ string_of_float pi)))) : unit) ]; Lwt.return Html.D.( html (head (title (txt "pi")) []) (body []))) ]} Since the client-side representation of values differs from the server-side representation, there are restrictions on what can be sent. The restrictions are the same as for the [~%variable] mechanism. (See {{!page-"clientserver-wrapping"}chapter Wrapping}.) {1:client_sending_data Send OCaml values to services} The client can send OCaml values as parameters to services. To do that, declare the expected parameter type using [Eliom_parameter.ocaml] (see {!Eliom_parameter_sigs.S.ocaml}). This is used for example to implement server functions. Since the server cannot trust the client to send correctly-formed data, Eliom is not using the standard OCaml marshalling mechanism. (The server needs to be able to check that the value is of the expected type.) For this reason, you must declare the types of the data you want to be able to send to the server using our [ppx_deriving] syntax extension: {@ocaml[ type%shared some_type = (int * string list) [@@deriving json] type%shared another_type = | A of some_type | B of another_type [@@deriving json] ]} This type can now be used as a parameter for a service: {@ocaml[ open Eliom_content let s = My_appl.create ~path:(Eliom_service.Path ["s1"]) ~meth: (Eliom_service.Get (Eliom_parameter.ocaml "param" [%derive.json: another_type])) (fun v () -> Lwt.return Html.D.( html (head (title (txt "title")) []) (body [ match v with | A _ -> txt "A" | B _ -> txt "B" ]))) let _ = My_appl.create ~path:(Eliom_service.Path ["s2"]) ~meth:(Eliom_service.Get Eliom_parameter.unit) (fun () () -> Lwt.return Html.D.( html (head (title (txt "title")) []) (body [p ~a:[a_onclick [%client (fun _ -> Lwt.async (fun () -> Eliom_client.change_page ~service:~%s (A (1, ["s"])) ())) ]] [txt "Click to send Ocaml data"]]))) ]} It works for the datatypes you define, and the data types from OCaml's standard library. For types defined in third-party libraries, have a look at deriving's {{:href:http://code.google.com/p/deriving/wiki/Introduction}documentation} and Js_of_ocaml's {!Deriving_Json}. {1 Server sending data (lower level interfaces for notifications)} Module {!Eliom_notif} described above is implemented on top of a mechanism to allow the server to send data to a client. We call this mechanism {e Comet}. The same idea is also known as {e HTTP push}. The simple low-level version above which all other following mechanisms are implemented is provided in the {!Eliom_comet.Channel} module. Comet defines channels which can transfer data. A channel is created using an Lwt stream. It is a kind of cooperative lazy list. The two main methods to create a stream are through the functions {!Lwt_stream.from} and {!Lwt_stream.create}. {@ocaml[ val from : (unit -> 'a option Lwt.t) -> 'a t val create : unit -> 'a t * ('a option -> unit) ]} Function {!Lwt_stream.from} makes possible to create a stream where a new value is added each time a function returns. Function {!Lwt_stream.create} returns a stream and a function to push new values to the stream. On client-side, the type {!Eliom_comet.Channel.t} is just an Lwt stream {!Lwt_stream.t}. There are 3 kinds of channels, depending on how you want to send data. - Channels created with {!Eliom_comet.Channel.create} have a buffer with a limited size. Message are read from the stream as soon as they are available, i.e. for stream created with {!Lwt_stream.from}, that means that the function is called another time as soon as the previous one terminates. For stream created with {!Lwt_stream.create}, this is as soon as they are pushed. If the client misses too many messages (more than the size of the buffer) it will receive an exception {!Eliom_comet.Channel_full} when reading data from the stream. - Channels created with {!Eliom_comet.Channel.create_newest} have no buffering and can lose messages, but the client will always receive the last value: For instance, if many messages are sent in a short time, the client may receive only the last one. Those channels never raise {!Eliom_comet.Channel_full}. - Channels created with {!Eliom_comet.Channel.create_unlimited} consume data on the stream only when their is a request from the client. Channels can be closed on client side by cancelling a thread waiting for data on it. Like services, channels have a scope (only site or client process). The constraints vary with respect to the scope you choose: - Channels created with scope {!Eliom_common.site_scope} or using {!Eliom_comet.Channel.create_newest} are stateless channels: the memory consumption does not depend on the number of users requesting data on it. When the channels are not reachable from the server code, they are garbage-collected and closed. Named stateless channels can be accessed from {{!page-"clientserver-applications".cors_channels}other servers}. - Channels created with scope {!Eliom_common.default_process_scope} must be created inside a service handler. They are assigned to a particular client process. Different channels created with the same stream do not share memory. They are closed when requested or when the client process is closed. It is possible to know when a client stop requesting data on those channels using {!Eliom_comet.Channel.wait_timeout}. Be careful about memory consumption when using client process channels. {2 Comet configuration} The server can push data to a client only when the client has an open HTTP connection waiting for a response. As of now, a comet request can only last at most 10 seconds. After that, the client can either do a new request or stale for some time: this is the activity behavior. This can be configured on client-side, using the functions from {!Eliom_comet.Configuration} For instance, if you receive data which doesn't need frequent updates, you can set the time between different requests to a high value, and stop requesting data as soon as the browser loses the focus. {@ocaml[ open Eliom_comet.Configuration let slow_c = new_configuration () in set_active_until_timeout slow_c false; set_time_between_request slow_c 60. ]} If you need more reactivity for a few seconds, do: {@ocaml[ open Eliom_comet.Configuration let fast_c = new_configuration () in set_set_always_active fast_c true; set_set_time_between_request fast_c 0.; ignore (Lwt_js.sleep 10. >|= (fun () -> drop_configuration fast_c)) ]} The original setting will be reset after the drop. {1 Reactive values} A common usage of comet is for the server to update a value available on client side. A convenient way to implement this is to use reactive programming. Eliom provides a reactive interface for channels, using the {{:http://erratique.ch/software/react} react}. library. To share a React event or signal with the client, use functions {!Eliom_react.Down.of_react} or {!Eliom_react.S.Down.of_react} On client-side, the value returned by those functions is directly a React event or signal. The opposite is also available using {!Eliom_react.Up.create}. Since this is implemented using Comet, tuning Comet configuration will also affect the behaviour of shared react variables. {1 Client-Server shared bus} It is sometimes useful to have a bidirectional channel shared between multiple clients. This is the purpose of buses. Those are created using {!Eliom_bus.create}. Since the server will also receive data on the bus, the description of the type (using [deriving]) is needed to create a bus. Like comet channels, the behaviour of buses can be tuned using the module {!Eliom_comet.Configuration}. There are additionnal configuration options available for buses to tune the client-side buffering. {1:cors_channels Another Server sending data (Comet on another server)} It is possible to access a named stateless channel created on another server. It has to be declared using {!Eliom_comet.Channel.external_channel}. The declaration of the channel must match exactly the creation. The server generating the page and the server that created the channel must run exactly the same version of Eliom. By default a browser can't do requests to a different server, to allow that the server serving the channel must allow Cross-Origin Resource Sharing using the {{!/ocsigenserver/page-cors}CORS Ocsigenserver extension}. eliom-12.1.0/doc/clientserver-html.mld000066400000000000000000000425731521623100200176150ustar00rootroot00000000000000{0:server_generating_html Generating HTML pages} Ocsigen provides several ways to generate and type HTML pages. - The default technique to produce HTML pages using Eliom are the {!Eliom_content.Html.F}, {!Eliom_content.Html.D} and {!Eliom_content.Html.R} modules. It is the only one supported for client-server Eliom programs. This module provides a typing based on OCaml's polymorphic variants, which ensures at compile time, that the pages you will generate will respect the recommendations of the W3C (or be very close). - It is also possible to use a syntax extension to write your pages with the usual HTML syntax. This solution is also typed with polymorphic variants and is compatible with the previous one. - You can also choose to generate untyped html as text. The types in OCaml closest to XML types are {e polymorphic variants}. Ocsigen uses them to provide a module with very good HTML typing. The full documentation is available {{!Tyxml.Html}in the TyXML documentation}. {1 Generating HTML for Eliom applications} {2 The TyXML library vs. the DOM API} On client side there are two kinds of HTML representations: one is based on the {{!/tyxml/page-index}TyXML library} and the other one is the browser DOM tree accessible through Js_of_ocaml modules {!Js_of_ocaml.Dom} and {!Js_of_ocaml.Dom_html}. The TyXML representation is a OCaml immutable typed tree. The DOM tree is mutable structure manipulated using the browser API which permit the modification of the displayed page. In the DOM represention adding a node as a child to an other node removes it from its previous ancessor. Since those representation does not behave at all the same way, they are not used for the same thing. - It is far easier and safer to describe content using TyXML, but it is not possible to add a TyXML element to the page without explicit conversion to the DOM representation. - The TyXML representation has the same interface on client and server side. This allows share code between server and client. - Dom manipulation is heavy: to build some part of a tree, one needs to create each node separately then append them to their parents. For example, here is a [div] element built with TyXML and then converted to the DOM representation using the module {!Eliom_content.Html}: {%wodoc:@ class=client%} {@ocaml[ open%client Eliom_content.Html.D let%client n = div ~a:[a_id "some div id"] [ txt "some text"; br (); txt "some other text"; ] let%client b = Eliom_content.Html.To_dom.of_div n ]} And here the same built using the DOM API: {%wodoc:@ class=client%} {@ocaml[ open%client Dom open%client Dom_html let%client d = let d = createDiv document in let t1 = document##createTextNode(Js.string "some text") in let t2 = document##createTextNode(Js.string "some other text") in let b = createB document in appendChild d t1; appendChild d b; appendChild d t2; d##.id := (Js.string "some div id"); d ]} To ease the DOM manipulation on the client, the usual DOM manipulation function are also available on TyXML elements. See section the next section for HTML element manipulation, by value and by reference. {2:unique HTML element manipulation, by value and by reference} There are four modules to create typed HTML: {!Eliom_content.Html.F}, {!Eliom_content.Html.D}, {!Eliom_content.Html.C} and {!Eliom_content.Html.R}. The last one is for reactive elements and is addressed in {{!page-"clientserver-html"}another section}. It is possible to mix the four kinds of nodes in the same page. Elements built with [Html.F] are sent by value, while elements built with [Html.D] are sent to the client by reference. Eliom adds an identifier as attribute of [D] elements to make it possible to find them back in the page from client side. {%wodoc:div class="concept"%}{b In short: D or F?} - If your program is server-side only (Web site) or client-side only, use module [F]. - If you are writing a client-server Eliom program, module [D] will probably do what you expect most of the times. We recommend this as the default for beginners. But it will add identifiers attributes to each node. - If you want to avoid these identifiers when not needed, use module [D] only when the node is used in an injection ([~%n]), and module [F] otherwise. {%wodoc:end%} Sending elements by reference allows easy manipulation of elements included in the initial html document from event handlers, as the [input] element in the following example. {%wodoc:@ class=server%} {@ocaml[ let%server main_service = My_app.register_service ~path:(Eliom_service.Path [""]) ~meth:(Eliom_service.Get Eliom_parameter.unit) (fun () () -> let open Eliom_content.Html.D in let input = input ~a:[a_input_type `Text] () in let onclick_handler = [%client (fun _ -> let v = Js.to_string (Eliom_content.Html.To_dom.of_input ~%input)##.value in Dom_html.window##alert(Js.string ("Input value :" ^ v))) ] in let button = button ~a:[a_onclick onclick_handler] [txt "Read value"] in Lwt.return (html (head (title (txt "Test")) []) (body [input; button]))) ]} In this example, if the input button would have been incorrectly sent by value, two different input fields would have been created: one displayed in the document and one referenced from the event handler. The latter will always contains an empty value. There are still two situations where sending elements by value is still required: - one want to have multiple occurences of the same elements in the document. Indeed, elements sent by reference follow the DOM semantics where an element have only one instance in current document. For example, the following list will contains a single element:{%html:
%} {%html:
%} [let li = li [txt "Shared item"] in ul [li; li; li;]]. - one have a large page with a lot elements. Handling elements by references add a small overhead while loading the page, around 50ms per 1000 elements on a not so fast computer. In any case, it is possible to mix elements sent by references and elements sent by value in the same document. By default, a reference on an element is only valid in the current HTTP request: hence, sending an element built with [Html.D] in two different pages will produce two distinct nodes. If you want to define a element reference that is preserved accross the different page of an application, you must explicitely name this element with the function {!val Eliom_content.Html.Id.create_named_elt}, that takes as parameters an element identifier and a non named element. Element identifiers are created with the function {!val Eliom_content.Html.Id.new_elt_id}. See also section {{!page-"clientserver-applications".global}Global elements of an application}. The module {!Eliom_content.Html.Manip} allows using the classical DOM manipulation functions (e.g. appendChild, addEventlistener, ...) directly on the identifier of an HTML elements. {2:reactive Reactive DOM} [Eliom_content.Html.R] allows one to insert time-varying values into the DOM tree. It relies on React's signal ['a React.signal]. More information about react can be found on the {{:http://erratique.ch/software/react} homepage}. The react nodes also use {{:https://github.com/ocsigen/reactiveData}ReactiveData}, which allows to manipulate lists of nodes in a reactive way. When dealing with dynamic content, one usally ends up with a lot of imperative DOM manipulations: replacing, appending, removing DOM elements, updating attributes, etc. [Html.R] hides most of those imperative DOM operations. Every time a signal changes, the corresponding DOM tree updates itself. {3 Usage on client side} To insert reactive DOM elements, just use module [Html.R] instead of [Html.D] or [Html.F] for these elements. [Html.R] makes also possible to define reactive attributes. Use function [Html.R.node : 'a elt React.signal -> 'a elt] to insert a reactive node in a page. {4 Example} {%wodoc:@ class=shared%} {@ocaml[ open%shared Eliom_lib open%shared Eliom_content open%shared Html open%shared F ]} {%wodoc:@ class=server%} {@ocaml[ module%server Reactivenodes_app = Eliom_registration.App ( struct let application_name = "reactivenodes" let global_data_path = None end) ]} {%wodoc:@ class=client%} {@ocaml[ open%client Eliom_content.Html let%client split s = let len = String.length s in let rec aux acc = function | 0 -> acc | n -> aux (s.[n - 1] :: acc) (pred n) in aux [] len let%client value_signal, set_value = React.S.create "initial" let%client value_len = React.S.map String.length value_signal let%client content_signal : Html_types.div_content_fun elt React.signal = React.S.map (fun value -> let l = split value in F.div ( List.map (fun c -> F.p [F.txt (Printf.sprintf "%c" c)]) l )) value_signal let%client make_color len = let d = (len * 10) mod 255 in Printf.sprintf "color: rgb(%d,%d,%d)" d d d let%client make_client_nodes () = [ D.p [R.txt value_signal]; D.p ~a:[ R.a_style (React.S.map make_color value_len)] [R.txt value_signal]; R.node content_signal ] ]} {%wodoc:@ class=server%} {@ocaml[ let%server make_input () = let inp = D.Raw.input ~a:[a_input_type `Text] () in let _ = [%client (Lwt_js_events.(async (fun () -> let inp = To_dom.of_input ~%inp in keyups inp (fun _ _ -> let s = Js.to_string (inp##.value) in set_value s; Lwt.return ()))) : unit) ] in inp let%server main_service = Eliom_service.create ~path:(Eliom_service.Path []) ~meth:(Eliom_service.Get Eliom_parameter.unit) () let%server () = Reactivenodes_app.register ~service:main_service (fun () () -> let inp = make_input () in let cldiv = C.node [%client D.div (make_client_nodes ())] in Lwt.return (Eliom_tools.F.html ~title:"reactivenodes" ~css:[["css"; "reactivenodes.css"]] (body [F.h1 [txt "Reactive DOM"]; inp; F.h2 [txt "Client side reactive nodes:"]; cldiv; ]) )) ]} {2:inject Dom & Client-values} [Eliom_content.Html.C] allows one to insert client-side content into server-side HTML-trees. This makes possible, for example, to insert reactive nodes in a server-side generated page. {3 Example} {%wodoc:@ class=shared%} {@ocaml[ open%shared Eliom_lib open%shared Eliom_content open%shared Html open%shared F ]} {%wodoc:@ class=server%} {@ocaml[ module%server Testnodes_app = Eliom_registration.App ( struct let application_name = "testnodes" let global_data_path = None end) let%server main_service = Eliom_service.create ~path:(Eliom_service.Path []) ~meth:(Eliom_service.Get Eliom_parameter.unit) () ]} {%wodoc:@ class=client%} {@ocaml[ open%client Eliom_content.Html let%client (value_signal : string React.signal), set_value = React.S.create "initial" ]} {%wodoc:@ class=server%} {@ocaml[ let%server client_reactive_attrib () = [%client R.a_style value_signal ] let%server client_reactive_title () = [%client F.h1 [txt "I'm a client node"] ] let%server () = Testnodes_app.register ~service:main_service (fun () () -> Lwt.return (Eliom_tools.F.html ~title:"testnodes" ~css:[["css"; "testnodes.css"]] (body [ F.div ~a:[C.attr (client_reactive_attrib ())] [ C.node (client_reactive_title ()) ] ]))) ]} Module C is also available on client-side, to make it possible to use it in shared sections. {2:global Global elements of an application} Sometimes you may want to modify the content of an HTML element and to keep the element and its modified content when changing page. For example a [div] element which contains a chat box or a music player should be preserved while browsing across the different pages of your site. For this purpose, Eliom provides a notion of global element. Such elements are instantied only once for an application and that unique instance is used in every page that references the element. To create a global element, use function {{!val Eliom_content.Html.Id.create_global_elt}Eliom_content.Html.Id.create_global_elt}. {@ocaml[ val create_global_elt: 'a elt -> 'a elt ]} In the following example, the content of [global_list] will be preserved when you click on the "reload page" link. {%wodoc:@ class=shared%} {@ocaml[ open%shared Eliom_content.Html.D ]} {%wodoc:@ class=server%} {@ocaml[ module%server My_app = Eliom_registration.App ( struct let application_name = "myo" let global_data_path = None end) let%server global_list = Eliom_content.Html.Id.create_global_elt (ul []) let%server cpt = ref 0 let%server main_service = Eliom_service.create ~path:(Eliom_service.Path [""]) ~meth:(Eliom_service.Get Eliom_parameter.unit) () let%server reload_link = a ~service:main_service [txt "reload page"] () let%server _ = My_app.register ~service:main_service (fun () () -> let page_number = incr cpt; string_of_int !cpt in let append_item = [%client let item_text = "item inserted in page #" ^ ~%page_number in let item = Eliom_client.Html.of_li (li [txt item_text]) in Dom.appendChild (Eliom_client.Html.of_ul ~%global_list) item ] in let append_link = a ~a:[a_onclick append_item] [txt "append item"] in Lwt.return (html (head (title (txt "Test")) []) (body [h1 [txt ("Page #" ^ page_number)]; p [append_link]; p [reload_link]; global_list]) ) ) ]} Another use of global element is for external javascript that should be included in every page but must be executed only once in an application. In the following code snippet, the alert "global script" is displayed only once, while the alert "non global script" is display every time you click on the "reload page" link. {%wodoc:@ class=server%} {@ocaml[ open%server Eliom_content.Html.D let%server global_script = Eliom_content.Html.Id.create_global_elt (script (cdata_script "alert(\"global script\")")) let%server simple_script = script (cdata_script "alert(\"non global script\")") let%server main_service = Eliom_service.create ~path:(Eliom_service.Path []) ~meth:(Eliom_service.Get Eliom_parameter.unit) () let%server reload_link = a ~service:main_service [txt "reload page"] () let%server _ = My_app.register ~service:main_service (fun () () -> Lwt.return (html (head (title (txt "Global script example")) [global_script; simple_script]) (body [p [reload_link]]))) ]} {2 HTML syntax extension} It is possible to use regular HTML syntax using Tyxml's PPX syntax extension. See {{!/tyxml/page-ppx}Tyxml's manual}. {2:text_html Text HTML} The last possibility is to use untyped HTML. Just build strings containing your pages. Here is an example: {%wodoc:@ class=server%} {@ocaml[ let%server hello = Eliom_registration.Html_text.create ~path:(Eliom_service.Path ["hello"]) ~meth:(Eliom_service.Get Eliom_parameter.unit) (fun () () -> Lwt.return "Hello") ]} Writing HTML as text makes applications much more difficult to maintain. We do not recommend this. {2:custom_data Custom data for HTML} Eliom provides a type-safe interface for create new attributes of the form [data-*], using {!Eliom_content.Html.Custom_data}. {3 Creation} Custom data may be created either from string-conversation functions by {!val Eliom_content.Html.Custom_data.create} {%wodoc:@ class=server%} {@ocaml[ open%server Eliom_content let%server my_int_data = Html.Custom_data.create ~name:"my_int" ~of_string:int_of_string ~to_string:string_of_int () ]} or by a Json-deriving type {!Eliom_content.Html.Custom_data.create_json} {%wodoc:@ class=shared%} {@ocaml[ type%shared coord = { x : int; y : int; } [@@deriving json] let%shared coord_data = Html.Custom_data.create_json ~name:"coord" [%derive.json: coord] ]} {3 Injecting} Custom data can be injected into HTML-trees of type {!Eliom_content.Html.elt} by the function {!Eliom_content.Html.Custom_data.attrib}: {@ocaml[ div ~a:[Html.Custom_data.attrib coord_data {x = 100; y = 200}] [] ]} {3 Reading/writing the DOM} On the client side, custom data can be read from and written to JavaScript DOM elements of type {!type Js_of_ocaml.Dom_html.element}. Custom data can be read from a DOM-element with the function {!Eliom_content.Html.Custom_data.get_dom}. If no respective custom data attribute can be found in the element - the [default] value from creating the custom data is returned, if any, or - an exception [Not_found] is raised, otherwise. The custom data of a DOM-element can be set with the function {!Eliom_content.Html.Custom_data.set_dom}. {@ocaml[ [%client fun (div : Dom_html.element Js.t) -> let i = Html.Custom_data.get_dom div coord_data in debug "{x=%d; y=%d}" i.x i.y; Html.Custom_data.set_dom div coord_data { x = i.x + 1; y = i.y - 1 } ] ]} {3 Default value} If a custom data is created with the optional argument [default], calls to {!val Eliom_content.Html.Custom_data.get_dom} return that instead of throwing an exception [Not_found]. {@ocaml[ let coord_data' = Html.Custom_data.create_json ~name:"coord" default:{x=0;y=0;} [%derive.json: my_data] ]} eliom-12.1.0/doc/clientserver-react.mld000066400000000000000000000130761521623100200177430ustar00rootroot00000000000000{0:sharedreact Shared reactive programming} In reactive programming, the programmer declaratively defines relationships between different pieces of data, and between the data and what is displayed in the interface. This allows rapid development of robust user interfaces. {{!page-"clientserver-html".reactive}Another manual section} describes Eliom's client-side reactive infrastructure. Client-side reactive programming in itself does not adequately cover all the requirements of the modern Web. Namely, with client-centric programming, the initialization of the interface happens on the client, sometimes with a noticeable lag. Also, the HTML sent by the server contains little of the actual content, thus being unsuitable for search-engine indexing. To overcome this limitation, Eliom 5.0 and higher enable what we call {e shared} reactive programming. This means that we operate on signals that have both a server-side and a client-side meaning. The server-side signals produce a first version of the interface that is more than a skeleton, while the client-side signals are responsible for the dynamic updates. {1 Shared signals} Our client-side reactive infrastructure heavily relies on the {{:http://erratique.ch/software/react} React} library. The module {!Eliom_shared.React} builds on [React] to provide {e shared} signals (type [Eliom_shared.React.S.t]). We explain the ideas behind {!Eliom_shared.React} aided by the following example. {@ocaml[ let%server (s : int Eliom_shared.React.S.t), (f : (?step:React.step -> int -> unit) Eliom_shared.Value.t) = Eliom_shared.React.S.create 0 let%client incr_s () = let v = Eliom_shared.React.S.value ~%s in ~%f (v + 1) let%shared msg_of_int i = Printf.sprintf "value is %d" i let s_as_string () : string Eliom_shared.React.S.t = Eliom_shared.React.S.map [%shared msg_of_int] s ]} {!module Eliom_shared.React.S} implements an interface very similar to plain [React.S]. In the example, we create a signal [s] via [create], which also gives us the function [f] for updating it. [f] can {e only} be called on the client side; calling it on the server raises an exception. The client-side function [incr_s] gets the current value of [s] and uses [f] to increase the value by 1. Note that we use injections [~%] to pass [s] and [f] to the client. Similarly to plain [React], we can use [Eliom_shared.React.S.map] to derive new signals by applying functions on previous signals. The difference is that we need to use a function (in the example, [msg_of_int]) implemented on both sides ([let%shared]). We use [[%shared msg_of_int]] to denote the combination of the two implementations, rather than the server-side implementation. (The example would also work with an anonymous function inside [[%shared ...]]: [[%shared (fun i -> Printf.sprintf "value is %d" i)]]). Since the signals do not get updated on the server, all server-side computation is one-off. For example, the server-side [msg_of_int] will be called only once. On the client, updates happen just like for plain [React]. {1 HTML and SVG content} The server-side module {!Eliom_content.Html.R} enables constructing HTML elements that get updated automatically based on {!Eliom_shared.React} signals. Continuing our example, we can use the signal [s_as_string ()] as follows: {@ocaml[ let%server node () = Eliom_content.Html.R.txt (s_as_string ()) ]} [node ()] can be used similarly to any node produced by {!Eliom_content.Html.D} or {{!Eliom_content.Html.F}F}: {@ocaml[ (* ... *) let () = Shared_reactive_app.register ~service:main_service @@ fun () () -> Lwt.return @@ Eliom_tools.F.html ~title:"shared_reactive" ~css:[["css";"shared_reactive.css"]] Eliom_content.Html.(F.body [ F.h2 [F.txt "Welcome from Eliom's distillery!"]; node (); F.p ~a:[F.a_onclick [%client fun _ -> incr_s ()]] [F.txt "incr s"]; ]) ]} {!Eliom_content.Svg.R} operates in a similar fashion, allowing for shared reactive graphics. {1 ReactiveData} Just like {!Eliom_shared.React} is the shared counterpart of [React], {!Eliom_shared.ReactiveData} is the shared counterpart of {{:https://github.com/ocsigen/reactiveData}ReactiveData}. We provide an example. {@ocaml[ let (l : int Eliom_shared.ReactiveData.RList.t), (h : int Eliom_shared.ReactiveData.RList.handle) = Eliom_shared.ReactiveData.RList.create [] let%client cons_to_l () = Eliom_shared.ReactiveData.RList.cons 1 ~%h ]} We use [create] to produce a shared reactive list [l] of integers. We also obtain a {e handle} that allows us to manipulate the list on the client, e.g., by adding elements as per the function [cons_to_l]. [Eliom_shared.ReactiveData.RList.map] applies a given shared function to every element of a shared reactive list (including new elements as they are produced), producing a new shared reactive list: {@ocaml[ let l_nodes () = Eliom_shared.ReactiveData.RList.map [%shared fun i -> Eliom_content.Html.D.txt (Printf.sprintf "[%d]" i) ] l ]} Shared reactive lists can (and need to) be used wherever {!Eliom_content.Html.R} expects lists, e.g., we can build a [
] as follows: {@ocaml[ let l_div () = Eliom_content.Html.R.div (l_nodes ()) ]} Adding a new element via [cons_to_l] does not rebuild the whole [l_div ()], but only adds a new child. Similarly, in the case where existing nodes are updated, only the modified ones are re-rendered after every update operation. {1 Links} - {{:http://erratique.ch/software/react} React} - {{:https://github.com/ocsigen/reactiveData}ReactiveData} - {{!page-"ppx-syntax"}Our PPX syntax extension}, which is crucial for enabling the discussed paradigm eliom-12.1.0/doc/clientserver-services.mld000066400000000000000000000064431521623100200204700ustar00rootroot00000000000000{0 Client-side services} Starting with Eliom 6.0, services can have client-side handlers. Registering client-side handlers for services allows us to perform most of the operations (e.g., building the user interface) locally, and only call the server for operations that are by necessity non-local (e.g., retrieving database data). Client-side service implementation is required for building mobile applications with Eliom, but can also be used to change page faster within a Web application. In this manual chapter, we focus on the client-side service mechanics and do not go into the details of setting up a complete mobile application. The complete setup needed is discussed in the {{!page-"mobile-apps"}mobile applications chapter} of this manual. {1 Service creation and registering} As described in the chapter {{!page-"server-services"}on server-side services}, services are first created via {!Eliom_service.create} and similar functions, and subsequently registered. The first step (creation) can only happen on the server; allowing the opposite would permit "creating" on the client services that are not actually provided by the server. To allow client-side service manipulation, services can be injected, as demonstrated by the following example: {@ocaml[ let%server s = Eliom_service.create ~meth:(Eliom_service.Get Eliom_parameter.unit) ~path:(Eliom_service.Path ["content"]) () let%client s = ~%s ]} The above style makes the service available under the same identifier (here [s]) on both sides. Subsequently, the service can be registered on both sides with the same {!Eliom_registration} APIs. For example: {@ocaml[ let%shared () = Eliom_registration.Html.register s (fun () () -> Lwt.return (Eliom_tools.F.html ~title:"hybrid" Html.F.(body [ h1 [txt "Salut !"]; ]))) ]} The types of the handlers are compatible between the two sides. For instance, {!Eliom_registration.Html} expects a function that produces a [Html_types.html Eliom_content.Html.D.elt Lwt.t], for {!Eliom_registration.Action} we must return [unit Lwt.t], and so on. The behavior of all services is compatible to the extent possible, e.g., actions perform a reload after performing their side effect. For application services, the {!Eliom_registration.App} functor needs to be called just like on the server. The signatures are compatible between the two sides, and therefore the functor invocation can happen in a shared section. A service registered as above can be called via links (e.g., [Eliom_content.Html.D.a]) and forms just like a standard server-only service. Eliom detects the existence of a client-side implementation and calls that instead of performing a request to the server. If no client-side implementation exists, a standard server request is performed, which may cause problems in the context of mobile applications. {1 Service routing} Service routing is the operation of choosing a service based on a URL and a collection of POST parameters. This is clearly a key operation on the server, allowing Eliom to respond on different paths. Routing has also become available on the client via the function {!Eliom_client.change_page_uri}. This function can be used for example in order to open a link inside a mobile application without launching the Web browser. eliom-12.1.0/doc/clientserver-wrapping.mld000066400000000000000000000102521521623100200204650ustar00rootroot00000000000000{0:wrapping Wrapping} Reading this chapter is not mandatory for writing Eliom applications. However, it is worth reading if you are planning to hack on the Eliom codebase. {1 Basics} The server side of Eliom can communicate to the client other kinds of data than the raw XML contents of the pages. The wrapper mechanism is used to allow the browser side to access to the contents of variables declared on server side. For instance when we write {@ocaml[ ignore [%client (Dom_html.window##alert(Js.string ~%text) : unit)] ]} the contents of the [text] variable is sent along the page for the client code to access it. Server side, when {@ocaml[ [%client (Dom_html.window##alert(Js.string ~%text) : unit)] ]} is executed, the variable [text] is registered into a table and an id is associated to it. This table will contain all the data references by variables annotated with [~%] in a page, and will be sent marshalled to the client. On client side the id will be used to retrieve [text]. Since all data are sent in one table, if a variable is referenced multiples times, it will be sent only once, and sharing will be preserved: {@ocaml[let a = ref 0 in let b = (1,a) in ignore [%client ((~%a := 42; Dom_html.window##alert(snd ~%b |> string_of_int |> Js.string)) : unit) ] ]} This code will display 42. After being sent, the client and server side values are distinct: the server side version of [a] won't be modified by the client side affectation and conversely the client side value won't change if [a] is changed later on server side. {1 Special types} {2 Custom wrappers} Usually, client and server side values are represented the same way, and it is sufficient to only copy their content ( marshalled ) to the client. But certain types can't be transmitted this easily: for instance, services. Those values must be transformed before marshalling: We need for this to use custom wrappers. This wrapping mechanism is defined in {!Eliom_wrap}. Before sending, the values goes thought {!Eliom_wrap.wrap} which transform marked values. A value marked is a value which have as its last field a value of type {!Eliom_wrap.wrapper}. For instance {@ocaml[type marked_tupple = int * ... * marked_tupple Eliom_wrap.wrapper type marked_record = { f1 : int; ... fn : marked_record Eliom_wrap.wrapper } ]} but not {@ocaml[type not_marked_tupple = int * ... * marked_tupple Eliom_wrap.wrapper * float type not_marked_tupple = int * ... * (int * marked_tupple Eliom_wrap.wrapper) type not_marked_tupple = int * ... * marked_tupple Eliom_wrap.wrapper list type not_marked_record = { f1 : int; ... fn : marked_record Eliom_wrap.wrapper; fk : float; }]} A wrapper is created by the {!Eliom_wrap.create_wrapper} function. It takes a function as parameter which will be called to transform the value during the wrapping. There is also a special wrapper {!Eliom_wrap.empty_wrapper} which does nothing. It is useful to stop calling the wrapper on a value: If there is still a wrapper in a value after its transformation, it will be called another time, potentially leading to an infinite loop. For instance {@ocaml[type v = Fun of unit -> int | Value of int type wrapped_type = v * wrapped_type Eliom_wrap.wrapper let wrapper = let wrap = function | Value i,wrapper -> Value i, Eliom_wrap.empty_wrapper | Fun f,wrapper -> Value (f ()), Eliom_wrap.empty_wrapper in Eliom_wrap.create_wrapper f let v = ( Fun (fun () -> 1), wrapper ) let (v', empty_wrapper) = Eliom_wrap.wrap v]} At that time [v'] will be [Value 1]. Notice that {!Eliom_wrap.create_wrapper} does not enforce the output type of the wrapping function to be the same as the input type: Eliom_wrap is to be use with much caution! Do not use it if you don't understand how it works, it may lead to unpredictable segmentation faults and corrupted memory. {1 Eliom types with predefined custom wrappers} The Eliom types that are marked are: - {!Eliom_service.t} transformed to {!Eliom_service.t} (but the client side representation) - {!Eliom_comet.Channel.t} transformed to {!Lwt_stream.t} - {!Eliom_react.Up.t} transformed to ['a -> unit] - {!Eliom_react.Down.t} transformed to ['a React.E.t] - {!Eliom_bus.t} transformed to {!Eliom_bus.t} eliom-12.1.0/doc/config.mld000066400000000000000000000401401521623100200153770ustar00rootroot00000000000000 {0 Compiling and configuring Eliom modules} This chapter explains how to compile Eliom module, how to set Eliom's options in the configuration file, and how to define options for your Eliom modules. See the Ocsigen server documentation for more information about the {{!/ocsigenserver/page-config}server's configuration}. {1:misc Using Eliom modules with Ocsigen server} {3 Compiling Eliom modules} If your application is server-side only, it is easy to compile an Eliom module (here [example.ml]) with the following command: {v ocamlfind ocamlc -thread -package eliom.server -c example.ml v} or, in a shortest way: {v eliomc -c example.ml v} {3 Configuration file} To run an Ocsigen server with an Eliom module [example.cmo], add the following lines to Ocsigen's configuration file ({%wodoc:span class="code"%}/etc/ocsigen/ocsigen.conf{%wodoc:end%} most of the times): {v            v} The bloc surrounded by the [] tag creates a sub-site on your host, in directory [examples]. The [] tag is optional if you want to register your services at the root of the server path tree. The [] tag defines an Eliom module to be loaded (dynamically while starting Ocsigen server) for this subsite. {3 Running Ocsigen server} Run Ocsigen server, by typing the command {v ocsigenserver v} or, if your configuration file is not in the default location: {v ocsigenserver -c //// v} You should see the page corresponding to service ["coucou"] in site [examples/] at url{%html:
%} {%wodoc:span class="code"%}http://{e your_server}/examples/coucou{%wodoc:end%}.{%html:
%} {%wodoc:div class="concept"%}{b Debugging} {b Tip:} Add the option [] in the configuration file during the development process of your application. Thus, Ocsigen server will display the exceptions raised during the generation of a page in error pages. {b Tip:} To debug your programs, add the option [-v] or [-V] of the [ocsigen] command to have verbose information on what succeeds during request. Also think to have look in the log files when something goes wrong!{%wodoc:end%} {2 Several Eliom modules for one site} If your site consists of several modules, you can load them consecutively from the configuration file using {%wodoc:span class="code"%} (same syntax as {%wodoc:span class="code"%}, the difference being that {%wodoc:span class="code"%} does not generate any page). In that case, only the position of the {%wodoc:span class="code"%} tag will be taken into account for generating the page using Eliom. Note that there can be only one {%wodoc:span class="code"%} tag for each {%wodoc:span class="code"%} (or {%wodoc:span class="code"%}). {2 Using findlib with Eliom modules} If your module has a findlib {%wodoc:span class="code"%}META{%wodoc:end%} file, it is also possible (and recommended!) to do: {v            v} {2:reload Updating sites without shutting down the server} Ocsigen server has a feature that allows reloading the configuration without shutting down the server (see section * *). This can be used to reload Eliom modules without closing volatile sessions. To do that use {%wodoc:span class="code"%}/etc/init.d/ocsigen reload{%wodoc:end%} for most of the distributions, or do it manually using: {v echo reload > /var/run/ocsigen_command v} Only modules loaded inside {%wodoc:span class="code"%}, {%wodoc:span class="code"%} or {%wodoc:span class="code"%} will be reloaded. Module loaded using {%wodoc:span class="code"%} will not. Have a look at the logs to see if all went well during the reload. If something went wrong, old services may still be reachable. Warning: - Services created with the old modules or URLs that have not been masked by new ones will still reachable after the update! - During the reload, some information of the configuration file will not be re-read (for example port numbers, user and group, etc.). {1 Interacting with Ocsigen server from Eliom programs} {2 Defining an exception handler for the whole site} When an exception is raised during the generation of a page, or when the page has not been found or has wrong parameters, an HTTP error 500 or 404 is sent to the client. You may want to catch these exceptions to print your own error page. Do this using {!Eliom_service.set_exn_handler}. Here is the handler used by the tutorial: {@ocaml[let _ = Eliom_service.set_exn_handler (fun e -> match e with | Eliom_common.Eliom_404 -> Eliom_registration.Html.send ~code:404 (html (head (title (txt "")) []) (body [h1 [txt "Eliom tutorial"]; p [txt "Page not found"]])) | e -> fail e) ]} {2 Giving configuration options to your sites} You can add your own options in the configuration file for your Web site. For example: {@ocaml[ ... ]} Use {!Eliom_config.get_config} during the initialization of your module to get the data between {%wodoc:span class="code"%} and {%wodoc:span class="code"%}. {%wodoc:div class="wip"%} Warning: parsing these data is very basic for now. That feature will be improved in the future. {%wodoc:end%} {2:static_linking Static linking of Eliom modules} From version 1.2, it is possible to link extensions and Eliom modules {{!/ocsigenserver/page-staticlink}statically}. But this is not straightforward. For Eliom modules, service registration and options setting must be delayed until the configuration file is read. To create a statically linkable Eliom module, use the function {!Eliom_service.register_eliom_module}. It takes as parameters the name of the module and the initialization function, that will be called when the module is initialized in the configuration file. That function will register services (and possibly call {!Eliom_config.get_config} if the module has configuration options). {%wodoc:div class="wip"%}How to improve this and this easier to use?{%wodoc:end%} To initialize the module from the configuration file, use the syntax: {v ... v} (or {%wodoc:span class="code"%} ... {%wodoc:end%}) which is equivalent to: {v ... v} (or {%wodoc:span class="code"%} ... {%wodoc:end%}) with the exception that it does not load the module using {%wodoc:span class="code"%}Dynlink{%wodoc:end%}, but calls the initialization function.{%html:
%} You can use functions like {!Eliom_state.create_volatile_table} that needs some information about the site (here, volatile tables are associated to a site), only during a request or during the initialisation phase of the server. If you want to use that kind of function before the initialisation phase, for example if your module is linked statically with the server, you must call these function using the function {!Eliom_service.register_eliom_module}. (One solution is to use a lazy value to delay the creation of the table, and force that value during the registration function). {2:extensions Advanced use: create an extension for the server that access Eliom's data} If you want an Ocsigen extension with access to Eliom's data (for example if you want an extension that will register some services), you can use the function {!Eliom_extension.register_eliom_extension} to register the function that will generate the {!Ocsigen_extensions.answer}. {%wodoc:div class="wip"%}Add more details about this{%wodoc:end%} {1:config Global configuration options} Here are Eliom's options you can use in configuration files. {2 Timeouts} {%wodoc:div class="wip"%}Revoir les timeouts pour client processes et groupes{%wodoc:end%} Timeouts for states (and other states) can be set either inside tag [] (default value for all sites), or inside a [] tag (default for one site). Timeouts can also be modified programmatically using functions like [Eliom_state.set_global_volatile_timeout], but by default these functions will not override configuration files. (see module {!Eliom_state} for other functions). Thus, a Web site can set its own defaults and the user can still override them from the configuration file. If you want to set a timeout programmatically even if it has been modified in a configuration file, use the optional parameter [~override_configfile:true]. Timeouts can be set either for all scopes hierarchies, for one precise hierarchy, or for the default scope hierarchy. To do that programmatically, use the optional parameter [~scope]. To do that in configuration file, use the optional attribute [hierarchyname] (where an empty string value means default hierarchy). If this attribute is absent, the timeout will affect all states for which no other default has been set. The [hierarchyname] attribute exists only inside an [] tag (and not inside []). The level attribute can take values [session] (browser) or [clientprocess] (tab). - [] The default timeout for volatile (in memory) states (value in seconds). [value="infinity"] means that the state will never finish. Note that each eliom module may set its own default, that will override this one. - [] Idem for persistent state data - [] Like [], but for in memory data states only (not service states). - [] Like [], but for service states only (not in memory data states). {2 Garbage collector of states and services} These options can appear inside tag []. For now, it cannot be set for each site independently (tell us if you need that). - [] Time between two garbage collections of sessions, in seconds (default 3600). The value ["infinity"] means no GC of session. - [] Time between two garbage collections of persistent sessions, in seconds (default 86400.). The value ["infinity"] means no GC of session. - [] Like [], but for service sessions only - [] Like [], but for "in memory data" sessions only {2 Limiting the number of sessions or coservices} To prevent from denial of service, Eliom limits the number of sessions and the number of dynamic coservices. Without these limitations, it would be possible for an attacker to open repeatedly lots of sessions, or creating new services (for example CSRF safe coservices can create lots of coservices when you reload repeatedly a page). When the limit is reached, it is still possible to open new sessions or create new services, but the oldest session or service will disappear (the one that has not been used for the longest time). {3 Limiting sessions} First of all, there is a limitation of the number of sessions in a session group. The typical use of this is when an user opens several sessions from several computers. All the sessions belong to the same group (the group name is usually the user name). The limit is usually small (5 sessions per group by default). This limit is implemented for all kinds of sessions (service session, volatile and persistent data sessions). For persistent sessions, the implementation is not very efficient for very large limits. It is highly recommended to use session groups when possible. If you can't use session groups, the number of sessions is limitated by sub network for volatile sessions (service sessions and data sessions). The limitation is larger (default 1 million). The limit must be large enough, for example if the server is behind a reverse proxy, all incoming requests will come from the same IP address. Limiting by sub network instead of by IP address prevents attacks even if the attacker has a whole sub network available. The default mask for sub networks is [/16] for IPv4 and [/56] for IPv6. Some figures: If 1 session takes 1000 bytes (data + tables etc), 1 million sessions take 1 GB. If somebody opens 1000 sessions per second, then it will take 1000 s (16 minutes) to reach 1000000. It means that regular users will have their sessions closed after 16 minutes of inactivity if they share their sub network with someone doing an attack (or if the server is behind a proxy). For persistent sessions, there is no limitation per sub network for now. 1 billion sessions take 1 TB. If somebody opens 1000 sessions per second, then it will take 1 million s (16000 minutes = 266 h = 11 days) to reach 1TB. {3 Limiting services} The number of anonymous coservices is limited by session or by sub network if the service is registered in the global table. Default values: 1000 for one session, and 500000 for one subnet. Note that there is no limitation of named coservices or regular services. It is not a good practice to allow the creation of too much services of this kinds dynamically. {3 How to set limits} The limits and the subnet mask can be set programmatically by each module (for example to adapt the values to the size of session data) or in the configuration file (for example to adapt the values to the size of memory or network configuration) (see module {!Eliom_state}). By default, functions like [Eliom_state.set_default_max_volatile_sessions_per_group] will not override a value set in the configuration file (but if you use [~override_configfile:true]). Thus, a Web site can set its own defaults and the user can still override them from the configuration file. The configuration file options can be set either inside the tag [] (global configuration), or inside the [] tag (configuration for each site). But the limits always are for one site (that is: a global limit value of 10 means 10 for each Eliom site). The syntax is: - [] - [] - [] - [] - [] - [] - [] - [] - [] - [] - [] {2 Setting HTML content type} You can set the default content type for html pages, either globally or for each site independently. Example: [] {2 Ignoring some GET or POST parameters} Some external tools add parameters in URLs (for example UTM parameters). You can ask Eliom to ignore them. For example: [] Same for [ignoredpostparams]. Ignored parameters can be accessed using functions {!Eliom_request_info.get_ignored_get_params} and {!Eliom_request_info.get_ignored_post_params}. {1 Per site configuration options} {2 Disabling XHR-links} By default, all links ([a], [form]) of an Eliom application are realized by Eliom's [change_page] magic (unless their creation is augmented with [~xhr:false]). In contrast, you may add the attribute [xhr-links] into the content of the configuration of a Eliom module: {v ... v} Then, all links that are not explicitely specfied as [~xhr:true] are realized by their default HTTP behaviour. You can also set this value through the function {!Eliom_config.set_default_links_xhr}. eliom-12.1.0/doc/dune000066400000000000000000000003621521623100200143140ustar00rootroot00000000000000; Eliom manual pages (odoc .mld), compiled in place with the API by ; odoc-driver and installed with the package, so they appear on ocaml.org ; and resolve {{!page-X}} / {!Module} references against the API. (documentation (package eliom)) eliom-12.1.0/doc/eliom-language.mld000066400000000000000000000242461521623100200170310ustar00rootroot00000000000000{0 Eliom -- the language extensions} In Eliom it possible to define the functionality of the server and the client program at in a single source file. The server program will constantly run on the Web server. The client program will run independently in each browser tab that visits the Web site. The latter is started with the initial request in a browser tab and keeps running while navigating within the Web site, across the usage of links, forms, history, and {{!Eliom_client.change_page}page changes}. Eliom provides three language extensions to OCaml to provide an integrated development of client/server-programs, and to deal with the mentioned asymetricity between the client- and server-program: The partitioning of the program into client-, and server-sections, the direct usage of server-side toplevel values in client sections, and the declaration and handling of client values within the code for the server. It is advisable to read the section about {{!page-"workflow-compilation".compilation}compiling eliom applications} first. For an outline of the implementation of those features, please refer to the chapter on {{!page-"workflow-compilation".implementation}compiling eliom applications}. {1:sections Sections (partitioning into client- and server-side)} The source code of an Eliom module can be partitioned on its top-level into several {e sections} to specify which code is to be compiled for and run in the server program, in the client program, or in both. A common OCaml module is comprised of a sequence of {e structure items}, i.e. top level declarations of variables, types, modules etc. An Eliom module consists of a sequence of such structure items and client, server, and shared {e sections}. The section declarations in turn may contain any number and kind of structure items. Code outside of those sections is compiled for the server side only. The sections (the latter contain code to be compiled and run on both sides) are introduced by the following syntax and may occur in any number or ordering: {@ocaml[ {client{ (* client only structure items *) }} ... {server{ (* server only structure item *) }} ... {shared{ (* structure items for server and client *) }} (* Code outside of section is compiled only to the server program *) ... ]} Declarations in the shared section are compiled separately for both the client {e and} for the server program. They may refer only to declarations which are available on the client-side {e and} on the server side. Note well, that they will refer possibly to different implementations: {@ocaml[ {server{ let output str = print_endline str (*1*) {client{ let output str = Dom_html.window##alert(str) (*2*) }} {shared{ let () = output "GUGA!" (* output refers to (*1*) when compiled for the server, to (*2*) when compiled to the client! *) }} ]} {1 Semantics} Sections as-it act as a filter on the original source code during compilation: Only the structure items on the top-level, the server-section, and the shared-sections are compiled to the server program (or bytecode object files) by [eliomc]. And only the structure items in client- and shared-sections are compiled to the client program (or bytecode object files) by [js_of_eliom]. All top-level expressions are evaluated {e early} while launching the program. In the server program they are thus evaluated while launching the server. {e No request information is available at that time}. In the client program, top-level expressions are evaluated early in the initialization phase of the client process, particulary {e before the DOM is in place}. This means, that you cannot do any DOM manipulation or access HTML-elements with {{!Eliom_content.Html.D}DOM semantics}. However, you can postpone that by {!Eliom_client.onload}. {1:injections Injections (in the client-section)} Eliom permits the direct usage of top-level server-side variables, and also of arbitrary expressions, in the client sections. Such a usage is called {e injection}. It is also supported inside a {{:#sharedinjection}shared section}. Assuming you have a top-level variable [v] defined on the server, its injection in the client section is introduced by prefixing it with a percent-sign [%v]. It then holds the value of the server-side variable [v] {e at the time of the initial request of the client process}. {@ocaml[ {server{ let service = ... (* Server-side declaration of a service. *) }} {client{ let f () = Eliom_client.change_page ~service:%service () (* Refer to the server-side variable within the client section! *) }} ]} It is also possible to inject an arbitrary server-side expression [exp] in a client section. The injection is introduced by prefixing it with a percent-sign and parentheses, [%(exp)]. Whenever an initial request of a client process is handled from the server, the expression [exp] is evaluated. The injection then holds the resulting value in that specific client process. {@ocaml[ {client{ let () = Eliom_lib.alert "Time of the first request on the server: %d" %(Unix.gettimeofday ()) }} ]} Note well that the value of an injection is {e not updated} when the injected value on the server changes. However, you may inject reactive signals (cf. {!val Eliom_react.S.Down.of_react}) to achieve the behavior of a client side values which updates alongside with a server correspondent. {2:sharedinjection In a shared section} Injections are also supported in the shared sections of an Eliom source file. When compiling the shared section to the client, the semantics is as described. However, when compiling an injection of a variable to the server program, it becomes just a new name of that variable. I.e. it will actually reflect changes made to the original variable. However, the same scoping rules hold for injections into the client- and shared-sections. {@ocaml[ {server{ let v = "server" (*1*) }} {shared{ let v = "shared" (*2*) let () = assert (%v = "server") (* %v refers to (*1*), also when compiling to the server, i.e. when (*2*) is in scope as v here *) }} ]} {1:clientvalues Client values (in the server-section)} The notion {e server-side client values} allows it to declare and deal with client values within the server-section (and is also supported in the {{:#sharedclientvalues}shared section}). A client-value may be declared in the server section as {@ocaml[ ... {typ{ exp }} ... ]} where [exp] is an expression on the client side, which means it is compiled only for the client program, and may make use of any libraries available on the client, and where [exp] has type [typ] on the client side. The hole client value then has type [typ Eliom_client_value.t]. A value of type [typ Eliom_client_value.t] is {e abstract on the server} (cf. {!type Eliom_client_value.t} (server)). But once it is sent to the client it becomes the value to which the expression [exp] evaluated on the client side (cf. {!type Eliom_client_value.t} (client)). A client value expression is an arbitrary OCaml expression, but may additionaly contain {e injections of server variables}, (cf. {{:#injections}Injections into the client section}). A variable [v] in the scope of a client-value expression can be injected by prefixing it with a percent sign, [%v]. A server-side expression [exp] can be injected by [%(exp)]. Note well, that the injection of a server side client-value [v] of type [typ Eliom_client_value.t] results in a value [%v] of type [typ]. Thus, the value of the client value becomes concrete. The other way to make a client value concrete is to send it to the client, e.g. by using it in the HTML tree sent from the server in an Eliom application-service, or by sending it as-it in an Ocaml-service. For convenience, the indication of the type of a client value may be ommitted if it is derivible from its {e usage} in the server code: {@ocaml[ {server{ ... let onclick = {{ fun ev -> Eliom_lib.alert "ohyeah!" }} in Eliom.content.Html.F.(div ~a:[a_onclick onclick] [txt "click me!"]) ... ]} Here, the function [a_onclick] has type [(#Dom_html.mouseEvent Js.t -> unit) -> [> `OnClick ] Eliom_content.Html.attrib]; this determines the type of the client value [onclick] sufficiently. {2:clientvaluesemantic Semantics} The point in time of the evaluation of the expression of a client value (which also includes the occurrence of its side effects) depends on the context of the creation of the client value on the server. In Eliom, global client values are distinguished from request client values. Client values created while launching the server, i.e. while evaluating the top-level declarations of the server program, are termed {e global client values}. The expressions of global client values are evaluated early in the initialization phase of the client process. Consider the following example to get an intuition of the order of evaluation of global client values; it will print the numbers 1 to 5 to the browser's console consecutively. {@ocaml[ {client{ let () = Eliom_lib.debug "1" }} {server{ let f n = ignore {unit { Eliom_lib.debug "%d" %n }} let () = ignore {unit{ Eliom_lib.debug "2" }}; ignore {unit{ f 3 }}; ignore {unit{ f 4 }} }} {client{ let () = Eliom_lib.debug "5" }} ]} Client values which are evaluated during the processing of a request are termed {e request client values}. The expressions of request client values are evaluated after receiving the corresponding request on the client in the order of their occurrence on the server. In requests which change the content of the page withing the application (originating from a service of the {!module Eliom_registration.App}), the expressions are evaluated {e after the content has changed}. That way they may refer to the new DOM. If a client value is created outside of the initialization of the server program and also outside of the processing of a request, the exception {!Eliom_client_value.Client_value_creation_invalid_context} is raised. {2:sharedclientvalues In a shared section} Client values are also supported in the shared-section of an Eliom program. They are then just directly compiled into the surrounding code. However, the same scoping rules apply to them: {@ocaml[ {server{ let v = "server" (*1*) }} {shared{ let v = "shared" (*2*) let () = {unit{ assert (%v = "server") (* Although v refers to (*2*) here *) }} ]} eliom-12.1.0/doc/index.mld000066400000000000000000000061071521623100200152460ustar00rootroot00000000000000{0 Eliom} Eliom is a framework for building client/server Web and mobile applications in OCaml. It can be used both as a traditional server-side Web framework or to implement complex client-server applications. Eliom transforms OCaml into a multi-tier language, making it possible to implement both the server and client parts of a Web and mobile app as a single program. This simplifies a lot the communication between server and client. Applications can run on any Web browser or mobile device (iOS, Android), saving from the need to develop one version for each platform. Eliom has support for reactive pages (generated on server or client), advanced session mechanism, server to client communication, continuation based Web programming, etc. Eliom is part of the {{:http://ocsigen.org}Ocsigen project}. {1 Installation instructions} {[ opam install eliom ]} {1 Getting started} Defining a service on path [/foo], taking any GET parameters: {@ocaml[ let myservice = Eliom_service.create ~path:(Eliom_service.Path ["foo"]) ~meth:(Eliom_service.Get (Eliom_parameter.any)) () let () = Eliom_registration.Html.register ~service:myservice (fun get_params () -> Lwt.return Eliom_content.Html.F.(html (head (title (txt ""))) (body [h1 [txt "Hello"]]))) ]} Inserting a link towards that service, with parameters: {@ocaml[ Eliom_content.Html.D.a ~service:myservice [txt "Home"] [("param1", "v1"); ("param2", "v2")] ]} Event handlers are written in OCaml: {@ocaml[ div ~a:[a_onclick [%client (fun ev -> ... )]] [ ... ] ]} The client-side and server sides are written as a single program: {@ocaml[ let%server a = ... (* code for the server part of the application *) let%client b = ... (* code for the client part of the application *) let%shared c = ... (* code that will be included in both parts *) ]} Using a server-side value in client-side code: {@ocaml[ let%server a = ... let%client f () = print_endline ~%a ; (* print in browser console *) ... ]} Calling a server function from the client program: {@ocaml[ let%rpc f (x : int) : string Lwt.t = ... (* server-side code *) let%client () = let%lwt r = f 4 in ... ]} Saving session data on the server using Eliom references: {@ocaml[ let%server r = Eliom_reference.eref ~scope:Eliom_common.default_session_scope 0 let%server f () = let%lwt v = Eliom_reference.get r in Eliom_reference.set r (v + 1); ... ]} Where scope can be: - [Eliom_common.default_session_scope] (different value for each browser), - [Eliom_common.default_process_scope] (different value for each tab), - [Eliom_common.default_group_scope] (different value for each user), - [Eliom_common.site_scope] (value for the whole site), - [Eliom_common.global_scope] (global value for the whole server). Eliom references are persistant if you add optional parameter [~persistent] to function [Eliom_reference.eref]. {1 Learning Eliom} More documentation {{:https://ocsigen.org/tuto/latest/manual/basics}here}. Write your first Web and mobile application with Eliom using {{!/ocsigen-start/page-index}Ocsigen Start}. eliom-12.1.0/doc/index.wiki000066400000000000000000000006321521623100200154320ustar00rootroot00000000000000= Eliom -- API reference [[wiki:mindmap.pdf|A mindmap to get an overview on the most important modules of Eliom]] <<| in /var/www/data/site-ocsimore/eliom >> <> <> <> <<|a_api subproject="ocamlbuild" text="The ocamlbuild plugin" | intro >> eliom-12.1.0/doc/intro.mld000066400000000000000000000154521521623100200152750ustar00rootroot00000000000000{0 Introduction} {e This document is the Eliom manual, where you will find an detailed explanation of all Eliom concepts. If you want to learn how to program with Ocsigen, please read the {{:https://ocsigen.org/tuto/}Ocsigen tutorial} first, and especially {{:https://ocsigen.org/tuto/basics.html}this overview of all concepts} or the {{:https://ocsigen.org/tuto/basics-server.html}server-side programming guide}.} Eliom is a framework for developing Web and mobile applications. It can be used to implement either basic server-side Web sites, or advanced client-server applications accessible through a Web browser. From Eliom 6.0, it is also possible to implement, with a single code base, {b multi-platform applications} that will run on a {b Web browser} or a {b mobile} device (iOS, Android~ ...). The goal of Eliom (and other projects of the Ocsigen framework) is to propose a new programming style that matches the needs of modern Web applications better than the usual languages (most of which were designed years ago, for a much more static Web). It simplifies the development of complex applications by: - providing high level expressive concepts that address the needs of Web developers. This allows programming complex behaviour in very few lines of code. - making it possible to write a client-server application as a single program, using a multi-tier extension of OCaml. - helping the programmer to write safe and bug-free applications. To improve security, it takes charge of many security issues itself. To reduce the number of bugs, it uses the very powerful type system of the OCaml language to check many properties of the application at compile time: well-formedness of HTML pages, types of page parameters, absence of broken links, etc. Eliom does not restrict the possibilities offered by Web technology. For example, the programmer can choose the exact URL and parameter names she wants. Eliom also produces clean HTML code that can be parsed by search engines, even for client/server applications. Eliom is not an entirely new language, but an extension of OCaml. An Eliom application can use any existing OCaml library. Eliom Web sites are written as OCaml modules (producing [cma] or [cmxs] files) that are loaded as plugins into the Ocsigen server. {1 Client/server applications} Eliom applications are applications distributed between a server and a browser. An instance of the application is running in a browser tab, and can communicate with the server. The entire application is written in OCaml, as a single program, with special syntax to distinguish between server-side and client-side code. Client-side code is compiled to JavaScript, using the Js~_of~_ocaml compiler, to be executed by the browser. It is possible to refer on the client side to values defined in server-side code. Communication between the server and the client is handled by Eliom automatically. The use of the same language on both sides makes it very easy to exchange data. It is possible to have a client side Eliom program and maintain the traditional Web interaction (with URLs, links, forms, bookmarks, and the back button). When you click on a link, the client-side program {e does not stop!} Thus: - You can keep a state on client side for the whole duration of the visit. - You can set bookmarks corresponding to some states of the client-side program. - Part of the page can persist after loading a new page. - If you are listening to music or watching a video, playback does not stop when you change page! {{!page-"clientserver-applications"}More information...} {1 Services} Pages are generated by {e services}, which are a kind of functions that are called when the user clicks on a link, or sends data through a form in a Web page. Services can be associated to URLs. Eliom's {e service identification mechanism} (that is: the way Eliom chooses the service to be executed) is actually much more sophisticated. It is using many different criteria, like the URL, but also special parameters, the HTTP method used (GET or POST), parameter names, the session, etc. Services usually return HTML pages, but you can also choose to return files, redirections, actions (that is: just perform a side effect on the server and redraw the current page), applications, etc. Eliom services enable several ways of generating HTML. You can send HTML as raw text, as in most Web programming frameworks. However, it is preferable to check at compile time that every page generated respects the recommendations of the W3C. For this we are using the TyXML library. When defining a service, you must specify the names and types of the parameters it expects (through the URL or through the body of the HTTP request). Eliom automatically checks parameters and translates them to the right OCaml type (even for complex types like lists and sets). The OCaml compiler checks that the parameters you put in links have the right type (w.r.t. the service). It also checks that your forms correspond to the service to which they are associated. For example, if your form contains a checkbox, the service must expect a boolean value. Services can be created dynamically. For example, you can create new services that depend on previous interaction with the user (form data for example). This is equivalent to what is usually called {e continuation-based Web programming}. This enables for example implementing multiple-step forms very easily (like booking a plane ticket). {{!page-"server-services"}More information...} {1 Sessions and server-side state} Eliom has a very advanced session mechanism. Sessions are a common pattern in Web programming, allowing some data (a state) to be stored on the server for each browser. This is used for example to recognize connected users, or to store a shopping basket. This is usually implemented by recording a session identifier in a browser cookie. With Eliom, session data is stored in {e Eliom references}, which are a kind of reference whose value depends on the session (the session identifier sent in the cookie). But you can choose other {e scope} than "session" for Eliom references: - It is also possible to define references with scope {e "client process"}, which corresponds to a tab of your browser (if you are using client-side features). For example, if you are implementing a game, you can have several instances of the game in separate tabs. The score is stored on the server in an Eliom reference of scope "client process". - Eliom also provides the scope {e "session group"}. This allows for example grouping together all sessions belonging to the same user. For example, you can use this to share a shopping basket between several devices (e.g., the user's laptop and smartphone). - There is also a scope {e "request"} that can be used to store data during the generation of a page. {{!page-"server-state"}More information...} eliom-12.1.0/doc/misc.mld000066400000000000000000000132561521623100200150750ustar00rootroot00000000000000{0 Miscellaneous} {1:predefinedconstructs HTML widgets} {2 Images, CSS, Javascript} To include an image, simply use function {!Eliom_content.Html.D.img}: {@ocaml[img ~alt:"Ocsigen" ~src:(Eliom_content.Html.F.make_uri ~service:(Eliom_service.static_dir ()) ["images"; "ocsigen1024.jpg"]) () ]} The function {!Eliom_content.Html.D.make_uri} creates a relative URL string from current URL (see above) to the URL of the image (here in the static directory configured in the configuration file). To simplify the creation of [] tags for CSS or [