Revise tests

Link to fuzz test docs and Fuzzer docs

Move part9 to be part12

Update part11

Update 12, and some other Mains

Rearrange things, drop 2 modules

Add a new part12

Fix READMEs

Move some things up a directory

Update part11

Use ! []

Update parts7-9

Fix part12g

Swap part11 and part12

Fix readmes for part11 and part12

Add HtmlRunner to part8

Update part8 and part9 READMEs

rm part10/test
This commit is contained in:
Richard Feldman
2016-06-26 01:00:27 -07:00
parent 00e641d186
commit b557ee0842
45 changed files with 481 additions and 883 deletions

View File

@@ -3,42 +3,37 @@ module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.Lazy exposing (..)
import Html.App
import Http
import Auth
import Task exposing (Task)
import Json.Decode exposing (Decoder)
import Json.Encode
import Dict exposing (Dict)
import SearchResult exposing (ResultId)
searchFeed : String -> Task x Msg
searchFeed : String -> Cmd Msg
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?access_token="
++ Auth.token
++ "&q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
Task.perform HandleSearchError HandleSearchResponse (Http.get responseDecoder url)
responseDecoder : Decoder (List SearchResult.Model)
responseDecoder =
"items" := Json.Decode.list SearchResult.decoder
Json.Decode.at [ "items" ] (Json.Decode.list SearchResult.decoder)
type alias Model =
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
, results : Dict ResultId SearchResult.Model
, errorMessage : Maybe String
}
@@ -46,6 +41,7 @@ initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
, errorMessage = Nothing
}
@@ -58,67 +54,85 @@ view model =
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul [ class "results" ] (viewSearchResults model.results)
]
viewSearchResults : Dict ResultId SearchResult.Model -> List Html
viewErrorMessage : Maybe String -> Html a
viewErrorMessage errorMessage =
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
Nothing ->
text ""
viewSearchResults : Dict ResultId SearchResult.Model -> List (Html Msg)
viewSearchResults results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (viewSearchResult address)
|> List.map viewSearchResult
viewSearchResult : SearchResult.Model -> Html Msg
viewSearchResult result =
SearchResult.view (Signal.forwardTo address (UpdateSearchResult result.id))
result
result
|> SearchResult.view
|> Html.App.map (UpdateSearchResult result.id)
type Msg
= Search
| SetQuery String
| SetResults (List SearchResult.Model)
| UpdateSearchResult ResultId SearchResult.Msg
| HandleSearchResponse (List SearchResult.Model)
| HandleSearchError Http.Error
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
model ! [ searchFeed model.query ]
SetQuery query ->
( { model | query = query }, Cmd.none )
{ model | query = query, errorMessage = Nothing } ! []
SetResults results ->
HandleSearchError error ->
case error of
Http.UnexpectedPayload str ->
{ model | errorMessage = Just str } ! []
_ ->
{ model | errorMessage = Just "Error loading search results" } ! []
HandleSearchResponse results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById : Dict ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Cmd.none )
{ model | results = resultsById } ! []
UpdateSearchResult id childMsg ->
let
updated =
model.results
|> Dict.get id
|> Maybe.map (SearchResult.update childMsg)
in
case updated of
Nothing ->
( model, Cmd.none )
case Dict.get id model.results of
Nothing ->
model ! []
Just ( newChildModel, childEffects ) ->
let
effects =
Effects.map (UpdateSearchResult id) childEffects
Just childModel ->
let
( newChildModel, childCmd ) =
SearchResult.update childMsg childModel
newResults =
Dict.insert id newChildModel model.results
in
( { model | results = newResults }, effects )
cmd =
Cmd.map (UpdateSearchResult id) childCmd
newResults =
Dict.insert id newChildModel model.results
in
{ model | results = newResults } ! [ cmd ]

14
part12/ElmHub/Css.elm Normal file
View File

@@ -0,0 +1,14 @@
module ElmHub.Css (..) where
import Css exposing (..)
css =
stylesheet
[ ((.) "content")
[ width (px 960)
, margin2 zero auto
, padding (px 30)
, fontFamilies [ "Helvetica", "Arial", "serif" ]
]
]

