Commit 089e18f0 authored by Paul's avatar Paul

Initial Examples

parents
# Simple Web Examples
This repository contains example projects demonstrating how to use
Simple Web.
1. [Hello World](examples/hello_world)
2. [Handling Static](examples/static_eg)
3. [Handling Templates](examples/templates_eg)
4. [An API](examples/api)
# A Simple Web API
To make creating an API easier, Simple Web has included a
`sw:route/3` and exposed the existing JSON Dict functionality of
`[library(http/http_json)](http://www.swi-prolog.org/pldoc/man?section=jsonsupport)`.
## Setup Your API
First we create a base url for our API with `http:location/3`, this
lets us define our API routes with `api/1` as shown with
`api(get_number)`.
## API Methods
You no-longer need to search the `Request` for the method and then
handle it, just put the method into the `sw:route/3`:
```
sw:route(api(somewhere), method(get), _Request) :- ...
```
The POST API route in the example can be tested with curl:
```
:~$ curl --header "Content-Type: application/json" --request POST --data '{"number": "2"}' http://localhost:8000/api/send_number
```
## JSON and Dicts
For more information about the `http_read_json_dict/2&3` predicates and
`reply_json_dict/1&2` predicates, please refer to the documentation
at `[library(http/http_json)](http://www.swi-prolog.org/pldoc/man?section=jsonsupport)`.
:- module(main,
[ serve/0
]
).
:- use_module(library(sw/simple_web)).
http:location(api, "/api", []).
sw:route(root(.), _Request) :-
reply_html("<a href='/api/get_number'>Go To API</a>").
sw:route(api(get_number), method(get), _Request) :-
random_between(1, 10, Number),
reply_json_dict(resp{number: Number, status: success}).
sw:route(api(send_number), method(post), Request) :-
http_read_json_dict(Request, Data),
NumStr = Data.get(number),
number_string(Number, NumStr),
Incr is Number + 1,
Resp = resp{number: Number,
incr: Incr,
status: success},
reply_json_dict(Resp).
serve :-
run(port(8000)).
# Hello World
**Create your first web page!**
This example shows how to setup a route and reply with
a HTML string. The predicate `serve/0` is used to run
the web server.
## Running from the command line:
```
:~$ swipl -g [hello_world] -g serve
```
## Running from swipl:
```
?- use_module(hello_world).
?- serve.
```
:- module(hello_world,
[ serve/0
]
).
:- use_module(library(sw/simple_web)).
sw:route('/', _Request) :-
reply_html("<h1>Hello, world!</h1>").
serve :-
run(port(8000)).
# Serving Static
Static files are those that are served with as files, for example:
* Images
* CSS
* JavaScript
* Icons
* Fonts
Often a web server (NGINX or Apache) will directly handle static
files for speed of response. However, sometimes, especially during
development, it's useful to let Prolog serve these files too. Simple
Web provides two ways to do this.
## Built-in Static Handling
Make a directory called static at the root of your application.
anything you put into this directory is served at the matching
route. For example `static/images/hedgehog.jpg` is at the matching route
`/static/images/hedgehog.jpg`. You can use this for CSS and
JavaScript too:
```
static
├── css
│   └── styles.css
├── favicon.ico
├── fonts
│   └── roboto.ttf
├── images
│   ├── logo.jpg
│   ├── hedgehog.jpg
│   ├── squirell.jpg
│   └── red_kite.jpg
└── js
└── main.js
```
With this structure you can include tags in your HTML such as:
```
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<link href="/static/css/styles.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/main.js">
<img src="/static/images/hedgehog.jpg" alt="hedgehog">
```
**Don't want to name the directory `static`?** Just add
`config(static_dir, DirectoryName)` to a file called `config.pl` in
the root of your project, where `DirectoryName` is the ground name of
your static directory.
## Handling Complex Cases
Sometimes you need more options and more flexibility. That's why
Simple Web also exposes
`[http_reply_file/3](http://www.swi-prolog.org/pldoc/doc_for?object=http_dispatch%3Ahttp_reply_file/3)` for you. In the example `main.pl` file, we're using this for the '/favicon.ico' route to demonstrate it with caching. Check the linked docs for more information on using this predicate.
config(debug, true).
config(static_dir, static).
:- module(main,
[ serve/0
]
).
:- use_module(library(sw/simple_web)).
% '/'
%
% Respond with image from static files
sw:route(root(.), _Request) :-
reply_html("<h1>Serving from static</h1>
<img src='/static/images/hedgehog.jpg' alt='hedgehog'/>").
% '/favicon.ico'
%
% Respond with image file from static files, cache for speed
sw:route(root('favicon.ico'), Request) :-
http_reply_file('static/images/hedgehog.jpg', [cache(true)], Request).
serve :-
run(port(8000)).
# Rendering Templates
Templates are awesome, they let you render HTML on the fly with the
results of your query. Simple Web was inspired by Simple Template and
was built to exploit this pack. But Simple Web is not Simple
Template, so we encourage you to [read their docs](http://www.swi-prolog.org/pack/list?p=simple_template) for a better
understanding of how to use them.
# Simple Web Templates
Simple web assumes your templates are in a directory called
`templates`, this can be changed by adding to a config.pl file in the
root of your application, where `NewTemplateDir` is the ground name
of your template directory:
```
config(template_dir, NewTemplateDir).
```
# Config Support for Simple Templates
To save you from needing to pass options to `render_template/3` all
the time, you can configure your own default options in the
`config.pl` file. These options are detailed in the Simple Web docs.
:- module(blog,
[ post_titles/1
, blog_post/2
]
).
:- use_module(blog_posts).
post_titles(Titles) :-
findall(T, blog_post(T, _), Titles).
:- module(blog_posts,
[ blog_post/2
]
).
%! blog_post(?title, ?content) is semidet.
blog_post('Post One', 'Once upon a time there was a blog.').
blog_post('Post Two', 'It was a very unimaginative blog.').
blog_post('Post Three', 'But it demonstrated templates well').
:- module(main,
[ serve/0
]
).
:- use_module(library(sw/simple_web)).
% module blog is local and included here
:- use_module(blog,
[ post_titles/1
, blog_post/2
]).
sw:route('/', _Request) :-
post_titles(Titles),
render_template(blog, data{titles: Titles}).
sw:route(root(Name), _Request) :-
blog_post(Name, Content),
Data = data{ name: Name
, content: Content
},
render_template(post, Data).
serve :-
run(port(8000)).
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="static/favicon.ico">
<title>Blog</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1>Blog Posts</h1>
<ul class="list-group">
{{ each titles, title}}<a href="/{{= title}}">
<li class="list-group-item">{{= title}}</li>
</a>{{ end }}
</ul>
</div>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="static/favicon.ico">
<title>Post</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1>{{= name }}</h1>
<p>{{= content}}</p>
<p><a href="/">←Back</a></p>
</div>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment