Update part8 and part9

This commit is contained in:
Richard Feldman
2016-04-03 08:50:35 -07:00
parent dfbff3f448
commit 63e357f146
11 changed files with 176 additions and 123 deletions

View File

@@ -1,18 +1,19 @@
module ElmHub (..) where
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Events exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode.Pipeline exposing (..)
import Json.Encode
import Signal exposing (Address)
searchFeed : Address String -> String -> Task x Action
searchFeed : Address String -> String -> Effects Action
searchFeed address query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
@@ -23,13 +24,15 @@ searchFeed address query =
++ query
++ "+language:elm&sort=stars&order=desc"
-- These only talk to JavaScript ports now. They don't
-- These only talk to JavaScript ports now. They never result in Actions
-- actually do any actions themselves.
task =
Signal.send address query
|> Task.map (\_ -> DoNothing)
performAction
(\_ -> DoNothing)
(\_ -> DoNothing)
(Signal.send address query)
in
Task.onError task (\_ -> Task.succeed DoNothing)
Effects.task task
responseDecoder : Decoder (List SearchResult)
@@ -39,16 +42,40 @@ responseDecoder =
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
Json.Decode.object3
SearchResult
("id" := Json.Decode.int)
("full_name" := Json.Decode.string)
("stargazers_count" := Json.Decode.int)
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
{-| Note: this will be a standard function in the next release of Elm.
Example:
type Action =
HandleResponse String | HandleError Http.Error
performAction
(\responseString -> HandleResponse responseString)
(\httpError -> HandleError httpError)
(Http.getString "https://google.com?q=something")
-}
performAction : (a -> b) -> (y -> b) -> Task y a -> Task x b
performAction successToAction errorToAction task =
let
successTask =
Task.map successToAction task
in
Task.onError successTask (\err -> Task.succeed (errorToAction err))
type alias Model =
{ query : String
, results : List SearchResult
, errorMessage : Maybe String
}
@@ -67,6 +94,7 @@ initialModel : Model
initialModel =
{ query = "tutorial"
, results = []
, errorMessage = Nothing
}
@@ -81,12 +109,23 @@ view address model =
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul
[ class "results" ]
(List.map (viewSearchResult address) model.results)
]
viewErrorMessage : Maybe String -> Html
viewErrorMessage errorMessage =
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
Nothing ->
text ""
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
@@ -114,6 +153,7 @@ type Action
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
| SetErrorMessage (Maybe String)
| DoNothing
@@ -121,17 +161,16 @@ update : Address String -> Action -> Model -> ( Model, Effects Action )
update searchAddress action model =
case action of
Search ->
( model, Effects.task (searchFeed searchAddress model.query) )
( model, searchFeed searchAddress model.query )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
newModel =
{ model | results = results }
in
( newModel, Effects.none )
( { model | results = results }, Effects.none )
SetErrorMessage errorMessage ->
( { model | errorMessage = errorMessage }, Effects.none )
DeleteById idToHide ->
let

View File

@@ -20,7 +20,7 @@ app =
StartApp.start
{ view = view
, update = update search.address
, init = ( initialModel, Effects.task (searchFeed search.address initialModel.query) )
, init = ( initialModel, searchFeed search.address initialModel.query )
, inputs = [ responseActions ]
}
@@ -47,12 +47,11 @@ responseActions =
decodeGithubResponse : Json.Encode.Value -> Action
decodeGithubResponse value =
case Json.Decode.decodeValue responseDecoder value of
Ok results ->
SetResults results
Err _ ->
DoNothing
-- TODO use Json.Decode.DecodeValue to decode the response into an Action.
--
-- Hint: look at ElmHub.elm, specifically the definition of Action and
-- the deefinition of responseDecoder
SetErrorMessage (Just "TODO decode the response!")
port githubResponse : Signal Json.Encode.Value

View File

@@ -4,10 +4,12 @@
"repository": "https://github.com/rtfeldman/elm-workshop.git",
"license": "BSD-3-Clause",
"source-directories": [
".", ".."
".",
".."
],
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",

View File

