Moved tutorial to servant-examples/tutorial and include it in doc/index.rst
This commit is contained in:
parent
7bb393fe17
commit
1d4e3a1e5b
19 changed files with 13 additions and 545 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -25,4 +25,4 @@ Setup
|
|||
.stack-work
|
||||
shell.nix
|
||||
default.nix
|
||||
tutorial/_build
|
||||
doc/_build
|
||||
|
|
15
doc/conf.py
15
doc/conf.py
|
@ -37,7 +37,7 @@ templates_path = ['_templates']
|
|||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
source_suffix = ['.md', '.rst']
|
||||
source_suffix = ['.md', '.rst', '.lhs']
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
@ -47,8 +47,8 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'servant'
|
||||
copyright = u'2015-2016, Servant contributors'
|
||||
author = u'Servant contributors'
|
||||
copyright = u'2016, Servant Contributors'
|
||||
author = u'Servant Contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
@ -74,7 +74,7 @@ language = None
|
|||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
exclude_patterns = ['_build', 'venv']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
|
@ -108,7 +108,7 @@ todo_include_todos = False
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
# html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
@ -222,8 +222,8 @@ latex_elements = {
|
|||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'servant.tex', u'servant Documentation',
|
||||
u'Servant contributors', 'manual'),
|
||||
(master_doc, 'servant.tex', u'servant Documentation',
|
||||
u'Servant Contributors', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
@ -285,4 +285,5 @@ texinfo_documents = [
|
|||
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
'.lhs': CommonMarkParser,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ Documentation table of contents
|
|||
-------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
README.md
|
||||
tutorial/index.rst
|
||||
CONTRIBUTING.md
|
||||
|
||||
|
|
1
doc/tutorial
Symbolic link
1
doc/tutorial
Symbolic link
|
@ -0,0 +1 @@
|
|||
../servant-examples/tutorial
|
|
@ -1,49 +0,0 @@
|
|||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
module T8 where
|
||||
|
||||
import Control.Monad.Trans.Except
|
||||
import Network.HTTP.Client (Manager, defaultManagerSettings,
|
||||
newManager)
|
||||
import Servant
|
||||
import Servant.Client
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
|
||||
import T3
|
||||
|
||||
position :: Int -- ^ value for "x"
|
||||
-> Int -- ^ value for "y"
|
||||
-> ExceptT ServantError IO Position
|
||||
|
||||
hello :: Maybe String -- ^ an optional value for "name"
|
||||
-> ExceptT ServantError IO HelloMessage
|
||||
|
||||
marketing :: ClientInfo -- ^ value for the request body
|
||||
-> ExceptT ServantError IO Email
|
||||
|
||||
position :<|> hello :<|> marketing = client api baseUrl manager
|
||||
|
||||
baseUrl :: BaseUrl
|
||||
baseUrl = BaseUrl Http "localhost" 8081 ""
|
||||
|
||||
{-# NOINLINE manager #-}
|
||||
manager :: Manager
|
||||
manager = unsafePerformIO $ newManager defaultManagerSettings
|
||||
|
||||
queries :: ExceptT ServantError IO (Position, HelloMessage, Email)
|
||||
queries = do
|
||||
pos <- position 10 10
|
||||
msg <- hello (Just "servant")
|
||||
em <- marketing (ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"])
|
||||
return (pos, msg, em)
|
||||
|
||||
run :: IO ()
|
||||
run = do
|
||||
res <- runExceptT queries
|
||||
case res of
|
||||
Left err -> putStrLn $ "Error: " ++ show err
|
||||
Right (pos, msg, em) -> do
|
||||
print pos
|
||||
print msg
|
||||
print em
|
|
@ -59,7 +59,7 @@ Tutorial
|
|||
---------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 1
|
||||
|
||||
api-type.lhs
|
||||
server.lhs
|
|
@ -1,4 +0,0 @@
|
|||
import T8
|
||||
|
||||
main :: IO ()
|
||||
main = run
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Tutorial - 9 - servant-jquery</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Books</h1>
|
||||
<input type="search" name="q" id="q" placeholder="Search author or book title..." />
|
||||
<div>
|
||||
<p>Results for <strong id="query">""</strong></p>
|
||||
<ul id="results">
|
||||
</ul>
|
||||
</div>
|
||||
<hr />
|
||||
<h1>Approximating π</h1>
|
||||
<p>Count: <span id="count">0</span></p>
|
||||
<p>Successes: <span id="successes">0</span></p>
|
||||
<p id="pi"></p>
|
||||
|
||||
<script type="text/javascript" src="/jq.js"></script>
|
||||
<script type="text/javascript" src="/api.js"></script>
|
||||
<script type="text/javascript" src="/ui.js"></script>
|
||||
|
||||
</body>
|
|
@ -1,61 +0,0 @@
|
|||
/* book search */
|
||||
function updateResults(data)
|
||||
{
|
||||
console.log(data);
|
||||
$('#results').html("");
|
||||
$('#query').text("\"" + data.query + "\"");
|
||||
for(var i = 0; i < data.results.length; i++)
|
||||
{
|
||||
$('#results').append(renderBook(data.results[i]));
|
||||
}
|
||||
}
|
||||
|
||||
function renderBook(book)
|
||||
{
|
||||
var li = '<li><strong>' + book.title + '</strong>, <i>'
|
||||
+ book.author + '</i> - ' + book.year + '</li>';
|
||||
return li;
|
||||
}
|
||||
|
||||
function searchBooks()
|
||||
{
|
||||
var q = $('#q').val();
|
||||
getBooks(q, updateResults, console.log)
|
||||
}
|
||||
|
||||
searchBooks();
|
||||
$('#q').keyup(function() {
|
||||
searchBooks();
|
||||
});
|
||||
|
||||
/* approximating pi */
|
||||
var count = 0;
|
||||
var successes = 0;
|
||||
|
||||
function f(data)
|
||||
{
|
||||
var x = data.x, y = data.y;
|
||||
if(x*x + y*y <= 1)
|
||||
{
|
||||
successes++;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
update('#count', count);
|
||||
update('#successes', successes);
|
||||
update('#pi', 4*successes/count);
|
||||
}
|
||||
|
||||
function update(id, val)
|
||||
{
|
||||
$(id).text(val);
|
||||
}
|
||||
|
||||
function refresh()
|
||||
{
|
||||
getPoint(f, console.log);
|
||||
}
|
||||
|
||||
window.setInterval(refresh, 200);
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import Network.Wai
|
||||
import Network.Wai.Handler.Warp
|
||||
import System.Environment
|
||||
|
||||
import qualified T1
|
||||
import qualified T10
|
||||
import qualified T2
|
||||
import qualified T3
|
||||
import qualified T4
|
||||
import qualified T5
|
||||
import qualified T6
|
||||
import qualified T7
|
||||
import qualified T9
|
||||
|
||||
app :: String -> (Application -> IO ()) -> IO ()
|
||||
app n f = case n of
|
||||
"1" -> f T1.app
|
||||
"2" -> f T2.app
|
||||
"3" -> f T3.app
|
||||
"4" -> f T4.app
|
||||
"5" -> f T5.app
|
||||
"6" -> f T6.app
|
||||
"7" -> f T7.app
|
||||
"8" -> f T3.app
|
||||
"9" -> T9.writeJSFiles >> f T9.app
|
||||
"10" -> f T10.app
|
||||
_ -> usage
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
args <- getArgs
|
||||
case args of
|
||||
[n] -> app n (run 8081)
|
||||
_ -> usage
|
||||
|
||||
usage :: IO ()
|
||||
usage = do
|
||||
putStrLn "Usage:\t tutorial N"
|
||||
putStrLn "\t\twhere N is the number of the example you want to run."
|
288
tutorial/conf.py
288
tutorial/conf.py
|
@ -1,288 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# servant documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Jan 22 12:22:48 2016.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
source_suffix = ['.md', '.rst', '.lhs']
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'servant'
|
||||
copyright = u'2016, Servant Contributors'
|
||||
author = u'Servant Contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build', 'venv']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
|
||||
#html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# Now only 'ja' uses this config value
|
||||
#html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
#html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'servantdoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'servant.tex', u'servant Documentation',
|
||||
u'Servant Contributors', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'servant', u'servant Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'servant', u'servant Documentation',
|
||||
author, 'servant', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
'.lhs': CommonMarkParser,
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
Servant tutorial
|
||||
================
|
||||
|
||||
This is an introductory tutorial to the current version of *servant*, which is **0.4**. Any comment or issue can be directed to [this website's issue tracker](http://github.com/haskell-servant/haskell-servant.github.io/issues).
|
||||
|
||||
Github
|
||||
-------
|
||||
|
||||
- the servant packages: [haskell-servant/servant](https://github.com/haskell-servant/servant)
|
||||
- the website (including this tutorial): [haskell-servant/haskell-servant.github.io](https://github.com/haskell-servant/haskell-servant.github.io/)
|
||||
- Feel free to use the issue tracker (or to send PRs!) on the website's repository to give feedback and suggestions about this tutorial
|
||||
|
||||
Introduction
|
||||
-------------
|
||||
|
||||
*servant* has the following guiding principles:
|
||||
|
||||
- concision
|
||||
|
||||
This is a pretty wide-ranging principle. You should be able to get nice
|
||||
documentation for your web servers, and client libraries, without repeating
|
||||
yourself. You should not have to manually serialize and deserialize your
|
||||
resources, but only declare how to do those things *once per type*. If a
|
||||
bunch of your handlers take the same query parameters, you shouldn't have to
|
||||
repeat that logic for each handler, but instead just "apply" it to all of
|
||||
them at once. Your handlers shouldn't be where composition goes to die. And
|
||||
so on.
|
||||
|
||||
- flexibility
|
||||
|
||||
If we haven't thought of your use case, it should still be easily
|
||||
achievable. If you want to use templating library X, go ahead. Forms? Do
|
||||
them however you want, but without difficulty. We're not opinionated.
|
||||
|
||||
- separation of concerns
|
||||
|
||||
Your handlers and your HTTP logic should be separate. True to the philosphy
|
||||
at the core of HTTP and REST, with *servant* your handlers return normal
|
||||
Haskell datatypes - that's the resource. And then from a description of your
|
||||
API, *servant* handles the *presentation* (i.e., the Content-Types). But
|
||||
that's just one example.
|
||||
|
||||
- type safety
|
||||
|
||||
Want to be sure your API meets a specification? Your compiler can check
|
||||
that for you. Links you can be sure exist? You got it.
|
||||
|
||||
To stick true to these principles, we do things a little differently than you
|
||||
might expect. The core idea is *reifying the description of your API*. Once
|
||||
reified, everything follows. We think we might be the first web framework to
|
||||
reify API descriptions in an extensible way. We're pretty sure we're the first
|
||||
to reify it as *types*.
|
||||
|
||||
To be able to write a webservice you only need to read the first two sections,
|
||||
but the goal of this document being to get you started with servant, we also
|
||||
cover the couple of ways you can extend servant for a great good.
|
||||
|
||||
Tutorial
|
||||
---------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api-type.lhs
|
||||
server.lhs
|
||||
client.lhs
|
||||
javascript.lhs
|
||||
docs.lhs
|
Loading…
Reference in a new issue