Skip to content

Commit 0375ca5

Browse files
authored
Add concept and exercise for Browser.sandbox
1 parent ae3113f commit 0375ca5

20 files changed

Lines changed: 668 additions & 8 deletions

File tree

bin/build.sh

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ set -o pipefail
55

66
# SANITY CHECKS
77

8+
echo "Checking that all elm.json files are identical to template/elm.json"
89
for elm_json in exercises/*/*/elm.json
910
do
10-
cmp template/elm.json $elm_json
11+
diff -w template/elm.json $elm_json
1112
done
1213
echo "All elm.json files are identical to template/elm.json"
1314

@@ -63,15 +64,18 @@ rm -rf build
6364
mkdir -p build/src build/tests
6465
cp template/elm.json build/
6566

66-
for example_file in exercises/concept/**/.meta/*.elm
67+
for exemplar_file in exercises/concept/**/.meta/*.elm
6768
do
68-
exercise_dir=$(dirname $(dirname $example_file))
69-
# get kebab-case slug and transform it to PascalCase
70-
exercise_name=$(basename $exercise_dir | sed -r 's/(^|-)([a-z])/\U\2/g')
69+
exercise_dir=$(dirname $(dirname ${exemplar_file}))
70+
# solution_file is src/ValentinesDay.elm or similar from config.json
71+
solution_file=$(jq --raw-output '.files.solution | .[0]' "${exercise_dir}/.meta/config.json")
72+
# solution_file_stub is ValentinesDay or similar
73+
solution_file_stub=$(echo ${solution_file} | sed -r 's/src\/([a-zA-Z]*)\.elm/\1/')
74+
7175
cp $exercise_dir/src/*.elm "build/src/"
72-
cp $example_file "build/src/$exercise_name.elm"
76+
cp $exemplar_file "build/src/$solution_file_stub.elm"
7377
# Copy tests files under a unique temporary directory and remove all "skip <| ..."
74-
cat "$exercise_dir/tests/Tests.elm" | sed "s/module Tests/module Tests$exercise_name/" | sed 's/skip <|//g' > "build/tests/Tests$exercise_name.elm"
78+
cat "$exercise_dir/tests/Tests.elm" | sed "s/module Tests/module Tests$solution_file_stub/" | sed 's/skip <|//g' > "build/tests/Tests$solution_file_stub.elm"
7579
done
7680

7781
cd build && npx --no-install elm-test-rs --fuzz 10

bin/run_all_exercises.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ done
3434
for exercise_dir in "$exercices"/concept/*
3535
do
3636
echo "Running tests for concept exercise $exercise_dir"
37-
bin/run.sh "ignored_slug" $exercise_dir $exercise_dir > /dev/null
37+
bin/run.sh "ignored_slug" $exercise_dir $exercise_dir
3838

3939
# Check that all tests have a task_id defined
4040
all_have_task_id=$(jq '.tests | map(.task_id) | all' "$exercise_dir/results.json")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"blurb": "Learn how to write Web Applications in Elm",
3+
"authors": [
4+
"ceddlyburge"
5+
],
6+
"contributors": [
7+
"jiegillet"
8+
]
9+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# About
2+
3+
Elm is a delightful language for building reliable web applications.
4+
It has friendly error messages, great performance, small assets, and no runtime exceptions.
5+
6+
Probably the most famous and widely copied part of Elm is The Elm Architecture, which is a simple pattern for structuring web applications, and is how all Elm web applications are written.
7+
8+
The core idea is that your code is built around a `Model` of your application state, a way to `update` your model, and a way to `view` your model.
9+
10+
The `Model` contains the application’s state - all the data that the application needs.
11+
It can be any type, but in any useful application it is usually a [record][record].
12+
If you imagine a simple application with a text box, and text showing the reverse of that text, the model would look like below.
13+
You can also see this example on the [Elm Guide][elm-guide-text-fields].
14+
15+
```elm
16+
type alias Model =
17+
{ text : String
18+
}
19+
```
20+
21+
`update` is a function that gets called with a `Msg` when there's a change (like the user clicking a button).
22+
It takes the `Msg` and the current `Model`, and returns a new `Model`.
23+
The `Msg` can be any type, but it is usually a [custom type][custom-type].
24+
25+
```elm
26+
type Msg
27+
= TextChanged String
28+
29+
update : Msg -> Model -> Model
30+
update msg model =
31+
case msg of
32+
TextChanged newText ->
33+
{ model | text = newText }
34+
```
35+
36+
As with all custom types, the variants of the `Msg` can include as much arbitrary information as they need.
37+
In the example above the `TextChanged` variant includes a `String`.
38+
39+
`view` is a function that returns HTML to show to the user in the browser.
40+
It takes the current `Model` and returns an `Html Msg` (the type that Elm uses to represent HTML).
41+
Each HTML element (for example `<div>`) has a corresponding Elm function (`Html.div`), in the `Html` package.
42+
Each of these functions takes two parameters, the first is a list of attributes (from the `Html.Attributes` and `Html.Events` packages), and the second is a list of child elements (functions from the `Html` package).
43+
There is also a `text` function that represents HTML string content, which just takes a string parameter, but is otherwise used in the same way as all the other functions / elements in the `Html` package.
44+
45+
The `view` function can specify that certain events, like clicking a button or editing text in a text box, result in a `Msg` being created, which the Elm Runtime will use to call the `update` function, modify the `Model` and generate updated HTML.
46+
In the code below this happens with `Html.Events.onInput TextChanged`.
47+
48+
```elm
49+
view : Model -> Html Msg
50+
view model =
51+
Html.div []
52+
[ Html.input
53+
[ Html.Attributes.placeholder "Text to reverse"
54+
, Html.Attributes.value model.text
55+
, Html.Events.onInput TextChanged
56+
]
57+
[]
58+
, Html.div [] [ Html.text (String.reverse model.text) ]
59+
]
60+
```
61+
62+
`Html.Events.onInput` expects to be passed a function (`String -> Msg`) which it will use to create the `Msg`.
63+
Different events expect different functions depending on what information they can provide, so for example `Html.Events.onCheck` (for checkboxes) expects `Bool -> Msg` and `Html.Events.onClick` just expects a `Msg`.
64+
65+
The Elm language is pure and functional and no mutation is possible, so the Elm Runtime handles all the things that change.
66+
You can see all of the different [HTML Elements][html-elements], [Element Attributes][element-attributes] and [HTML Events][html-events] on the Elm package registry.
67+
68+
There are 4 "levels" of web application you can write in Elm, that get progressively more powerful and more complicated (although Elm remains a delightfully simple language and ecosystem).
69+
These are all defined in the [Browser package][browser-package]:
70+
71+
- [sandbox][browser-sandbox] — react to user input, like buttons and checkboxes
72+
- [element][browser-element] — talk to the outside world, using HTTP and JavaScript interop
73+
- [document][browser-document] — control the `<title>` and `<body>` of a web page
74+
- [application][browser-application] — create full single-page appliations that handle routing / url changes
75+
76+
This concept uses the [sandbox][browser-sandbox], as it is the simplest (and because it is a sandbox, it is relatively easy to work with the Exercism online editor).
77+
The entry module for an Elm program must be called `Main`, and the function that creates the application must be called `main`, as below.
78+
79+
```elm
80+
import Browser
81+
import Html exposing (Html, Attribute, div, input, text)
82+
import Html.Attributes exposing (placeholder, value)
83+
import Html.Events exposing (onInput)
84+
85+
main =
86+
Browser.sandbox { init = init, update = update, view = view }
87+
88+
init : Model
89+
init =
90+
{ text = "" }
91+
92+
-- Add Model, Msg, update and view here
93+
```
94+
95+
We have already seen the `update` and `view` functions, and the [sandbox][browser-sandbox] additionally requires an `init` function, to return the initial state of the `Model`.
96+
97+
[record]: https://elm-lang.org/docs/records
98+
[custom-type]: https://guide.elm-lang.org/types/custom_types.html
99+
[elm-guide-text-fields]: https://guide.elm-lang.org/architecture/text_fields
100+
[html-elements]: https://package.elm-lang.org/packages/elm/html/latest/Html
101+
[element-attributes]: https://package.elm-lang.org/packages/elm/html/latest/Html-Attributes
102+
[html-events]: https://package.elm-lang.org/packages/elm/html/latest/Html-Events
103+
[browser-package]: https://package.elm-lang.org/packages/elm/browser/latest/
104+
[browser-sandbox]: https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox
105+
[browser-element]: https://package.elm-lang.org/packages/elm/browser/latest/Browser#element
106+
[browser-document]: https://package.elm-lang.org/packages/elm/browser/latest/Browser#document
107+
[browser-application]: https://package.elm-lang.org/packages/elm/browser/latest/Browser#application
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# About
2+
3+
Elm is a delightful language for building reliable web applications.
4+
It has friendly error messages, great performance, small assets, and no runtime exceptions.
5+
6+
Probably the most famous and widely copied part of Elm is The Elm Architecture, which is a simple pattern for structuring web applications, and is how all Elm web applications are written.
7+
8+
The core idea is that your code is built around a `Model` of your application state, a way to `update` your model, and a way to `view` your model.
9+
10+
The `Model` contains the application’s state - all the data that the application needs.
11+
If you imagine a simple application with a text box, and text showing the reverse of that text, the model would look like below.
12+
You can also see this example on the [Elm Guide][elm-guide-text-fields].
13+
14+
```elm
15+
type alias Model =
16+
{ text : String
17+
}
18+
```
19+
20+
`update` is a function that gets called with a `Msg` when there's a change (like the user clicking a button).
21+
It takes the `Msg` and the current `Model`, and returns a new `Model`.
22+
23+
```elm
24+
type Msg
25+
= TextChanged String
26+
27+
update : Msg -> Model -> Model
28+
update msg model =
29+
case msg of
30+
TextChanged newText ->
31+
{ model | text = newText }
32+
```
33+
34+
`view` is a function that returns HTML to show to the user in the browser.
35+
It takes the current `Model` and returns an `Html Msg` (the type that Elm uses to represent HTML).
36+
37+
```elm
38+
view : Model -> Html Msg
39+
view model =
40+
Html.div []
41+
[ Html.input
42+
[ Html.Attributes.placeholder "Text to reverse"
43+
, Html.Attributes.value model.text
44+
, Html.Events.onInput TextChanged
45+
]
46+
[]
47+
, Html.div [] [ Html.text (String.reverse model.text) ]
48+
]
49+
```
50+
51+
[elm-guide-text-fields]: https://guide.elm-lang.org/architecture/text_fields
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"url": "https://guide.elm-lang.org/architecture/",
4+
"description": "The Elm Architecture"
5+
},
6+
{
7+
"url": "https://guide.elm-lang.org/architecture/buttons",
8+
"description": "Buttons example (using Browser.sandbox)"
9+
},
10+
{
11+
"url": "https://package.elm-lang.org/packages/elm/browser/latest/",
12+
"description": "Browser package"
13+
},
14+
{
15+
"url": "https://github.com/lustre-labs/lustre",
16+
"description": "Lustre, a Gleam framework based on The Elm Architecture"
17+
},
18+
{
19+
"url": "https://github.com/redbadger/crux",
20+
"description": "Crux, a Rust cross platform app framework based on The Elm Architecture"
21+
}
22+
]

config.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,22 @@
210210
],
211211
"status": "beta"
212212
},
213+
{
214+
"slug": "paulas-palindromes",
215+
"name": "Paula's Palindromes",
216+
"uuid": "31f9b8d8-701d-4124-8ca1-726f5969661c",
217+
"concepts": [
218+
"web-applications-sandbox"
219+
],
220+
"prerequisites": [
221+
"records",
222+
"custom-types",
223+
"pattern-matching",
224+
"lists",
225+
"strings"
226+
],
227+
"status": "beta"
228+
},
213229
{
214230
"slug": "monster-attack",
215231
"name": "Monster Attack",
@@ -1530,6 +1546,11 @@
15301546
"slug": "random",
15311547
"name": "Random"
15321548
},
1549+
{
1550+
"uuid": "d3864c81-b7e4-4b42-87d1-aac069ecb839",
1551+
"slug": "web-applications-sandbox",
1552+
"name": "Web Applications"
1553+
},
15331554
{
15341555
"uuid": "afb8dec3-c776-4f46-a6ee-77d30963cf56",
15351556
"slug": "json",
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Hints
2+
3+
## General
4+
5+
- Check out the [Elm Guide][elm-guide], which has a nice worked example that is similar.
6+
7+
## 1. Define the Model and Msg types for the application, and write the init function
8+
9+
- The `Model` contains the application's state - all the data that the application needs.
10+
- It should be a [record][record].
11+
- This application just needs a `String` to store the text in the text box (the tests require it to be called `content`).
12+
13+
- The `Msg` type defines the messages that are passed to the `update` function, to trigger specific changes in the model.
14+
- It should be a [custom type][custom-type].
15+
- This application only needs one change to the model - updating the model when the text in the text box changes.
16+
17+
- The `init` function should simply return a `Model` with sensible a default value (the tests require an empty string).
18+
19+
## 2. Write the update function
20+
21+
- In any useful application the update function will use a `case` statement to pattern match on the `Msg` parameter.
22+
- In each branch of the case statement it will extract information it needs from the `Msg` parameter and return an updated `Model`
23+
- The `Model` should be a record, and [record update syntax][record-update-syntax] is normally used.
24+
25+
## 3. Write the view function
26+
27+
- The `view` function should return a `div` for the root element
28+
- The first child should be an `input`, with a `value` attribute for the current text, and an `onInput` attribute / event with the relevant variant of the `Msg`.
29+
- The second child should be another `div` with a `text` child stating whether the text is a palindrome or not.
30+
31+
## 4. Write the main function
32+
33+
- The main function should just call [`Browser.sandbox`][browser-sandbox]
34+
- `Browser.sandbox` requires a [record][record] argument with the `init`, `update` and `view` functions.
35+
36+
[elm-guide]: https://guide.elm-lang.org/architecture/text_fields
37+
[record]: https://elm-lang.org/docs/records
38+
[custom-type]: https://guide.elm-lang.org/types/custom_types.html
39+
[record-update-syntax]: https://elm-lang.org/docs/records#updating-records
40+
[browser-sandbox]: https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Instructions
2+
3+
Your friend Paula is trying to get better at solving crossword clues. She has read that [some clues show that the answer is a palindrome][palindrome-crossword-clues].
4+
A palindrome is a word or sentence that reads the same backards as forwards.
5+
6+
She asks you write her a very simple website that will check whether a string is a palindrome (or not) so that she can check her guesses.
7+
8+
You decide to do this using an Elm [sandbox][browser-sandox] application.
9+
10+
The application will have a text box, that Paula can type her guess into, and text stating whether or not the text in the box is a palindrome.
11+
The text will update whenever the text in the text box changes.
12+
13+
## 1. Define the `Model` and `Msg` types for the application, and write the `init` function
14+
15+
The application `Model` needs the string to check, this field should be called `content`.
16+
17+
The application needs a `Msg` to indicate that the text in the Text Box has changed.
18+
The variant for this should be called `Change`
19+
20+
The `init` function should return a initial `Model` value with an empty `content` field.
21+
22+
## 2. Write the update function
23+
24+
The `update` function should take a `Msg` and a `Model` parameter, and return a new `Model`.
25+
The `Msg` will carry some text typed by the user, and the new `Model` should include that text.
26+
27+
## 3. Write the view function
28+
29+
Elm requires that the view function has to return an `Html msg` type, which means that you need to return a single element from the view function (and not an array of elements).
30+
31+
Inside this root element, there should be an `input` element (the Text Box), and a `div` with `text` content to state whether the text in the `input` is a palindrome or not.
32+
33+
The text should be "This is a palindrome" or "Not a palindrome".
34+
35+
## 4. Write the main function
36+
37+
The `main` function should call [`Browser.sandbox`][browser-sandbox], passing a record parameter with the `init`, `update` and `view` functions.
38+
To make the exercise work on the Exercism online editor, we use a fake `Browser.sandbox` with the code.
39+
This makes no difference to the code that you write to solve this exercise.
40+
41+
[palindrome-crossword-clues]: https://www.theguardian.com/crosswords/crossword-blog/2012/nov/01/cryptic-crosswords-beginners-palindromes
42+
[browser-sandbox]: https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox

0 commit comments

Comments
 (0)