@@ -1,20 +1,21 @@
module ElmHub (..) where
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Attributes exposing (class, target, href, property)
import Html.Events exposing (..)
import Http
import Auth
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Decode.Pipeline exposing (..)
import Json.Encode
import Signal exposing (Address)
import Dict exposing (Dict)
searchFeed : String -> Task x Action
searchFeed query =
searchFeed : Address String -> String -> Effects Action
searchFeed address query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
@@ -24,11 +25,15 @@ searchFeed query =
++ query
++ "+language:elm&sort=stars&order=desc"
-- These only talk to JavaScript ports now. They never result in Actions
-- actually do any actions themselves.
task =
Http.get responseDecoder url
|> Task.map SetResults
performAction
(\_ -> DoNothing)
(\_ -> DoNothing)
(Signal.send address query)
in
Task.onError task (\_ -> Task.succeed (SetResults []))
Effects.task task
responseDecoder : Decoder (List SearchResult)
@@ -38,16 +43,40 @@ responseDecoder =
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
Json.Decode.object3
SearchResult
("id" := Json.Decode.int)
("full_name" := Json.Decode.string)
("stargazers_count" := Json.Decode.int)
decode SearchResult
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
{-| Note: this will be a standard function in the next release of Elm.
Example:
type Action =
HandleResponse String | HandleError Http.Error
performAction
(\responseString -> HandleResponse responseString)
(\httpError -> HandleError httpError)
(Http.getString "https://google.com?q=something")
-}
performAction : (a -> b) -> (y -> b) -> Task y a -> Task x b
performAction successToAction errorToAction task =
let
successTask =
Task.map successToAction task
in
Task.onError successTask (\err -> Task.succeed (errorToAction err))
type alias Model =
{ query : String
, results : Dict ResultId SearchResult
, errorMessage : Maybe String
}
@@ -66,6 +95,7 @@ initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
, errorMessage = Nothing
}
@@ -119,13 +149,15 @@ type Action
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
| SetErrorMessage (Maybe String)
| DoNothing
update : Action -> Model -> ( Model, Effects Action )
update action model =
update : Address String -> Action -> Model -> ( Model, Effects Action )
update searchAddress action model =
case action of
Search ->
( model, Effects.task (searchFeed model.query) )
( model, searchFeed searchAddress model.query )
SetQuery query ->
( { model | query = query }, Effects.none )
@@ -142,3 +174,9 @@ update action model =
DeleteById id ->
-- TODO delete the result with the given id
( model, Effects.none )
SetErrorMessage errorMessage ->
( { model | errorMessage = errorMessage }, Effects.none )
DoNothing ->
( model, Effects.none )

View File

@@ -5,6 +5,9 @@ import ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
import Signal
import Json.Encode
import Json.Decode
main : Signal Html
@@ -16,12 +19,40 @@ app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = []
, update = update search.address
, init = ( initialModel, searchFeed search.address initialModel.query )
, inputs = [ responseActions ]
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
search : Signal.Mailbox String
search =
Signal.mailbox ""
port githubSearch : Signal String
port githubSearch =
search.signal
responseActions : Signal Action
responseActions =
Signal.map decodeGithubResponse githubResponse
decodeGithubResponse : Json.Encode.Value -> Action
decodeGithubResponse value =
case Json.Decode.decodeValue responseDecoder value of
Ok results ->
SetResults results
Err message ->
SetErrorMessage (Just message)
port githubResponse : Signal Json.Encode.Value

View File

@@ -4,10 +4,12 @@
"repository": "https://github.com/rtfeldman/elm-workshop.git",
"license": "BSD-3-Clause",
"source-directories": [
".", ".."
".",
".."
],
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.0.0 <= v < 2.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",

2
part9/github.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -4,23 +4,34 @@
<head>
<meta charset="UTF-8">
<title>ElmHub</title>
<script type="text/javascript" src="github.js"></script>
<script type="text/javascript" src="elm.js"></script>
<!-- Uncomment the below line to enable elm-reactor support. -->
<!-- <script type="text/javascript" src="/_reactor/debug.js"></script> -->
<link rel="stylesheet" href="style.css">
<link rel="icon" type="image/png" href="elm-hub.png">
</head>
<body>
<div id="elm-landing-pad"></div>
</body>
<script type="text/javascript">
var app = Elm.fullscreen(Elm.Main, {});
// documentation: https://github.com/michael/github
var github = new Github();
// Uncomment this line and comment out the above to enable elm-reactor support.
// var app = Elm.fullscreenDebug("ElmHub", "Main.elm");
var app = Elm.embed(
Elm.Main,
document.getElementById("elm-landing-pad"),
{githubResponse: []});
function searchGithub(query) {
var search = github.getSearch(query);
search.repositories({}, function (err, repositories) {
app.ports.githubResponse.send(repositories);
});
}
app.ports.githubSearch.subscribe(searchGithub);
</script>
</html>

View File

@@ -1,15 +0,0 @@
module Main where
import Signal exposing (Signal)
import ElmTest exposing (consoleRunner)
import Console exposing (IO, run)
import Task
import Tests
console : IO ()
console = consoleRunner Tests.all
port runner : Signal (Task.Task x ())
port runner = run console

View File

@@ -1,35 +0,0 @@
module Tests (..) where
import ElmTest exposing (..)
import ElmHub exposing (responseDecoder)
import Json.Decode exposing (decodeString)
all : Test
all =
suite
"Decoding responses from GitHub"
[ test "they can decode empty responses"
<| let
emptyResponse =
"""{ "items": [] }"""
in
assertEqual
(decodeString responseDecoder emptyResponse)
(Ok [])
, test "they can decode responses with results in them"
<| let
response =
"""{ "items": [
{ "id": 5, "full_name": "foo", "stargazers_count": 42 },
{ "id": 3, "full_name": "bar", "stargazers_count": 77 }
] }"""
in
assertEqual
(decodeString responseDecoder response)
(Ok
[ { id = 5, name = "foo", stars = 42 }
, { id = 3, name = "bar", stars = 77 }
]
)
]

View File

@@ -1,21 +0,0 @@
{
"version": "1.0.0",
"summary": "Like GitHub, but for Elm stuff.",
"repository": "https://github.com/rtfeldman/elm-workshop.git",
"license": "BSD-3-Clause",
"source-directories": [
".",
".."
],
"exposed-modules": [],
"dependencies": {
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0",
"laszlopandy/elm-console": "1.0.3 <= v < 2.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
}