Skip to content

Commit a9a98c9

Browse files
author
Joy Clark
committed
Update next script and instructions
1 parent a0f07c1 commit a9a98c9

File tree

6 files changed

+132
-77
lines changed

6 files changed

+132
-77
lines changed

todo-app/next

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ current_exercise_no=$(ls test/todo_app/*_tests.clj \
77
| tail -n 1)
88
next_exercise_no="$(printf "%02d" "$(( ${current_exercise_no} + 1 ))")"
99

10-
lein test .
10+
lein test
1111

1212
rc=$?
1313
if [[ $rc != 0 ]]
@@ -16,25 +16,14 @@ then
1616
exit $rc;
1717
fi
1818

19-
echo "Retrieving exercise $next_exercise_no"
20-
21-
#(git checkout $(whoami) || git checkout -b $(whoami)) 2> /dev/null
22-
23-
#if ! git status | grep "nothin.*to commit" &> /dev/null
24-
#then
25-
# git add *.clj && git commit --allow-empty -anm "Commit exercise $(printf "%02d" ${current_exercise_no})"
26-
#fi
27-
28-
29-
#git fetch --all
30-
31-
32-
#if ! git branch -a | grep "/origin/${next_exercise_no}" &> /dev/null
33-
#then
34-
# echo "The next exercise is not yet available"
35-
# exit 1
36-
#fi
37-
38-
#git merge --no-ff -m "Fetching exercise ${next_exercise_no}" origin/${next_exercise_no}
19+
if ! ls tests | grep "${next_exercise_no}_tests.clj" &> /dev/null
20+
then
21+
echo "There are no test for the next exercise!"
22+
echo "You must be done with your web application!"
23+
echo
24+
echo "Congratulations!"
25+
exit 1
26+
fi
3927

40-
#cat tutorial/chapters/${next_exercise_no}_instructions.md
28+
echo "Retrieving tests for $next_exercise_no"
29+
cp tests/${next_exercise_no}_tests.clj test/todo_app
Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
1-
# Tutorial Step 1
1+
# First Steps
22

3-
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.
44

5-
Create a leinigen project for your todo app based on the compojure template.
5+
Congratulations!
66

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.
88

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.
1010

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
1214

13-
`lein ring server`
15+
lein new compojure todo-app
1416

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.
1618

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:
1820

19-
Once you are done, execute the `./next` script to retrieve the next instruction step.
21+
lein ring server
2022

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)
2224

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.

tutorial/chapters/01_instructions.md

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,67 @@
1-
# Chapter 01
1+
# Basics, HTML Templating, and Namespaces
22

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`
44

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.
66

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
88

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:
1016

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!"
1218

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):
1420

1521
:keyword
1622

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.
1824

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):
2030

2131
[:I :am :a :vector :of :keywords :and [:a :subvector :of :keywords]]
2232

33+
Maps:
34+
35+
{: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+
2365
Hiccup takes advantage of the fact that conceptually, an HTML element is a list of subelements which has a particular name, i.e.
2466

2567
<div>
@@ -35,11 +77,12 @@ In Clojure we have something like a list (Vectors!) and something like a name (K
3577

3678
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!
3779

38-
(defn page [title content]
80+
(defn page [title & content]
3981
(html5
4082
[: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.
4386

4487
## Namespaces
4588

@@ -66,10 +109,16 @@ Or you can just refer all of the functions from a namespace (only a good idea if
66109
(ns example.handler
67110
(:require [hiccup.page :refer :all]))
68111

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.
70121

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!
74123

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.

tutorial/chapters/02_instructions.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
# Chapter 02
2-
## Creating our domain objects
1+
# Creating Domain Objects
32

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.
54

65
This is done because a value in the real world does not and cannot change: the value `42` will always be `42`.
76

87
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.
98

109
This logical entity will have exactly one state at any given point of time.
1110

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).
1312

1413
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.
1514

@@ -27,7 +26,7 @@ In Clojure the `!` is used to denote a function which has a side effect.
2726

2827
## Task 02:
2928

30-
Define a new namespace `domain.clj` in your project.
29+
Define a new namespace `src/todo_app/domain.clj` in your project.
3130

3231
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:
3332

@@ -37,6 +36,8 @@ Create a function `add-todo!` which adds an item to the list and then returns th
3736

3837
Create a function `remove-todo!` which takes an item id and removes the correct item from the list.
3938

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!
4140

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.
Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,63 @@
1-
# Chapter 03
2-
## Routing
1+
# Routing, Middleware, and Security
32

43
We now have our HTML templates and our domain objects. Now we want to tie everything together.
54

5+
## Routing
6+
67
The library we are using for routing in our application is [Compojure](https://github.com/weavejester/compojure).
78

89
We've seen the most basic route definition already.
910

10-
`(GET "/" [] "HI!")`
11+
(GET "/" [] "HI!")
1112

1213
If we want to extract a parameter from the URI, Compojure gives us the opportunity to destructure it.
1314

14-
`(GET "/:book" [book] (show book))`
15+
(GET "/:book" [book] (show book))
1516

1617
This also works for form parameters
1718

18-
`(POST "/" [something] (dosth something))`
19+
(POST "/" [something] (dosth something))
1920

2021
More information about the destructuring syntax can be found [here](https://github.com/weavejester/compojure/wiki/Destructuring-Syntax).
2122

2223
To generate redirects, the function `ring.util.response/redirect` can be used.
2324

2425
To generate forms, use the functions available from `hiccup.form`
2526

26-
## Middleware & CSRF
27+
## Middleware
2728

2829
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.
2930

3031
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.
3132

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)))
4039

4140
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`.
4241

42+
## CSRF
43+
4344
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.
4445

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+
4550
Task 03:
4651

4752
Define the following routes for the application
4853

4954
1. GET / -> returns a list of todos
5055
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
5158
3. GET /:id -> shows a single todo
5259
4. DELETE /:id -> removes todo, redirects to /
5360

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!
5762

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.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Chapter 04
1+
# Further Resources
22
## That's it for now!
33

44
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
77

88
* [Buddy](https://github.com/funcool/buddy-auth) for Authorization
99
* [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

Comments
 (0)