View File

@@ -1,6 +1,7 @@
module Main exposing (..)
import ElmHub exposing (..)
import Html.App
main : Program Never
@@ -9,5 +10,5 @@ main =
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
, subscriptions = \_ -> Sub.none
}

View File

@@ -19,6 +19,12 @@ to fail; in that case, just run `elm-package install` again.)
elm-live Main.elm --open --output=elm.js
```
## Compiling CSS
```bash
elm css Stylesheets.elm
```
## References
* [Elm Architecture Tutorial](https://github.com/evancz/elm-architecture-tutorial)
* [Elm CSS documentation](http://package.elm-lang.org/packages/rtfeldman/elm-css/1.1.0/)

View File

@@ -35,25 +35,25 @@ decoder =
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
-- TODO implement Expand and Collapse logic
( model, Cmd.none )
case msg of
Expand ->
{ model | expanded = True } ! []
Collapse ->
{ model | expanded = False } ! []
view : Model -> Html Msg
view model =
li [] <|
if model.expanded then
li []
<| if model.expanded then
[ span [ class "star-count" ] [ text (toString model.stars) ]
, a [ href ("https://github.com/" ++ model.name), target "_blank" ]
[ text model.name ]
, button
-- TODO when the user clicks, send a Collapse action
[ class "hide-result" ]
, button [ class "hide-result", onClick Collapse ]
[ text "X" ]
]
else
[ button
-- TODO when the user clicks, send an Expand action
[ class "expand-result" ]
else
[ button [ class "expand-result", onClick Expand ]
[ text "Show" ]
]

10
part12/Stylesheets.elm Normal file
View File

@@ -0,0 +1,10 @@
module Stylesheets (..) where
import Css.File exposing (..)
import ElmHub.Css
port files : CssFileStructure
port files =
toFileStructure
[ ( "style.css", compile ElmHub.Css.css ) ]

View File

@@ -12,7 +12,8 @@
"NoRedInk/elm-decode-pipeline": "1.1.2 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
"evancz/elm-http": "3.0.1 <= v < 4.0.0",
"rtfeldman/elm-css": "3.1.0 <= v < 4.0.0"
},
"elm-version": "0.17.0 <= v < 0.18.0"
}
}

View File

@@ -1,92 +1,6 @@
.content {
width: 960px;
margin: 0 auto;
padding: 30px;
font-family: Helvetica, Arial, serif;
}
header {
position: relative;
padding: 6px 12px;
height: 36px;
background-color: rgb(96, 181, 204);
}
h1 {
color: white;
font-weight: normal;
margin: 0;
}
.tagline {
color: #eee;
position: absolute;
right: 16px;
top: 12px;
font-size: 24px;
font-style: italic;
}
.results {
list-style-image: url('http://img-cache.cdn.gaiaonline.com/76bd5c99d8f2236e9d3672510e933fdf/http://i278.photobucket.com/albums/kk81/d3m3nt3dpr3p/Tiny-Star-Icon.png');
list-style-position: inside;
padding: 0;
}
.results li {
font-size: 18px;
margin-bottom: 16px;
}
.star-count {
font-weight: bold;
margin-right: 16px;
}
a {
color: rgb(96, 181, 204);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.search-query {
padding: 8px;
font-size: 24px;
margin-bottom: 18px;
margin-top: 36px;
}
.search-button {
padding: 8px 16px;
font-size: 24px;
color: white;
border: 1px solid #ccc;
background-color: rgb(96, 181, 204);
margin-left: 12px
}
.search-button:hover {
color: rgb(96, 181, 204);
background-color: white;
}
.hide-result {
background-color: transparent;
border: 0;
font-weight: bold;
font-size: 18px;
margin-left: 18px;
cursor: pointer;
}
.hide-result:hover {
color: rgb(96, 181, 204);
}
button:focus, input:focus {
outline: none;
width: 960px;
margin: 0 auto;
padding: 30px;
font-family: Helvetica, Arial, serif;
}

View File

@@ -12,6 +12,7 @@
"NoRedInk/elm-decode-pipeline": "1.1.2 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"rtfeldman/elm-css": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.17.0 <= v < 0.18.0"