├── .gitignore ├── README.md ├── day-01 ├── Main.elm ├── README.md └── elm-package.json ├── day-02 ├── Main.elm ├── README.md └── elm-package.json ├── day-03 ├── Main.elm ├── README.md └── elm-package.json ├── day-04 ├── Main.elm ├── README.md ├── elm-package.json ├── index.js ├── package-lock.json └── package.json ├── day-05 ├── Main.elm ├── README.md ├── elm-package.json ├── index.js ├── package-lock.json └── package.json └── day-06 ├── Main.elm ├── MainWithOurType.elm └── elm-package.json /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | *.txt 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | elm-quicks 2 | ========== 3 | 4 | A series of bite-sized topics to gradually help you learn the basics of the friendly functional frontend language. 5 | 6 | Each Friday I can, I do some live-coding with my co-workers. I will share the code in sub-directories here. 7 | 8 | The sub-directories have their own README files that go through the contents of that session. 9 | -------------------------------------------------------------------------------- /day-01/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Char 4 | import Html 5 | 6 | 7 | main = 8 | Html.text (toString (isPalindrome palindrome)) 9 | 10 | 11 | palindrome = 12 | "Are we not drawn onward, we few, drawn onward to new era?" 13 | 14 | 15 | isPalindrome input = 16 | let 17 | characters = 18 | String.toList (String.toLower input) 19 | 20 | justLetters = 21 | List.filter Char.isLower characters 22 | 23 | lettersInReverse = 24 | List.reverse justLetters 25 | in 26 | justLetters == lettersInReverse 27 | -------------------------------------------------------------------------------- /day-01/README.md: -------------------------------------------------------------------------------- 1 | # Day 1: Check that palindrome 2 | 3 | For the first day of live-coding on elm-quicks we created a tiny program that can tell whether a phrase is a palindrome or not. We started with the most simple Elm program possible: 4 | 5 | ```elm 6 | import Html 7 | 8 | main = 9 | Html.text "Hello Elm" 10 | ``` 11 | 12 | Next, we added a potential palindrome and a stub for a function that would check the palindrome-ness of a string. 13 | 14 | ```elm 15 | palindrome = 16 | "Are we not drawn onward, we few, drawn onward to new era?" 17 | 18 | isPalindrome input = 19 | False 20 | ``` 21 | 22 | This was then used in `main`: 23 | 24 | ```elm 25 | main = 26 | Html.text (toString (isPalindrome palindrome)) 27 | ``` 28 | 29 | `toString` was needed to turn the boolean value from `isPalindrome` into a string, which is what `Html.text` expects to receive. 30 | 31 | From then on, we filled out the logic for the function in a `let-in` structure. The `let` block is where you can define local variables for a function. The code after `in` is the actual function body. We also used `Debug.log` at times to make sure things looked like we thought they should. 32 | 33 | The idea for how we can check for palindrome-ness was: turn the phrase into lower case letters only, and then check if it is equal to its reverse. At the live-coding session, I used a silly method to check if a character is a letter. Thanks to one of the participants I found a much nicer way to do this, so the final code looks like this: 34 | 35 | ```elm 36 | isPalindrome input = 37 | let 38 | characters = 39 | String.toList (String.toLower input) 40 | 41 | justLetters = 42 | List.filter Char.isLower characters 43 | 44 | lettersInReverse = 45 | List.reverse justLetters 46 | in 47 | justLetters == lettersInReverse 48 | ``` 49 | -------------------------------------------------------------------------------- /day-01/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 13 | }, 14 | "elm-version": "0.18.0 <= v < 0.19.0" 15 | } 16 | -------------------------------------------------------------------------------- /day-02/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Char 4 | import Html exposing (..) 5 | import Html.Attributes exposing (..) 6 | import Html.Events exposing (..) 7 | 8 | 9 | main = 10 | Html.beginnerProgram 11 | { model = model 12 | , view = view 13 | , update = update 14 | } 15 | 16 | 17 | update textThatTheUserHasWritten model = 18 | textThatTheUserHasWritten 19 | 20 | 21 | view model = 22 | div [] 23 | [ input [ value model, onInput (\text -> text) ] [] 24 | , br [] [] 25 | , Html.text (toString (isPalindrome model)) 26 | ] 27 | 28 | 29 | model = 30 | "Are we not drawn onward, we few, drawn onward to new era?" 31 | 32 | 33 | isPalindrome input = 34 | let 35 | characters = 36 | String.toList (String.toLower input) 37 | 38 | justLetters = 39 | List.filter Char.isLower characters 40 | 41 | lettersInReverse = 42 | List.reverse justLetters 43 | in 44 | justLetters == lettersInReverse 45 | -------------------------------------------------------------------------------- /day-02/README.md: -------------------------------------------------------------------------------- 1 | # Day 2: As a user I want to check my palindrome 2 | 3 | Using the [day-01](../day-01) as a base, we started by renaming things: `palindrome` became `model`, the old `main` became `view` and so on. We also introduced a new kind of entry point `Html.beginnerProgram` and added the `update` function, which simply returned the same model as before. 4 | 5 | ```elm 6 | -- new kind of program, refers to the functions below 7 | main = 8 | Html.beginnerProgram 9 | { model = model 10 | , view = view 11 | , update = update 12 | } 13 | 14 | 15 | -- new function 16 | update msg model = 17 | model 18 | 19 | 20 | -- the old `main`, but NOTE: a parameter was added 21 | view model = 22 | Html.text (toString (isPalindrome model)) 23 | 24 | 25 | -- the old `palindrome` 26 | model = 27 | "Are we not drawn onward, we few, drawn onward to new era?" 28 | ``` 29 | 30 | This resulted in no visible change to how the program works. 31 | 32 | Next, we made the `view` a little more complex by adding an input field on top. The `view` given to the `Html.beginnerProgram` needs to produce a single HTML element, so we wrapped the text in a `div` first. 33 | 34 | ```elm 35 | -- added `exposing (..)` to bring in all the HTML element functions 36 | import Html exposing (..) 37 | -- ... 38 | 39 | view model = 40 | div [] 41 | [ input [] [] 42 | , br [] [] 43 | , text (toString (isPalindrome model)) 44 | ] 45 | ``` 46 | 47 | All HTML elements work the same in Elm: they are functions with the pattern `tagname attributeList childList`. In the code above, none of the elements have any attributes. `div []` means we have a `div` element with an empty attribute list. The div does have children though, namely the input, br, and text. 48 | 49 | Now we had a blank input field displayed on the page right above the text "True". Typing into the field didn't do anything either, so we went on to add the value from the parameter `model` as the value of the input. We were not listening for user events at all either, so we added an `onInput` attribute too. 50 | 51 | In Elm, event handlers like `onInput` are already wired up to pass the value (in this case the text written) through to the Elm runtime. We could also "tag" the values coming from several sources to tell them apart, but for now we don't need to do that. Instead, we give `onInput` a function that does nothing to the string and just returns it as is: `\text -> text`. 52 | 53 | 54 | ```elm 55 | -- added `Html.Attributes`, which is where all the attributes come from 56 | import Html.Attributes exposing (..) 57 | 58 | 59 | -- within `view` 60 | input [ value model, onInput (\text -> text) ] [] 61 | ``` 62 | 63 | This made the input "broken": no matter how you try to type in it, the text would always switch back to the original. Our `update` was always returning the original value. 64 | 65 | In our application, the value coming from the `onInput` was in fact the new string we wanted to check for palindrome-ness. It is the text that the user has written in the input field. This means just switching the `update` to return that new string, we get the end result we were looking for. 66 | 67 | ```elm 68 | update textThatTheUserHasWritten model = 69 | textThatTheUserHasWritten 70 | ``` 71 | 72 | The user can now type in their own potential palindromes and our program tells their validity as they type. Sweet! 73 | 74 | 75 | --- 76 | 77 | You can try out the application and play with the code on Ellie: [https://ellie-app.com/LGftKnn9a1/0](https://ellie-app.com/LGftKnn9a1/0) 78 | -------------------------------------------------------------------------------- /day-02/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 13 | }, 14 | "elm-version": "0.18.0 <= v < 0.19.0" 15 | } 16 | -------------------------------------------------------------------------------- /day-03/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Char 4 | import Html exposing (..) 5 | import Html.Attributes exposing (..) 6 | import Html.Events exposing (..) 7 | 8 | 9 | main = 10 | Html.beginnerProgram 11 | { model = model 12 | , view = view 13 | , update = update 14 | } 15 | 16 | 17 | type Msg 18 | = UpdateCurrentPalindrome String 19 | | SavePalindrome 20 | 21 | 22 | update msg model = 23 | case msg of 24 | UpdateCurrentPalindrome text -> 25 | { model | currentPalindome = text } 26 | 27 | SavePalindrome -> 28 | { model 29 | | currentPalindome = "" 30 | , savedPalindromes = 31 | model.currentPalindome :: model.savedPalindromes 32 | } 33 | 34 | 35 | view model = 36 | div [] 37 | [ input 38 | [ value model.currentPalindome 39 | , onInput UpdateCurrentPalindrome 40 | ] 41 | [] 42 | , br [] [] 43 | , Html.text (toString (isPalindrome model.currentPalindome)) 44 | , br [] [] 45 | , button 46 | [ onClick SavePalindrome ] 47 | [ text "Save the palindrome" ] 48 | , savedPalindromesView model.savedPalindromes 49 | ] 50 | 51 | 52 | savedPalindromesView palindromes = 53 | ul [] 54 | (List.map palindromeView palindromes) 55 | 56 | 57 | palindromeView palindrome = 58 | li [] [ text palindrome ] 59 | 60 | 61 | model = 62 | { currentPalindome = "Are we not drawn onward, we few, drawn onward to new era?" 63 | , savedPalindromes = [] 64 | } 65 | 66 | 67 | isPalindrome input = 68 | let 69 | characters = 70 | String.toList (String.toLower input) 71 | 72 | justLetters = 73 | List.filter Char.isLower characters 74 | 75 | lettersInReverse = 76 | List.reverse justLetters 77 | in 78 | justLetters == lettersInReverse 79 | -------------------------------------------------------------------------------- /day-03/README.md: -------------------------------------------------------------------------------- 1 | # Day 3: Save the palindromes! 2 | 3 | Using the [day-02](../day-02) as a base. 4 | -------------------------------------------------------------------------------- /day-03/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 13 | }, 14 | "elm-version": "0.18.0 <= v < 0.19.0" 15 | } 16 | -------------------------------------------------------------------------------- /day-04/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Char 4 | import Html exposing (..) 5 | import Html.Attributes exposing (..) 6 | import Html.Events exposing (..) 7 | import Http 8 | import Json.Decode exposing (list, string) 9 | import Json.Encode 10 | 11 | 12 | main = 13 | Html.program 14 | { init = init 15 | , view = view 16 | , update = update 17 | , subscriptions = subscriptions 18 | } 19 | 20 | 21 | subscriptions model = 22 | Sub.none 23 | 24 | 25 | type Msg 26 | = UpdateCurrentPalindrome String 27 | | SavePalindrome 28 | | GotPalindromes (List String) 29 | | GotError Http.Error 30 | 31 | 32 | update msg model = 33 | case msg of 34 | UpdateCurrentPalindrome text -> 35 | ( { model | currentPalindome = text }, Cmd.none ) 36 | 37 | SavePalindrome -> 38 | let 39 | newPalindromes = 40 | model.currentPalindome :: model.savedPalindromes 41 | in 42 | ( { model 43 | | currentPalindome = "" 44 | , savedPalindromes = newPalindromes 45 | } 46 | , Http.send handler (Http.post "http://localhost:3000/" (encode newPalindromes) decoder) 47 | ) 48 | 49 | GotPalindromes palindromes -> 50 | ( { model | savedPalindromes = palindromes }, Cmd.none ) 51 | 52 | GotError error -> 53 | ( { model | error = toString error }, Cmd.none ) 54 | 55 | 56 | encode palindromes = 57 | Http.jsonBody (Json.Encode.list (List.map Json.Encode.string palindromes)) 58 | 59 | 60 | view model = 61 | div [] 62 | [ input 63 | [ value model.currentPalindome 64 | , onInput UpdateCurrentPalindrome 65 | ] 66 | [] 67 | , br [] [] 68 | , text (toString (isPalindrome model.currentPalindome)) 69 | , br [] [] 70 | , button 71 | [ onClick SavePalindrome ] 72 | [ text "Save the palindrome" ] 73 | , savedPalindromesView model.savedPalindromes 74 | , br [] [] 75 | , text model.error 76 | ] 77 | 78 | 79 | savedPalindromesView palindromes = 80 | ul [] 81 | (List.map palindromeView palindromes) 82 | 83 | 84 | palindromeView palindrome = 85 | li [] [ text palindrome ] 86 | 87 | 88 | init = 89 | ( { currentPalindome = "Are we not drawn onward, we few, drawn onward to new era?" 90 | , savedPalindromes = [] 91 | , error = "" 92 | } 93 | , Http.send handler (Http.get "http://localhost:3000/" decoder) 94 | ) 95 | 96 | 97 | decoder = 98 | Json.Decode.list string 99 | 100 | 101 | handler result = 102 | case result of 103 | Ok palindromes -> 104 | GotPalindromes palindromes 105 | 106 | Err error -> 107 | GotError error 108 | 109 | 110 | isPalindrome input = 111 | let 112 | characters = 113 | String.toList (String.toLower input) 114 | 115 | justLetters = 116 | List.filter Char.isLower characters 117 | 118 | lettersInReverse = 119 | List.reverse justLetters 120 | in 121 | justLetters == lettersInReverse 122 | -------------------------------------------------------------------------------- /day-04/README.md: -------------------------------------------------------------------------------- 1 | # Day 4: Save the palindromes! 2 | 3 | Using the [day-03](../day-03) as a base. 4 | -------------------------------------------------------------------------------- /day-04/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 13 | "elm-lang/http": "1.0.0 <= v < 2.0.0" 14 | }, 15 | "elm-version": "0.18.0 <= v < 0.19.0" 16 | } 17 | -------------------------------------------------------------------------------- /day-04/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | 5 | const app = express() 6 | app.use(cors()) 7 | 8 | let db = [] 9 | 10 | app.get('/', (req, res) => res.json(db)) 11 | // app.get('/', (req, res) => res.sendStatus(500)) 12 | app.post('/', bodyParser.json(), (req, res) => { 13 | db = req.body 14 | res.json(db) 15 | }) 16 | 17 | module.exports = app.listen(3000, () => { 18 | console.log('Listening on 3000') 19 | }) 20 | -------------------------------------------------------------------------------- /day-04/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "day-04", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.4", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", 10 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 11 | "requires": { 12 | "mime-types": "2.1.17", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.18.2", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 24 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 25 | "requires": { 26 | "bytes": "3.0.0", 27 | "content-type": "1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "1.1.1", 30 | "http-errors": "1.6.2", 31 | "iconv-lite": "0.4.19", 32 | "on-finished": "2.3.0", 33 | "qs": "6.5.1", 34 | "raw-body": "2.3.2", 35 | "type-is": "1.6.15" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.0.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 41 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.2", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 46 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 47 | }, 48 | "content-type": { 49 | "version": "1.0.4", 50 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 51 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 52 | }, 53 | "cookie": { 54 | "version": "0.3.1", 55 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 56 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 57 | }, 58 | "cookie-signature": { 59 | "version": "1.0.6", 60 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 61 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 62 | }, 63 | "cors": { 64 | "version": "2.8.4", 65 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", 66 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", 67 | "requires": { 68 | "object-assign": "4.1.1", 69 | "vary": "1.1.2" 70 | } 71 | }, 72 | "debug": { 73 | "version": "2.6.9", 74 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 75 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 76 | "requires": { 77 | "ms": "2.0.0" 78 | } 79 | }, 80 | "depd": { 81 | "version": "1.1.1", 82 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 83 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 84 | }, 85 | "destroy": { 86 | "version": "1.0.4", 87 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 88 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 89 | }, 90 | "ee-first": { 91 | "version": "1.1.1", 92 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 93 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 94 | }, 95 | "encodeurl": { 96 | "version": "1.0.1", 97 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 98 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 99 | }, 100 | "escape-html": { 101 | "version": "1.0.3", 102 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 103 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 104 | }, 105 | "etag": { 106 | "version": "1.8.1", 107 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 108 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 109 | }, 110 | "express": { 111 | "version": "4.16.2", 112 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", 113 | "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", 114 | "requires": { 115 | "accepts": "1.3.4", 116 | "array-flatten": "1.1.1", 117 | "body-parser": "1.18.2", 118 | "content-disposition": "0.5.2", 119 | "content-type": "1.0.4", 120 | "cookie": "0.3.1", 121 | "cookie-signature": "1.0.6", 122 | "debug": "2.6.9", 123 | "depd": "1.1.1", 124 | "encodeurl": "1.0.1", 125 | "escape-html": "1.0.3", 126 | "etag": "1.8.1", 127 | "finalhandler": "1.1.0", 128 | "fresh": "0.5.2", 129 | "merge-descriptors": "1.0.1", 130 | "methods": "1.1.2", 131 | "on-finished": "2.3.0", 132 | "parseurl": "1.3.2", 133 | "path-to-regexp": "0.1.7", 134 | "proxy-addr": "2.0.2", 135 | "qs": "6.5.1", 136 | "range-parser": "1.2.0", 137 | "safe-buffer": "5.1.1", 138 | "send": "0.16.1", 139 | "serve-static": "1.13.1", 140 | "setprototypeof": "1.1.0", 141 | "statuses": "1.3.1", 142 | "type-is": "1.6.15", 143 | "utils-merge": "1.0.1", 144 | "vary": "1.1.2" 145 | } 146 | }, 147 | "finalhandler": { 148 | "version": "1.1.0", 149 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", 150 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", 151 | "requires": { 152 | "debug": "2.6.9", 153 | "encodeurl": "1.0.1", 154 | "escape-html": "1.0.3", 155 | "on-finished": "2.3.0", 156 | "parseurl": "1.3.2", 157 | "statuses": "1.3.1", 158 | "unpipe": "1.0.0" 159 | } 160 | }, 161 | "forwarded": { 162 | "version": "0.1.2", 163 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 164 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 165 | }, 166 | "fresh": { 167 | "version": "0.5.2", 168 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 169 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 170 | }, 171 | "http-errors": { 172 | "version": "1.6.2", 173 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 174 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 175 | "requires": { 176 | "depd": "1.1.1", 177 | "inherits": "2.0.3", 178 | "setprototypeof": "1.0.3", 179 | "statuses": "1.3.1" 180 | }, 181 | "dependencies": { 182 | "setprototypeof": { 183 | "version": "1.0.3", 184 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 185 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 186 | } 187 | } 188 | }, 189 | "iconv-lite": { 190 | "version": "0.4.19", 191 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 192 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 193 | }, 194 | "inherits": { 195 | "version": "2.0.3", 196 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 197 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 198 | }, 199 | "ipaddr.js": { 200 | "version": "1.5.2", 201 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", 202 | "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" 203 | }, 204 | "media-typer": { 205 | "version": "0.3.0", 206 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 207 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 208 | }, 209 | "merge-descriptors": { 210 | "version": "1.0.1", 211 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 212 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 213 | }, 214 | "methods": { 215 | "version": "1.1.2", 216 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 217 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 218 | }, 219 | "mime": { 220 | "version": "1.4.1", 221 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 222 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 223 | }, 224 | "mime-db": { 225 | "version": "1.30.0", 226 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", 227 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 228 | }, 229 | "mime-types": { 230 | "version": "2.1.17", 231 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", 232 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 233 | "requires": { 234 | "mime-db": "1.30.0" 235 | } 236 | }, 237 | "ms": { 238 | "version": "2.0.0", 239 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 240 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 241 | }, 242 | "negotiator": { 243 | "version": "0.6.1", 244 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 245 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 246 | }, 247 | "object-assign": { 248 | "version": "4.1.1", 249 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 250 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 251 | }, 252 | "on-finished": { 253 | "version": "2.3.0", 254 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 255 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 256 | "requires": { 257 | "ee-first": "1.1.1" 258 | } 259 | }, 260 | "parseurl": { 261 | "version": "1.3.2", 262 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 263 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 264 | }, 265 | "path-to-regexp": { 266 | "version": "0.1.7", 267 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 268 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 269 | }, 270 | "proxy-addr": { 271 | "version": "2.0.2", 272 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", 273 | "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", 274 | "requires": { 275 | "forwarded": "0.1.2", 276 | "ipaddr.js": "1.5.2" 277 | } 278 | }, 279 | "qs": { 280 | "version": "6.5.1", 281 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 282 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 283 | }, 284 | "range-parser": { 285 | "version": "1.2.0", 286 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 287 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 288 | }, 289 | "raw-body": { 290 | "version": "2.3.2", 291 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 292 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 293 | "requires": { 294 | "bytes": "3.0.0", 295 | "http-errors": "1.6.2", 296 | "iconv-lite": "0.4.19", 297 | "unpipe": "1.0.0" 298 | } 299 | }, 300 | "safe-buffer": { 301 | "version": "5.1.1", 302 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 303 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 304 | }, 305 | "send": { 306 | "version": "0.16.1", 307 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", 308 | "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", 309 | "requires": { 310 | "debug": "2.6.9", 311 | "depd": "1.1.1", 312 | "destroy": "1.0.4", 313 | "encodeurl": "1.0.1", 314 | "escape-html": "1.0.3", 315 | "etag": "1.8.1", 316 | "fresh": "0.5.2", 317 | "http-errors": "1.6.2", 318 | "mime": "1.4.1", 319 | "ms": "2.0.0", 320 | "on-finished": "2.3.0", 321 | "range-parser": "1.2.0", 322 | "statuses": "1.3.1" 323 | } 324 | }, 325 | "serve-static": { 326 | "version": "1.13.1", 327 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", 328 | "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", 329 | "requires": { 330 | "encodeurl": "1.0.1", 331 | "escape-html": "1.0.3", 332 | "parseurl": "1.3.2", 333 | "send": "0.16.1" 334 | } 335 | }, 336 | "setprototypeof": { 337 | "version": "1.1.0", 338 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 339 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 340 | }, 341 | "statuses": { 342 | "version": "1.3.1", 343 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 344 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 345 | }, 346 | "type-is": { 347 | "version": "1.6.15", 348 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 349 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 350 | "requires": { 351 | "media-typer": "0.3.0", 352 | "mime-types": "2.1.17" 353 | } 354 | }, 355 | "unpipe": { 356 | "version": "1.0.0", 357 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 358 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 359 | }, 360 | "utils-merge": { 361 | "version": "1.0.1", 362 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 363 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 364 | }, 365 | "vary": { 366 | "version": "1.1.2", 367 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 368 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 369 | } 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /day-04/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "day-04", 3 | "version": "1.0.0", 4 | "description": "Using the [day-02](../day-02) as a base.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "body-parser": "^1.18.2", 8 | "cors": "^2.8.4", 9 | "express": "^4.16.2" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "start": "node index.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+ssh://git@github.com/ohanhi/elm-quicks.git" 18 | }, 19 | "author": "", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/ohanhi/elm-quicks/issues" 23 | }, 24 | "homepage": "https://github.com/ohanhi/elm-quicks#readme" 25 | } 26 | -------------------------------------------------------------------------------- /day-05/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Char 4 | import Html exposing (..) 5 | import Html.Attributes exposing (..) 6 | import Html.Events exposing (..) 7 | import Http 8 | import Json.Decode exposing (list, string) 9 | import Json.Encode 10 | 11 | 12 | type Msg 13 | = UpdateCurrentPalindrome String 14 | | SavePalindrome 15 | | GotPalindromes (List String) 16 | | GotError Http.Error 17 | 18 | 19 | type alias Model = 20 | { currentPalindome : String 21 | , savedPalindromes : List String 22 | , error : String 23 | } 24 | 25 | 26 | model : Model 27 | model = 28 | { currentPalindome = "" 29 | , savedPalindromes = [] 30 | , error = "" 31 | } 32 | 33 | 34 | update : Msg -> Model -> ( Model, Cmd Msg ) 35 | update msg model = 36 | case msg of 37 | UpdateCurrentPalindrome text -> 38 | ( { model | currentPalindome = text }, Cmd.none ) 39 | 40 | SavePalindrome -> 41 | let 42 | newPalindromes = 43 | model.currentPalindome :: model.savedPalindromes 44 | in 45 | ( { model 46 | | currentPalindome = "" 47 | , savedPalindromes = newPalindromes 48 | } 49 | , Http.send handler (Http.post "http://localhost:3000/" (encode newPalindromes) decoder) 50 | ) 51 | 52 | GotPalindromes palindromes -> 53 | ( { model | savedPalindromes = palindromes }, Cmd.none ) 54 | 55 | GotError error -> 56 | ( { model | error = toString error }, Cmd.none ) 57 | 58 | 59 | encode : List String -> Http.Body 60 | encode palindromes = 61 | palindromes 62 | |> List.map Json.Encode.string 63 | |> Json.Encode.list 64 | |> Http.jsonBody 65 | 66 | 67 | view : Model -> Html Msg 68 | view model = 69 | div [] 70 | [ input 71 | [ value model.currentPalindome 72 | , onInput UpdateCurrentPalindrome 73 | ] 74 | [] 75 | , br [] [] 76 | , text (toString (isPalindrome model.currentPalindome)) 77 | , br [] [] 78 | , button 79 | [ onClick SavePalindrome ] 80 | [ text "Save the palindrome" ] 81 | , savedPalindromesView model.savedPalindromes 82 | , br [] [] 83 | , text model.error 84 | ] 85 | 86 | 87 | savedPalindromesView : List String -> Html msg 88 | savedPalindromesView palindromes = 89 | palindromes 90 | |> List.map palindromeView 91 | |> ul [] 92 | 93 | 94 | palindromeView : String -> Html msg 95 | palindromeView palindrome = 96 | li [] [ text palindrome ] 97 | 98 | 99 | init : ( Model, Cmd Msg ) 100 | init = 101 | ( model 102 | , Http.send handler (Http.get "http://localhost:3000/" decoder) 103 | ) 104 | 105 | 106 | decoder : Json.Decode.Decoder (List String) 107 | decoder = 108 | Json.Decode.list string 109 | 110 | 111 | handler : Result Http.Error (List String) -> Msg 112 | handler result = 113 | case result of 114 | Ok palindromes -> 115 | GotPalindromes palindromes 116 | 117 | Err error -> 118 | GotError error 119 | 120 | 121 | isPalindrome : String -> Bool 122 | isPalindrome input = 123 | let 124 | characters = 125 | String.toList (String.toLower input) 126 | 127 | justLetters = 128 | List.filter Char.isLower characters 129 | 130 | lettersInReverse = 131 | List.reverse justLetters 132 | in 133 | justLetters == lettersInReverse 134 | 135 | 136 | subscriptions : Model -> Sub Msg 137 | subscriptions model = 138 | Sub.none 139 | 140 | 141 | main : Program Never Model Msg 142 | main = 143 | Html.program 144 | { init = init 145 | , view = view 146 | , update = update 147 | , subscriptions = subscriptions 148 | } 149 | -------------------------------------------------------------------------------- /day-05/README.md: -------------------------------------------------------------------------------- 1 | # Day 5: Refactoring is nice! 2 | 3 | Using the [day-04](../day-04) as a base. 4 | -------------------------------------------------------------------------------- /day-05/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 13 | "elm-lang/http": "1.0.0 <= v < 2.0.0" 14 | }, 15 | "elm-version": "0.18.0 <= v < 0.19.0" 16 | } 17 | -------------------------------------------------------------------------------- /day-05/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | 5 | const app = express() 6 | app.use(cors()) 7 | 8 | let db = [] 9 | 10 | app.get('/', (req, res) => res.json(db)) 11 | // app.get('/', (req, res) => res.sendStatus(500)) 12 | app.post('/', bodyParser.json(), (req, res) => { 13 | db = req.body 14 | res.json(db) 15 | }) 16 | 17 | module.exports = app.listen(3000, () => { 18 | console.log('Listening on 3000') 19 | }) 20 | -------------------------------------------------------------------------------- /day-05/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "day-04", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.4", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", 10 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 11 | "requires": { 12 | "mime-types": "2.1.17", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.18.2", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 24 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 25 | "requires": { 26 | "bytes": "3.0.0", 27 | "content-type": "1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "1.1.1", 30 | "http-errors": "1.6.2", 31 | "iconv-lite": "0.4.19", 32 | "on-finished": "2.3.0", 33 | "qs": "6.5.1", 34 | "raw-body": "2.3.2", 35 | "type-is": "1.6.15" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.0.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 41 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.2", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 46 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 47 | }, 48 | "content-type": { 49 | "version": "1.0.4", 50 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 51 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 52 | }, 53 | "cookie": { 54 | "version": "0.3.1", 55 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 56 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 57 | }, 58 | "cookie-signature": { 59 | "version": "1.0.6", 60 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 61 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 62 | }, 63 | "cors": { 64 | "version": "2.8.4", 65 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", 66 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", 67 | "requires": { 68 | "object-assign": "4.1.1", 69 | "vary": "1.1.2" 70 | } 71 | }, 72 | "debug": { 73 | "version": "2.6.9", 74 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 75 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 76 | "requires": { 77 | "ms": "2.0.0" 78 | } 79 | }, 80 | "depd": { 81 | "version": "1.1.1", 82 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 83 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 84 | }, 85 | "destroy": { 86 | "version": "1.0.4", 87 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 88 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 89 | }, 90 | "ee-first": { 91 | "version": "1.1.1", 92 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 93 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 94 | }, 95 | "encodeurl": { 96 | "version": "1.0.1", 97 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 98 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 99 | }, 100 | "escape-html": { 101 | "version": "1.0.3", 102 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 103 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 104 | }, 105 | "etag": { 106 | "version": "1.8.1", 107 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 108 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 109 | }, 110 | "express": { 111 | "version": "4.16.2", 112 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", 113 | "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", 114 | "requires": { 115 | "accepts": "1.3.4", 116 | "array-flatten": "1.1.1", 117 | "body-parser": "1.18.2", 118 | "content-disposition": "0.5.2", 119 | "content-type": "1.0.4", 120 | "cookie": "0.3.1", 121 | "cookie-signature": "1.0.6", 122 | "debug": "2.6.9", 123 | "depd": "1.1.1", 124 | "encodeurl": "1.0.1", 125 | "escape-html": "1.0.3", 126 | "etag": "1.8.1", 127 | "finalhandler": "1.1.0", 128 | "fresh": "0.5.2", 129 | "merge-descriptors": "1.0.1", 130 | "methods": "1.1.2", 131 | "on-finished": "2.3.0", 132 | "parseurl": "1.3.2", 133 | "path-to-regexp": "0.1.7", 134 | "proxy-addr": "2.0.2", 135 | "qs": "6.5.1", 136 | "range-parser": "1.2.0", 137 | "safe-buffer": "5.1.1", 138 | "send": "0.16.1", 139 | "serve-static": "1.13.1", 140 | "setprototypeof": "1.1.0", 141 | "statuses": "1.3.1", 142 | "type-is": "1.6.15", 143 | "utils-merge": "1.0.1", 144 | "vary": "1.1.2" 145 | } 146 | }, 147 | "finalhandler": { 148 | "version": "1.1.0", 149 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", 150 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", 151 | "requires": { 152 | "debug": "2.6.9", 153 | "encodeurl": "1.0.1", 154 | "escape-html": "1.0.3", 155 | "on-finished": "2.3.0", 156 | "parseurl": "1.3.2", 157 | "statuses": "1.3.1", 158 | "unpipe": "1.0.0" 159 | } 160 | }, 161 | "forwarded": { 162 | "version": "0.1.2", 163 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 164 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 165 | }, 166 | "fresh": { 167 | "version": "0.5.2", 168 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 169 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 170 | }, 171 | "http-errors": { 172 | "version": "1.6.2", 173 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 174 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 175 | "requires": { 176 | "depd": "1.1.1", 177 | "inherits": "2.0.3", 178 | "setprototypeof": "1.0.3", 179 | "statuses": "1.3.1" 180 | }, 181 | "dependencies": { 182 | "setprototypeof": { 183 | "version": "1.0.3", 184 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 185 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 186 | } 187 | } 188 | }, 189 | "iconv-lite": { 190 | "version": "0.4.19", 191 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 192 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 193 | }, 194 | "inherits": { 195 | "version": "2.0.3", 196 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 197 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 198 | }, 199 | "ipaddr.js": { 200 | "version": "1.5.2", 201 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", 202 | "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" 203 | }, 204 | "media-typer": { 205 | "version": "0.3.0", 206 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 207 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 208 | }, 209 | "merge-descriptors": { 210 | "version": "1.0.1", 211 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 212 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 213 | }, 214 | "methods": { 215 | "version": "1.1.2", 216 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 217 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 218 | }, 219 | "mime": { 220 | "version": "1.4.1", 221 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 222 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 223 | }, 224 | "mime-db": { 225 | "version": "1.30.0", 226 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", 227 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 228 | }, 229 | "mime-types": { 230 | "version": "2.1.17", 231 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", 232 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 233 | "requires": { 234 | "mime-db": "1.30.0" 235 | } 236 | }, 237 | "ms": { 238 | "version": "2.0.0", 239 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 240 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 241 | }, 242 | "negotiator": { 243 | "version": "0.6.1", 244 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 245 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 246 | }, 247 | "object-assign": { 248 | "version": "4.1.1", 249 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 250 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 251 | }, 252 | "on-finished": { 253 | "version": "2.3.0", 254 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 255 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 256 | "requires": { 257 | "ee-first": "1.1.1" 258 | } 259 | }, 260 | "parseurl": { 261 | "version": "1.3.2", 262 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 263 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 264 | }, 265 | "path-to-regexp": { 266 | "version": "0.1.7", 267 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 268 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 269 | }, 270 | "proxy-addr": { 271 | "version": "2.0.2", 272 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", 273 | "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", 274 | "requires": { 275 | "forwarded": "0.1.2", 276 | "ipaddr.js": "1.5.2" 277 | } 278 | }, 279 | "qs": { 280 | "version": "6.5.1", 281 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 282 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 283 | }, 284 | "range-parser": { 285 | "version": "1.2.0", 286 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 287 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 288 | }, 289 | "raw-body": { 290 | "version": "2.3.2", 291 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 292 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 293 | "requires": { 294 | "bytes": "3.0.0", 295 | "http-errors": "1.6.2", 296 | "iconv-lite": "0.4.19", 297 | "unpipe": "1.0.0" 298 | } 299 | }, 300 | "safe-buffer": { 301 | "version": "5.1.1", 302 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 303 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 304 | }, 305 | "send": { 306 | "version": "0.16.1", 307 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", 308 | "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", 309 | "requires": { 310 | "debug": "2.6.9", 311 | "depd": "1.1.1", 312 | "destroy": "1.0.4", 313 | "encodeurl": "1.0.1", 314 | "escape-html": "1.0.3", 315 | "etag": "1.8.1", 316 | "fresh": "0.5.2", 317 | "http-errors": "1.6.2", 318 | "mime": "1.4.1", 319 | "ms": "2.0.0", 320 | "on-finished": "2.3.0", 321 | "range-parser": "1.2.0", 322 | "statuses": "1.3.1" 323 | } 324 | }, 325 | "serve-static": { 326 | "version": "1.13.1", 327 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", 328 | "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", 329 | "requires": { 330 | "encodeurl": "1.0.1", 331 | "escape-html": "1.0.3", 332 | "parseurl": "1.3.2", 333 | "send": "0.16.1" 334 | } 335 | }, 336 | "setprototypeof": { 337 | "version": "1.1.0", 338 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 339 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 340 | }, 341 | "statuses": { 342 | "version": "1.3.1", 343 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 344 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 345 | }, 346 | "type-is": { 347 | "version": "1.6.15", 348 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 349 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 350 | "requires": { 351 | "media-typer": "0.3.0", 352 | "mime-types": "2.1.17" 353 | } 354 | }, 355 | "unpipe": { 356 | "version": "1.0.0", 357 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 358 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 359 | }, 360 | "utils-merge": { 361 | "version": "1.0.1", 362 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 363 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 364 | }, 365 | "vary": { 366 | "version": "1.1.2", 367 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 368 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 369 | } 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /day-05/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "day-05", 3 | "version": "1.0.0", 4 | "description": "Using the [day-04](../day-04) as a base.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "body-parser": "^1.18.2", 8 | "cors": "^2.8.4", 9 | "express": "^4.16.2" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "start": "node index.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+ssh://git@github.com/ohanhi/elm-quicks.git" 18 | }, 19 | "author": "", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/ohanhi/elm-quicks/issues" 23 | }, 24 | "homepage": "https://github.com/ohanhi/elm-quicks#readme" 25 | } 26 | -------------------------------------------------------------------------------- /day-06/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Events exposing (onClick) 5 | 6 | 7 | type Msg 8 | = FruitSelected String 9 | | FruitDeselected 10 | 11 | 12 | type alias Model = 13 | { fruits : List String 14 | , selectedFruit : Maybe String 15 | } 16 | 17 | 18 | model : Model 19 | model = 20 | { fruits = [ "Banana", "Orange", "Kiwi" ] 21 | , selectedFruit = Nothing 22 | } 23 | 24 | 25 | update : Msg -> Model -> Model 26 | update msg model = 27 | case msg of 28 | FruitSelected fruit -> 29 | { model | selectedFruit = Just fruit } 30 | 31 | FruitDeselected -> 32 | { model | selectedFruit = Nothing } 33 | 34 | 35 | view : Model -> Html Msg 36 | view model = 37 | div [] 38 | [ button [ onClick (FruitSelected "Banana") ] [ text "Banana" ] 39 | , button [ onClick (FruitSelected "Orange") ] [ text "Orange" ] 40 | , button [ onClick (FruitSelected "Kiwi") ] [ text "Kiwi" ] 41 | , button [ onClick FruitDeselected ] [ text "Deselect" ] 42 | , br [] [] 43 | , case model.selectedFruit of 44 | Just fruit -> 45 | text ("Selected fruit: " ++ fruit) 46 | 47 | Nothing -> 48 | text "No fruit selected" 49 | ] 50 | 51 | 52 | main : Program Never Model Msg 53 | main = 54 | beginnerProgram 55 | { model = model 56 | , update = update 57 | , view = view 58 | } 59 | -------------------------------------------------------------------------------- /day-06/MainWithOurType.elm: -------------------------------------------------------------------------------- 1 | module MainWithOurType exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Events exposing (onClick) 5 | 6 | 7 | type MaybeFruit 8 | = OneFruit String 9 | | TwoFruits String String 10 | | NoFruit 11 | 12 | 13 | type Msg 14 | = FruitSelected String 15 | | FruitDeselected 16 | 17 | 18 | type alias Model = 19 | { fruits : List String 20 | , selectedFruit : MaybeFruit 21 | } 22 | 23 | 24 | model : Model 25 | model = 26 | { fruits = [ "Banana", "Orange", "Kiwi" ] 27 | , selectedFruit = NoFruit 28 | } 29 | 30 | 31 | update : Msg -> Model -> Model 32 | update msg model = 33 | case msg of 34 | FruitSelected fruit -> 35 | { model | selectedFruit = updateFruits model.selectedFruit fruit } 36 | 37 | FruitDeselected -> 38 | { model | selectedFruit = NoFruit } 39 | 40 | 41 | updateFruits : MaybeFruit -> String -> MaybeFruit 42 | updateFruits maybeFruit newFruit = 43 | case maybeFruit of 44 | OneFruit oldFruit -> 45 | TwoFruits oldFruit newFruit 46 | 47 | TwoFruits oldFruit oldFruit2 -> 48 | TwoFruits oldFruit2 newFruit 49 | 50 | NoFruit -> 51 | OneFruit newFruit 52 | 53 | 54 | view : Model -> Html Msg 55 | view model = 56 | div [] 57 | [ button [ onClick (FruitSelected "Banana") ] [ text "Banana" ] 58 | , button [ onClick (FruitSelected "Orange") ] [ text "Orange" ] 59 | , button [ onClick (FruitSelected "Kiwi") ] [ text "Kiwi" ] 60 | , button [ onClick FruitDeselected ] [ text "Deselect" ] 61 | , br [] [] 62 | , case model.selectedFruit of 63 | OneFruit fruit -> 64 | text ("Selected fruit: " ++ fruit) 65 | 66 | TwoFruits fruit1 fruit2 -> 67 | text ("Selected fruits: " ++ fruit1 ++ " and " ++ fruit2) 68 | 69 | NoFruit -> 70 | text "No fruit selected" 71 | ] 72 | 73 | 74 | main : Program Never Model Msg 75 | main = 76 | beginnerProgram 77 | { model = model 78 | , update = update 79 | , view = view 80 | } 81 | -------------------------------------------------------------------------------- /day-06/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 13 | }, 14 | "elm-version": "0.18.0 <= v < 0.19.0" 15 | } 16 | --------------------------------------------------------------------------------