You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Install [leinigen](http://github.com/technomancy/leinigen) to build your clojure project.
3
+
If you are reading this tutorial, the chances that you have downloaded the correct git archive are quite high.
4
4
5
-
Create a leinigen project for your todo app based on the compojure template.
5
+
Congratulations!
6
6
7
-
`lein new compojure todo-app`
7
+
If you are viewing this tutorial in a browser, the chances that you have already installed [leinigen](http://github.com/technomancy/leinigen) are also quite high. If this is not the case, please do this right now.
8
8
9
-
Update the `project.clj` so that it contains the latest version of the hiccup templating library (currently version "1.0.5")
9
+
During this tutorial we want to have a hands on experience with Clojure and build a Clojure web application from the base up.
10
10
11
-
The application can be started with the command:
11
+
The app that we are going to be working on is the `todo-app` (so if you `'cd tutorial'`ed to start this tutorial, `cd ../todo-app` to get into the correct directory now). From now on, we will assume you are working in this directory.
12
+
13
+
The app was created using the compojure leinigen template
12
14
13
-
`lein ring server`
15
+
lein new compojure todo-app
14
16
15
-
By default, this will start a server on port 3000.
17
+
If you are using the [Nightcode IDE](https://sekao.net/nightcode/) for developing Clojure (which I recommend, especially when starting out with the language), you can start the application directly from the IDE.
16
18
17
-
Try to modify the application so that it returns the string "Hello My Lovely World!"
19
+
Otherwise, you can start the app from the command line:
18
20
19
-
Once you are done, execute the `./next` script to retrieve the next instruction step.
21
+
lein ring server
20
22
21
-
If you don't know what to do, execute the `./next` script to retrieve an example application.
23
+
By default, this will start a server on port 3000 (the port can also be optionally specified as an additional argument)
22
24
25
+
For each task in the tutorial, we have created a test file to test if the the task has been fulfilled. The first test file can already be found in the project test files: `/test/todo_app/01_tests.clj`
26
+
27
+
The tests can be run as follows:
28
+
29
+
lein test
30
+
31
+
Further tests for each task can be found in the `tests/` directory. If you are stuck on a specific task, don't despair! There is a `cheats/` directory which contains an example namespace that you can copy over to your project source files (`src/todo_app`) so that the tests will pass.
32
+
33
+
If you think that you have finished one of the tasks, you can execute the `next` script which will check if all tests pass, and if so, will copy over the next test file.
Copy file name to clipboardExpand all lines: tutorial/chapters/01_instructions.md
+66-17Lines changed: 66 additions & 17 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,25 +1,67 @@
1
-
# Chapter 01
1
+
# Basics, HTML Templating, and Namespaces
2
2
3
-
Congratulations! You have created your first Clojure web application!
3
+
If you have started the `todo-app` with `lein ring server` you can see what the app looks like at `http://localhost:3000`
4
4
5
-
Currently, the application has one route '/' which returns a message in text format.
5
+
The app currently only has a single route '/' which returns a message in text format. We want to return HTML instead of text, so let's look at what resources Clojure has to help us with this.
6
6
7
-
We've created an example application `example-todo-app` which will illustrate our solution to the different tasks throughout the rest of the tutorial.
7
+
## Basics
8
8
9
-
## HTML Templating
9
+
Clojure is a [Lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language\)) which runs on the [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine), which means that elements that belong together are grouped together in parentheses `(``)`.
10
+
11
+
As far as data types go, Clojure implements all of the basic data types, including integers:
12
+
13
+
42
14
+
15
+
Strings:
10
16
11
-
We would like for this route to return an HTML page instead of using text. For this, we can use the Hiccup library which translates Clojure data structures into the HTML format.
17
+
"I'm a string!"
12
18
13
-
In Clojure, one of the datatypes is a keyword. This is a symbolic identifier which stands for itself. A keyword looks like this:
19
+
Keywords (a symbolic identifier that stands for itself):
14
20
15
21
:keyword
16
22
17
-
:anotherKeyword
23
+
In Clojure, there is also great support for [persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure) which are immutable by default and use structural sharing to remain efficient. Because Clojure is dynamically typed, they can contain a mixture of any data types.
18
24
19
-
Another important data type in Clojure are vectors. Vectors are a collection type that are similar in functionality to a list but are optimized to provide a quick lookup time for finding elements within the collection. A vector is denoted by square brackets `[``]` and, because Clojure is a dynamic programming language, can contain elements of any type.
25
+
The most common collection types are lists:
26
+
27
+
(list 1 2 3)
28
+
29
+
Vectors (similar in functionality to lists, but optimized to provide a quick lookup time):
{:someKey "Some Value" "String can be keys too" 2}
36
+
37
+
Commas are optional and are treated as whitespace.
38
+
39
+
As stated above, in Clojure all elements which belong together are grouped in parentheses. In addition to this, [prefix notation](https://en.wikipedia.org/wiki/Polish_notation) is exclusively used, so the function or [macro](https://clojure.org/reference/macros) which is being called is always found in the first position followed by all arguments.
40
+
41
+
A variable can be defined in Clojure with the `def` keyword
42
+
43
+
(def x 5)
44
+
45
+
`def` should only be used for values which do not change!
46
+
47
+
A function can be defined with the keyword `fn`
48
+
49
+
(fn [x] (+ x 5))
50
+
51
+
To define a function which has a specific name, these two can be combined with the `defn` keyword:
52
+
53
+
(defn add2 [x y] (+ x y))
54
+
55
+
That's pretty much it as far as the syntax goes! Any additional syntax that we will need for our application will be introduced as we go.
56
+
57
+
The [Clojure Cheatsheet](https://clojure.org/api/cheatsheet) is a good reference for finding useful functions that can be used when developing Clojure.
58
+
59
+
For programming Clojure, it is a good idea to follow [this style guide](https://github.com/bbatsov/clojure-style-guide). This has the added benefit that if you are using the [parinfer plugin](https://shaunlebron.github.io/parinfer/) (which comes built in with Nightcode), by styling your Clojure code according to the style guide, the parentheses will be correctly generated and set.
60
+
61
+
## HTML Templating
62
+
63
+
We can use the [Hiccup](https://github.com/weavejester/hiccup) for doing HTML templating using Clojure data structures.
64
+
23
65
Hiccup takes advantage of the fact that conceptually, an HTML element is a list of subelements which has a particular name, i.e.
24
66
25
67
<div>
@@ -35,11 +77,12 @@ In Clojure we have something like a list (Vectors!) and something like a name (K
35
77
36
78
Hiccup provides the `hiccup.page/html5` element for generating an HTML5 page from such a data structure. This can also be combined with functions to have reusable templates!
37
79
38
-
(defn page [title content]
80
+
(defn page [title & content]
39
81
(html5
40
82
[:head [:title title]]
41
-
[:body [:h1 title]
42
-
content]))
83
+
[:body content]))
84
+
85
+
The `&` in the function specifies that the function takes a variable number of arguments. The first argument `title` is required, and any others that are passed in will be bound to `content` as a list. In Hiccup, you can simply insert the list of elements, and these will be rendered correctly within the body tag.
43
86
44
87
## Namespaces
45
88
@@ -66,10 +109,16 @@ Or you can just refer all of the functions from a namespace (only a good idea if
66
109
(ns example.handler
67
110
(:require [hiccup.page :refer :all]))
68
111
69
-
Task 01:
112
+
## Task 01:
113
+
114
+
Update your application by modifiying the `project.clj` file to include a dependency to hiccup (currently in version `2.0.0-alpha1`).
115
+
116
+
In the `src/todo_app/handler.clj` file, create a `page` function which sets the title of the page and adds a variable number of elements to the body of the page. Also include the 'splendor.css' file that is a static resource in the project (found under `resources/public`) to the HTML page (HINT: use the `hiccup.page/include-css` function).
117
+
118
+
Use this function to produce HTML for the main route "/" which sets the title of the page to `TODO App`.
119
+
120
+
Tests for this task are already present in the `/test/todo_app/01_tests.clj` file.
70
121
71
-
Update your application to include a dependency to hiccup (currently in version `2.0.0-alpha1`).
72
-
Create a `page` function which takes the title of a page and embeds it in a generated HTML document.
73
-
Use this function to produce HTML for the main route which sets the title of the page to `TODO App`.
122
+
If you think you are done, execute the `next` script which will run the tests and, if successful, will copy the tests for Task 2 into the project. Then go on to the next step!
74
123
75
-
Tests for this behavior can be found in the `handler_test.clj`file.
124
+
If you need some help, look at the `project.clj`and `handler.clj` files in the `cheats/01/` directory.
Copy file name to clipboardExpand all lines: tutorial/chapters/02_instructions.md
+8-7Lines changed: 8 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,15 +1,14 @@
1
-
# Chapter 02
2
-
## Creating our domain objects
1
+
# Creating Domain Objects
3
2
4
-
In Clojure, all data types are immutable. When adding to a collection, a new collection is returned. This uses [persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure) so that the data structures are still efficient.
3
+
In Clojure, all data types are immutable. When adding to a collection, a new collection is returned. As stated in [the last section](/01_instructions.md), this uses [persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure) so that the data structures are still efficient.
5
4
6
5
This is done because a value in the real world does not and cannot change: the value `42` will always be `42`.
7
6
8
7
However, when we are dealing with the real world we have the concept of **identity** which is where we associate a logical entity with a series of values which may change over time. For instance, we may associate the entity _mouse_ with the positions `x,y` that our computer mouse takes on while we move it around our screen.
9
8
10
9
This logical entity will have exactly one state at any given point of time.
11
10
12
-
An in depth explanation can be found here: https://clojure.org/about/state
11
+
An in depth explanation can be found [here](https://clojure.org/about/state).
13
12
14
13
In Clojure, we can use atoms to model this concept of identity. For instance, we can define a logical entity 'counter' whose state will be incremented over time.
15
14
@@ -27,7 +26,7 @@ In Clojure the `!` is used to denote a function which has a side effect.
27
26
28
27
## Task 02:
29
28
30
-
Define a new namespace `domain.clj` in your project.
29
+
Define a new namespace `src/todo_app/domain.clj` in your project.
31
30
32
31
Define the domain objects for the application in this file. We will model our application state `todos` as a vector containing Clojure maps with the following form:
33
32
@@ -37,6 +36,8 @@ Create a function `add-todo!` which adds an item to the list and then returns th
37
36
38
37
Create a function `remove-todo!` which takes an item id and removes the correct item from the list.
39
38
40
-
Hint: use the https://clojure.org/api/cheatsheet to see what functions can be used for dealing with maps, vectors, and keywords!
39
+
Hint: use the [Clojure Cheatsheet](https://clojure.org/api/cheatsheet) to see what functions can be used for dealing with maps, vectors, and keywords!
41
40
42
-
The desired behavior of the domain model can be seen in the `domain_test` in the example-todo-app.
41
+
You've hopefully either executed the `next` script or copied the `tests/02_tests.clj` file over to your project. Once these tests pass, move on to the next task!
42
+
43
+
If you need some help, look at the `cheats/02/domain.clj` file.
We now have our HTML templates and our domain objects. Now we want to tie everything together.
5
4
5
+
## Routing
6
+
6
7
The library we are using for routing in our application is [Compojure](https://github.com/weavejester/compojure).
7
8
8
9
We've seen the most basic route definition already.
9
10
10
-
`(GET "/" [] "HI!")`
11
+
(GET "/" [] "HI!")
11
12
12
13
If we want to extract a parameter from the URI, Compojure gives us the opportunity to destructure it.
13
14
14
-
`(GET "/:book" [book] (show book))`
15
+
(GET "/:book" [book] (show book))
15
16
16
17
This also works for form parameters
17
18
18
-
`(POST "/" [something] (dosth something))`
19
+
(POST "/" [something] (dosth something))
19
20
20
21
More information about the destructuring syntax can be found [here](https://github.com/weavejester/compojure/wiki/Destructuring-Syntax).
21
22
22
23
To generate redirects, the function `ring.util.response/redirect` can be used.
23
24
24
25
To generate forms, use the functions available from `hiccup.form`
25
26
26
-
## Middleware & CSRF
27
+
## Middleware
27
28
28
29
In Clojure, the requests and responses are translated into Clojure data structures which are modelled as Clojure maps. This according to the [Ring standard](https://github.com/ring-clojure/ring/wiki/Concepts), which is used in all major Clojure webservers.
29
30
30
31
This gives you complete control over the requests and responses. Once pattern is to write middleware which can modify a request going in or modify a request when going out.
31
32
32
-
```
33
-
(defn middleware [handler]
34
-
(fn [request]
35
-
;; Do something to request
36
-
(let [response (handler request)]
37
-
;; Do something to response
38
-
response)))
39
-
```
33
+
(defn middleware [handler]
34
+
(fn [request]
35
+
;; Do something to request
36
+
(let [response (handler request)]
37
+
;; Do something to response
38
+
response)))
40
39
41
40
It is not necessary to write a lot of middleware when just getting started. The default template for Compojure uses the default middleware `site-defaults` which is available from the `ring.middleware.defaults`.
42
41
42
+
## CSRF
43
+
43
44
Among other things, the middleware initializes the [CSRF Protection](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF\)) that is available in Clojure. Clojure protects against Anti-Forgery attacks by rendering a secret into the HTML of a web page. With requests which modify the application state (i.e. :post, :put, :delete, :patch), the client needs to send this secret back to the server so that the server knows that it comes from a valid user. This secret can be added into your application by generating a `ring.util.anti-forgery/anti-forgery-field` within all of the necessary forms in the application.
44
45
46
+
## XSS
47
+
48
+
In our application, we are rendering String that we receive from the user directly in our HTML document. This makes us vulnerable for [XSS Attacks](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS\)). In order to protect us, we can use the `hiccup.util/escape-html` function to escape the html output when we are rendering it in the document.
49
+
45
50
Task 03:
46
51
47
52
Define the following routes for the application
48
53
49
54
1. GET / -> returns a list of todos
50
55
2. POST / -> adds a new todo, redirects to /
56
+
* If the todo string is blank, ignore the request and redirect to /
57
+
* We should automatically escape any HTML from the user so that we are not vulnerable for XSS attacks
51
58
3. GET /:id -> shows a single todo
52
59
4. DELETE /:id -> removes todo, redirects to /
53
60
54
-
The tests can be seen in the `handler_test.clj` file
55
-
56
-
Now you have a working web application!
61
+
Once the `03_tests.clj` pass, you have a working web application and can move on to the next section!
57
62
58
-
As a bonus task, you can make it pretty by adding CSS!
63
+
If you need some help, look at the `cheats/03/handler.clj` file.
This is obviously only a basic web application, but there are a lot of libraries available in the Clojure universe to allow you to configure your application as you want.
@@ -7,4 +7,4 @@ Some that might be of interest
7
7
8
8
*[Buddy](https://github.com/funcool/buddy-auth) for Authorization
9
9
*[Yesql](https://github.com/krisajenkins/yesql) for dealing with SQL databases in Clojure
10
-
*[environ](https://github.com/weavejester/environ) for configuring the application over environment variables
10
+
*[environ](https://github.com/weavejester/environ) for configuring the application over environment variables
0 commit comments