Shift stages ahead one.

This commit is contained in:
Richard Feldman
2016-03-26 08:02:45 -07:00
parent dffa33f776
commit 236cd9057d
41 changed files with 440 additions and 443 deletions

View File

@@ -1,37 +0,0 @@
module Main (..) where
import Html exposing (..)
import Html.Attributes exposing (..)
model =
{ result =
{ id = 1
, name = "TheSeamau5/elm-checkerboardgrid-tutorial"
, stars = 66
}
}
view model =
div
[ class "content" ]
[ header
[]
[ -- TODO add the equivalent of <h1>ElmHub</h1> right before the tagline
span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, ul
[ class "results" ]
[ li
[]
[ span [ class "star-count" ] [{- TODO display the number of stars -}]
-- TODO use the model to put a link here that points to
-- https://github.com/TheSeamau5/elm-checkerboardgrid-tutorial
]
]
]
main =
view model

View File

@@ -4,81 +4,34 @@ import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
main =
view model
type alias Model =
{ query : String
, results : List SearchResult
}
type alias SearchResult =
{ id : ResultId
, name : String
, stars : Int
}
type alias ResultId =
Int
{- See https://developer.github.com/v3/search/#example -}
model : Model
model = model =
{ query = "tutorial" { result =
, results = { id = 1
[ { id = 1 , name = "TheSeamau5/elm-checkerboardgrid-tutorial"
, name = "TheSeamau5/elm-checkerboardgrid-tutorial" , stars = 66
, stars = 66 }
}
, { id = 2
, name = "grzegorzbalcerek/elm-by-example"
, stars = 41
}
, { id = 3
, name = "sporto/elm-tutorial-app"
, stars = 35
}
, { id = 4
, name = "jvoigtlaender/Elm-Tutorium"
, stars = 10
}
, { id = 5
, name = "sporto/elm-tutorial-assets"
, stars = 7
}
]
} }
view : Model -> Html
view model = view model =
div div
[ class "content" ] [ class "content" ]
[ header [ header
[] []
[ h1 [] [ text "ElmHub" ] [ -- TODO add the equivalent of <h1>ElmHub</h1> right before the tagline
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ] span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
] ]
, ul , ul
[ class "results" ] [ class "results" ]
[{- TODO use model.results and viewSearchResults to display results -}] [ li
[]
[ span [ class "star-count" ] [{- TODO display the number of stars -}]
-- TODO use the model to put a link here that points to
-- https://github.com/TheSeamau5/elm-checkerboardgrid-tutorial
]
]
] ]
viewSearchResult : SearchResult -> Html main =
viewSearchResult result = view model
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ]
, text result.name
]

View File

@@ -26,8 +26,12 @@ type Action
update : Action -> Model -> ( Model, Effects Action ) update : Action -> Model -> ( Model, Effects Action )
update action model = update action model =
-- TODO make expand and collapse work case action of
( model, Effects.none ) Expand ->
( { model | expanded = True }, Effects.none )
Collapse ->
( { model | expanded = False }, Effects.none )
view : Address Action -> Model -> Html view : Address Action -> Model -> Html
@@ -42,13 +46,11 @@ view address model =
] ]
[ text model.name ] [ text model.name ]
, button , button
-- TODO when the user clicks, send a Collapse action [ class "hide-result", onClick address Collapse ]
[ class "hide-result" ]
[ text "X" ] [ text "X" ]
] ]
else else
[ button [ button
-- TODO when the user clicks, send an Expand action [ class "expand-result", onClick address Expand ]
[ class "expand-result" ]
[ text "Show" ] [ text "Show" ]
] ]

27
stages/10/Main.elm Normal file
View File

@@ -0,0 +1,27 @@
module Main (..) where
import StartApp
import Component.ElmHub exposing (..)
import Effects exposing (Effects)
import Task exposing (Task)
import Html exposing (Html)
main : Signal Html
main =
app.html
app : StartApp.App Model
app =
StartApp.start
{ view = view
, update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = []
}
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks

View File

@@ -1,4 +1,4 @@
Stage 1 Stage 5
======= =======
## Installation ## Installation
@@ -15,3 +15,9 @@ to fail; in that case, just run `elm package install` again.)
```bash ```bash
elm live Main.elm --open -- --output=elm.js elm live Main.elm --open -- --output=elm.js
``` ```
## Compiling CSS
```bash
elm test css/Stylesheets.elm
```

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -9,7 +9,11 @@
"exposed-modules": [], "exposed-modules": [],
"dependencies": { "dependencies": {
"elm-lang/core": "3.0.0 <= v < 4.0.0", "elm-lang/core": "3.0.0 <= v < 4.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.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",
"rtfeldman/elm-css": "1.0.0 <= v < 2.0.0"
}, },
"elm-version": "0.16.0 <= v < 0.17.0" "elm-version": "0.16.0 <= v < 0.17.0"
} }

View File

@@ -20,7 +20,7 @@
var app = Elm.fullscreen(Elm.Main, {}); var app = Elm.fullscreen(Elm.Main, {});
// Uncomment this line and comment out the above to enable elm-reactor support. // Uncomment this line and comment out the above to enable elm-reactor support.
// var app = Elm.fullscreenDebug("ElmHub", "ElmHub.elm"); // var app = Elm.fullscreenDebug("ElmHub", "Main.elm");
</script> </script>
</html> </html>

64
stages/10/test/Tests.elm Normal file
View File

@@ -0,0 +1,64 @@
module Tests (..) where
import ElmTest exposing (..)
import ElmHub exposing (responseDecoder)
import Json.Decode as Decode
import Json.Encode as Encode
import Check exposing (Claim, Evidence, check, claim, that, is, for)
import Check.Producer exposing (..)
import Check.Test exposing (evidenceToTest)
import String
import ElmHub exposing (..)
import Random
all : Test
all =
suite
"Decoding responses from GitHub"
[ test "they can decode empty responses"
<| let
emptyResponse =
"""{ "items": [] }"""
in
assertEqual
(Decode.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
(Decode.decodeString responseDecoder response)
(Ok
[ { id = 5, name = "foo", stars = 42 }
, { id = 3, name = "bar", stars = 77 }
]
)
, (claim "they can decode individual search results"
`that` (\( id, name, stars ) -> encodeAndDecode id name stars)
`is` (\( id, name, stars ) -> Ok (SearchResult id name stars))
`for` tuple3 ( int, string, int )
)
|> check 100 defaultSeed
|> evidenceToTest
]
encodeAndDecode : Int -> String -> Int -> Result String SearchResult
encodeAndDecode id name stars =
[ ( "id", Encode.int id )
, ( "full_name", Encode.string name )
, ( "stargazers_count", Encode.int stars )
]
|> Encode.object
|> Encode.encode 0
|> Decode.decodeString searchResultDecoder
defaultSeed =
Random.initialSeed 42

View File

@@ -9,6 +9,7 @@
], ],
"exposed-modules": [], "exposed-modules": [],
"dependencies": { "dependencies": {
"NoRedInk/elm-check": "3.0.0 <= v < 4.0.0",
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0", "deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= 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-effects": "2.0.0 <= v < 3.0.0",

View File

@@ -2,17 +2,6 @@ module Main (..) where
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (..)
import StartApp.Simple as StartApp
import Signal exposing (Address)
main =
StartApp.start
{ view = view
, update = update
, model = initialModel
}
type alias Model = type alias Model =
@@ -32,12 +21,9 @@ type alias ResultId =
Int Int
{-| TODO add a type annotation to this function
{- See https://developer.github.com/v3/search/#example -} -}
model =
initialModel : Model
initialModel =
{ query = "tutorial" { query = "tutorial"
, results = , results =
[ { id = 1 [ { id = 1
@@ -64,8 +50,9 @@ initialModel =
} }
view : Address Action -> Model -> Html {-| TODO add a type annotation to this function
view address model = -}
view model =
div div
[ class "content" ] [ class "content" ]
[ header [ header
@@ -75,32 +62,24 @@ view address model =
] ]
, ul , ul
[ class "results" ] [ class "results" ]
(List.map (viewSearchResult address) model.results) [{- TODO use model.results and viewSearchResults to display results -}]
] ]
viewSearchResult : Address Action -> SearchResult -> Html {-| TODO add a type annotation to this function
viewSearchResult address result = -}
viewSearchResult result =
li li
[] []
[ span [ class "star-count" ] [ text (toString result.stars) ] [ span [ class "star-count" ] [ text (toString result.stars) ]
, a , a
[ href ("https://github.com/" ++ result.name), target "_blank" ] [ href ("https://github.com/" ++ result.name), target "_blank" ]
[ text result.name ] [ text result.name ]
, button , text result.name
-- TODO add an onClick handler that sends a DELETE_BY_ID action
[ class "hide-result" ]
[ text "X" ]
] ]
type alias Action = {-| TODO add a type annotation to this function
{ -- TODO implement this type alias -}
} main =
view model
update : Action -> Model -> Model
update action model =
-- TODO if we receive a DELETE_BY_ID action,
-- build a new model without the given ID present anymore.
model

View File

@@ -1,4 +1,4 @@
Stage 2 Stage 1
======= =======
## Installation ## Installation

View File

@@ -9,9 +9,7 @@
"exposed-modules": [], "exposed-modules": [],
"dependencies": { "dependencies": {
"elm-lang/core": "3.0.0 <= 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-html": "4.0.0 <= v < 5.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0"
}, },
"elm-version": "0.16.0 <= v < 0.17.0" "elm-version": "0.16.0 <= v < 0.17.0"
} }

View File

@@ -3,26 +3,15 @@ module Main (..) where
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (..) import Html.Events exposing (..)
import StartApp import StartApp.Simple as StartApp
import Task exposing (Task)
import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Encode
import Signal exposing (Address) import Signal exposing (Address)
main : Signal Html
main = main =
app.html
app : StartApp.App Model
app =
StartApp.start StartApp.start
{ view = view { view = view
, update = update , update = update
, init = ( initialModel, Effects.none ) , model = initialModel
, inputs = []
} }
@@ -43,10 +32,6 @@ type alias ResultId =
Int Int
{- See https://developer.github.com/v3/search/#example -}
initialModel : Model initialModel : Model
initialModel = initialModel =
{ query = "tutorial" { query = "tutorial"
@@ -84,46 +69,34 @@ view address model =
[ h1 [] [ text "ElmHub" ] [ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ] , span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
] ]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button" ] [ text "Search" ]
, ul , ul
[ class "results" ] [ class "results" ]
(List.map (viewSearchResult address) model.results) (List.map (viewSearchResult address) model.results)
] ]
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
defaultValue str =
property "defaultValue" (Json.Encode.string str)
viewSearchResult : Address Action -> SearchResult -> Html viewSearchResult : Address Action -> SearchResult -> Html
viewSearchResult address result = viewSearchResult address result =
li li
[] []
[ span [ class "star-count" ] [ text (toString result.stars) ] [ span [ class "star-count" ] [ text (toString result.stars) ]
, a , a
[ href ("https://github.com/" ++ result.name) [ href ("https://github.com/" ++ result.name), target "_blank" ]
, target "_blank"
]
[ text result.name ] [ text result.name ]
, button , button
-- TODO add an onClick handler that sends a DeleteById action -- TODO add an onClick handler that sends a DELETE_BY_ID action
[ class "hide-result" ] [ class "hide-result" ]
[ text "X" ] [ text "X" ]
] ]
type Action type alias Action =
= SetQuery String { -- TODO implement this type alias
| DeleteById ResultId }
update : Action -> Model -> ( Model, Effects Action ) update : Action -> Model -> Model
update action model = update action model =
-- TODO write a case-expression that makes SetQuery set the query -- TODO if we receive a DELETE_BY_ID action,
-- and DeleteById delete the appropriate result -- build a new model without the given ID present anymore.
( model, Effects.none ) model

View File

@@ -1,4 +1,4 @@
Stage 3 Stage 2
======= =======
## Installation ## Installation

View File

@@ -4,7 +4,6 @@ import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (..) import Html.Events exposing (..)
import StartApp import StartApp
import Http
import Task exposing (Task) import Task exposing (Task)
import Effects exposing (Effects) import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=)) import Json.Decode exposing (Decoder, (:=))
@@ -22,47 +21,11 @@ app =
StartApp.start StartApp.start
{ view = view { view = view
, update = update , update = update
, init = ( initialModel, Effects.task (searchFeed initialModel.query) ) , init = ( initialModel, Effects.none )
, inputs = [] , inputs = []
} }
port tasks : Signal (Task Effects.Never ())
port tasks =
app.tasks
searchFeed : String -> Task x Action
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
responseDecoder : Decoder (List SearchResult)
responseDecoder =
"items" := Json.Decode.list searchResultDecoder
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
Json.Decode.object3
SearchResult
-- See https://developer.github.com/v3/search/#example
("TODO what field goes here?" := Json.Decode.int)
("TODO what field goes here?" := Json.Decode.string)
("TODO what field goes here?" := Json.Decode.int)
type alias Model = type alias Model =
{ query : String { query : String
, results : List SearchResult , results : List SearchResult
@@ -80,10 +43,35 @@ type alias ResultId =
Int Int
{- See https://developer.github.com/v3/search/#example -}
initialModel : Model initialModel : Model
initialModel = initialModel =
{ query = "tutorial" { query = "tutorial"
, results = [] , results =
[ { id = 1
, name = "TheSeamau5/elm-checkerboardgrid-tutorial"
, stars = 66
}
, { id = 2
, name = "grzegorzbalcerek/elm-by-example"
, stars = 41
}
, { id = 3
, name = "sporto/elm-tutorial-app"
, stars = 35
}
, { id = 4
, name = "jvoigtlaender/Elm-Tutorium"
, stars = 10
}
, { id = 5
, name = "sporto/elm-tutorial-assets"
, stars = 7
}
]
} }
@@ -97,7 +85,7 @@ view address model =
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ] , span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
] ]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] [] , input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ] , button [ class "search-button" ] [ text "Search" ]
, ul , ul
[ class "results" ] [ class "results" ]
(List.map (viewSearchResult address) model.results) (List.map (viewSearchResult address) model.results)
@@ -123,40 +111,19 @@ viewSearchResult address result =
] ]
[ text result.name ] [ text result.name ]
, button , button
[ class "hide-result", onClick address (DeleteById result.id) ] -- TODO add an onClick handler that sends a DeleteById action
[ class "hide-result" ]
[ text "X" ] [ text "X" ]
] ]
type Action type Action
= Search = SetQuery String
| SetQuery String
| DeleteById ResultId | DeleteById ResultId
| SetResults (List SearchResult)
update : Action -> Model -> ( Model, Effects Action ) update : Action -> Model -> ( Model, Effects Action )
update action model = update action model =
case action of -- TODO write a case-expression that makes SetQuery set the query
Search -> -- and DeleteById delete the appropriate result
( model, Effects.task (searchFeed model.query) ) ( model, Effects.none )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
newModel =
{ model | results = results }
in
( newModel, Effects.none )
DeleteById idToHide ->
let
newResults =
List.filter (\{ id } -> id /= idToHide) model.results
newModel =
{ model | results = newResults }
in
( newModel, Effects.none )

View File

@@ -1,4 +1,4 @@
Stage 4 Stage 3
======= =======
## Installation ## Installation

View File

@@ -11,7 +11,6 @@
"elm-lang/core": "3.0.0 <= 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-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.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" "evancz/start-app": "2.0.0 <= v < 3.0.0"
}, },
"elm-version": "0.16.0 <= v < 0.17.0" "elm-version": "0.16.0 <= v < 0.17.0"

View File

@@ -1,10 +1,15 @@
module Main (..) where module Main (..) where
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import StartApp import StartApp
import ElmHub exposing (..) import Http
import Effects exposing (Effects)
import Task exposing (Task) import Task exposing (Task)
import Html exposing (Html) import Effects exposing (Effects)
import Json.Decode exposing (Decoder, (:=))
import Json.Encode
import Signal exposing (Address)
main : Signal Html main : Signal Html
@@ -25,3 +30,133 @@ app =
port tasks : Signal (Task Effects.Never ()) port tasks : Signal (Task Effects.Never ())
port tasks = port tasks =
app.tasks app.tasks
searchFeed : String -> Task x Action
searchFeed query =
let
-- See https://developer.github.com/v3/search/#example for how to customize!
url =
"https://api.github.com/search/repositories?q="
++ query
++ "+language:elm&sort=stars&order=desc"
task =
Http.get responseDecoder url
|> Task.map SetResults
in
Task.onError task (\_ -> Task.succeed (SetResults []))
responseDecoder : Decoder (List SearchResult)
responseDecoder =
"items" := Json.Decode.list searchResultDecoder
searchResultDecoder : Decoder SearchResult
searchResultDecoder =
Json.Decode.object3
SearchResult
-- See https://developer.github.com/v3/search/#example
("TODO what field goes here?" := Json.Decode.int)
("TODO what field goes here?" := Json.Decode.string)
("TODO what field goes here?" := Json.Decode.int)
type alias Model =
{ query : String
, results : List SearchResult
}
type alias SearchResult =
{ id : ResultId
, name : String
, stars : Int
}
type alias ResultId =
Int
initialModel : Model
initialModel =
{ query = "tutorial"
, results = []
}
view : Address Action -> Model -> Html
view address model =
div
[ class "content" ]
[ header
[]
[ h1 [] [ text "ElmHub" ]
, span [ class "tagline" ] [ text "Like GitHub, but for Elm things." ]
]
, input [ class "search-query", onInput address SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick address Search ] [ text "Search" ]
, ul
[ class "results" ]
(List.map (viewSearchResult address) model.results)
]
onInput address wrap =
on "input" targetValue (\val -> Signal.message address (wrap val))
defaultValue str =
property "defaultValue" (Json.Encode.string str)
viewSearchResult : Address Action -> SearchResult -> Html
viewSearchResult address result =
li
[]
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name)
, target "_blank"
]
[ text result.name ]
, button
[ class "hide-result", onClick address (DeleteById result.id) ]
[ text "X" ]
]
type Action
= Search
| SetQuery String
| DeleteById ResultId
| SetResults (List SearchResult)
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
Search ->
( model, Effects.task (searchFeed model.query) )
SetQuery query ->
( { model | query = query }, Effects.none )
SetResults results ->
let
newModel =
{ model | results = results }
in
( newModel, Effects.none )
DeleteById idToHide ->
let
newResults =
List.filter (\{ id } -> id /= idToHide) model.results
newModel =
{ model | results = newResults }
in
( newModel, Effects.none )

View File

@@ -1,4 +1,4 @@
Stage 5 Stage 4
======= =======
## Installation ## Installation
@@ -15,10 +15,3 @@ to fail; in that case, just run `elm package install` again.)
```bash ```bash
elm live Main.elm --open -- --output=elm.js elm live Main.elm --open -- --output=elm.js
``` ```
## Running Tests
```bash
cd test
elm test TestRunner.elm
```

View File

@@ -20,7 +20,7 @@
var app = Elm.fullscreen(Elm.Main, {}); var app = Elm.fullscreen(Elm.Main, {});
// Uncomment this line and comment out the above to enable elm-reactor support. // Uncomment this line and comment out the above to enable elm-reactor support.
// var app = Elm.fullscreenDebug("ElmHub", "Main.elm"); // var app = Elm.fullscreenDebug("ElmHub", "ElmHub.elm");
</script> </script>
</html> </html>

View File

@@ -1,34 +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)
({- TODO: what goes here? -})
, test "they can decode responses with results in them"
<| let
response =
"""{ "items": [
/* TODO: dummy JSON goes here */
] }"""
in
assertEqual
(decodeString responseDecoder response)
(Ok
[ { id = 5, name = "foo", stars = 42 }
, { id = 3, name = "bar", stars = 77 }
]
)
]

View File

@@ -20,6 +20,5 @@ elm live Main.elm --open -- --output=elm.js
```bash ```bash
cd test cd test
elm package install
elm test TestRunner.elm elm test TestRunner.elm
``` ```

View File

@@ -2,14 +2,7 @@ module Tests (..) where
import ElmTest exposing (..) import ElmTest exposing (..)
import ElmHub exposing (responseDecoder) import ElmHub exposing (responseDecoder)
import Json.Decode as Decode import Json.Decode exposing (decodeString)
import Json.Encode as Encode
import Check exposing (Claim, Evidence, check, claim, that, is, for)
import Check.Producer exposing (..)
import Check.Test exposing (evidenceToTest)
import String
import ElmHub exposing (..)
import Random
all : Test all : Test
@@ -22,43 +15,20 @@ all =
"""{ "items": [] }""" """{ "items": [] }"""
in in
assertEqual assertEqual
(Decode.decodeString responseDecoder emptyResponse) (decodeString responseDecoder emptyResponse)
(Ok []) ({- TODO: what goes here? -})
, test "they can decode responses with results in them" , test "they can decode responses with results in them"
<| let <| let
response = response =
"""{ "items": [ """{ "items": [
{ "id": 5, "full_name": "foo", "stargazers_count": 42 }, /* TODO: dummy JSON goes here */
{ "id": 3, "full_name": "bar", "stargazers_count": 77 }
] }""" ] }"""
in in
assertEqual assertEqual
(Decode.decodeString responseDecoder response) (decodeString responseDecoder response)
(Ok (Ok
[ { id = 5, name = "foo", stars = 42 } [ { id = 5, name = "foo", stars = 42 }
, { id = 3, name = "bar", stars = 77 } , { id = 3, name = "bar", stars = 77 }
] ]
) )
, (claim "they can decode individual search results"
`that` ({- TODO call encodeAndDecode -})
`is` (\( id, name, stars ) -> Ok (SearchResult id name stars))
`for` tuple3 ( int, string, int )
)
|> check 100 defaultSeed
|> evidenceToTest
] ]
encodeAndDecode : Int -> String -> Int -> Result String SearchResult
encodeAndDecode id name stars =
-- TODO: finish turning this into a JSON String,
-- then Decode it with searchResultDecoder
[ ( "id", Encode.int id )
, ( "full_name", Encode.string name )
, ( "stargazers_count", Encode.int stars )
]
|> Encode.object
defaultSeed =
Random.initialSeed 42

View File

@@ -9,7 +9,6 @@
], ],
"exposed-modules": [], "exposed-modules": [],
"dependencies": { "dependencies": {
"NoRedInk/elm-check": "3.0.0 <= v < 4.0.0",
"deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0", "deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0",
"elm-lang/core": "3.0.0 <= 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-effects": "2.0.0 <= v < 3.0.0",

View File

@@ -11,8 +11,8 @@ import Json.Encode
import Signal exposing (Address) import Signal exposing (Address)
searchFeed : Address String -> String -> Task x Action searchFeed : String -> Task x Action
searchFeed address query = searchFeed query =
let let
-- See https://developer.github.com/v3/search/#example for how to customize! -- See https://developer.github.com/v3/search/#example for how to customize!
url = url =
@@ -20,13 +20,11 @@ searchFeed address query =
++ query ++ query
++ "+language:elm&sort=stars&order=desc" ++ "+language:elm&sort=stars&order=desc"
-- These only talk to JavaScript ports now. They don't
-- actually do any actions themselves.
task = task =
Signal.send address query Http.get responseDecoder url
|> Task.map (\_ -> DoNothing) |> Task.map SetResults
in in
Task.onError task (\_ -> Task.succeed DoNothing) Task.onError task (\_ -> Task.succeed (SetResults []))
responseDecoder : Decoder (List SearchResult) responseDecoder : Decoder (List SearchResult)
@@ -113,14 +111,13 @@ type Action
| SetQuery String | SetQuery String
| DeleteById ResultId | DeleteById ResultId
| SetResults (List SearchResult) | SetResults (List SearchResult)
| DoNothing
update : Address String -> Action -> Model -> ( Model, Effects Action ) update : Action -> Model -> ( Model, Effects Action )
update searchAddress action model = update action model =
case action of case action of
Search -> Search ->
( model, Effects.task (searchFeed searchAddress model.query) ) ( model, Effects.task (searchFeed model.query) )
SetQuery query -> SetQuery query ->
( { model | query = query }, Effects.none ) ( { model | query = query }, Effects.none )
@@ -142,6 +139,3 @@ update searchAddress action model =
{ model | results = newResults } { model | results = newResults }
in in
( newModel, Effects.none ) ( newModel, Effects.none )
DoNothing ->
( model, Effects.none )

View File

@@ -5,9 +5,6 @@ import ElmHub exposing (..)
import Effects exposing (Effects) import Effects exposing (Effects)
import Task exposing (Task) import Task exposing (Task)
import Html exposing (Html) import Html exposing (Html)
import Signal
import Json.Encode
import Json.Decode
main : Signal Html main : Signal Html
@@ -19,40 +16,12 @@ app : StartApp.App Model
app = app =
StartApp.start StartApp.start
{ view = view { view = view
, update = update search.address , update = update
, init = ( initialModel, Effects.task (searchFeed search.address initialModel.query) ) , init = ( initialModel, Effects.task (searchFeed initialModel.query) )
, inputs = [ responseActions ] , inputs = []
} }
port tasks : Signal (Task Effects.Never ()) port tasks : Signal (Task Effects.Never ())
port tasks = port tasks =
app.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 _ ->
DoNothing
port githubResponse : Signal Json.Encode.Value

View File

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

View File

@@ -40,7 +40,7 @@ all =
] ]
) )
, (claim "they can decode individual search results" , (claim "they can decode individual search results"
`that` (\( id, name, stars ) -> encodeAndDecode id name stars) `that` ({- TODO call encodeAndDecode -})
`is` (\( id, name, stars ) -> Ok (SearchResult id name stars)) `is` (\( id, name, stars ) -> Ok (SearchResult id name stars))
`for` tuple3 ( int, string, int ) `for` tuple3 ( int, string, int )
) )
@@ -51,13 +51,13 @@ all =
encodeAndDecode : Int -> String -> Int -> Result String SearchResult encodeAndDecode : Int -> String -> Int -> Result String SearchResult
encodeAndDecode id name stars = encodeAndDecode id name stars =
-- TODO: finish turning this into a JSON String,
-- then Decode it with searchResultDecoder
[ ( "id", Encode.int id ) [ ( "id", Encode.int id )
, ( "full_name", Encode.string name ) , ( "full_name", Encode.string name )
, ( "stargazers_count", Encode.int stars ) , ( "stargazers_count", Encode.int stars )
] ]
|> Encode.object |> Encode.object
|> Encode.encode 0
|> Decode.decodeString searchResultDecoder
defaultSeed = defaultSeed =

View File

@@ -11,8 +11,8 @@ import Json.Encode
import Signal exposing (Address) import Signal exposing (Address)
searchFeed : String -> Task x Action searchFeed : Address String -> String -> Task x Action
searchFeed query = searchFeed address query =
let let
-- See https://developer.github.com/v3/search/#example for how to customize! -- See https://developer.github.com/v3/search/#example for how to customize!
url = url =
@@ -20,11 +20,13 @@ searchFeed query =
++ query ++ query
++ "+language:elm&sort=stars&order=desc" ++ "+language:elm&sort=stars&order=desc"
-- These only talk to JavaScript ports now. They don't
-- actually do any actions themselves.
task = task =
Http.get responseDecoder url Signal.send address query
|> Task.map SetResults |> Task.map (\_ -> DoNothing)
in in
Task.onError task (\_ -> Task.succeed (SetResults [])) Task.onError task (\_ -> Task.succeed DoNothing)
responseDecoder : Decoder (List SearchResult) responseDecoder : Decoder (List SearchResult)
@@ -111,13 +113,14 @@ type Action
| SetQuery String | SetQuery String
| DeleteById ResultId | DeleteById ResultId
| SetResults (List SearchResult) | SetResults (List SearchResult)
| DoNothing
update : Action -> Model -> ( Model, Effects Action ) update : Address String -> Action -> Model -> ( Model, Effects Action )
update action model = update searchAddress action model =
case action of case action of
Search -> Search ->
( model, Effects.task (searchFeed model.query) ) ( model, Effects.task (searchFeed searchAddress model.query) )
SetQuery query -> SetQuery query ->
( { model | query = query }, Effects.none ) ( { model | query = query }, Effects.none )
@@ -139,3 +142,6 @@ update action model =
{ model | results = newResults } { model | results = newResults }
in in
( newModel, Effects.none ) ( newModel, Effects.none )
DoNothing ->
( model, Effects.none )

View File

@@ -1,10 +1,13 @@
module Main (..) where module Main (..) where
import StartApp import StartApp
import Component.ElmHub exposing (..) import ElmHub exposing (..)
import Effects exposing (Effects) import Effects exposing (Effects)
import Task exposing (Task) import Task exposing (Task)
import Html exposing (Html) import Html exposing (Html)
import Signal
import Json.Encode
import Json.Decode
main : Signal Html main : Signal Html
@@ -16,12 +19,40 @@ app : StartApp.App Model
app = app =
StartApp.start StartApp.start
{ view = view { view = view
, update = update , update = update search.address
, init = ( initialModel, Effects.task (searchFeed initialModel.query) ) , init = ( initialModel, Effects.task (searchFeed search.address initialModel.query) )
, inputs = [] , inputs = [ responseActions ]
} }
port tasks : Signal (Task Effects.Never ()) port tasks : Signal (Task Effects.Never ())
port tasks = port tasks =
app.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 _ ->
DoNothing
port githubResponse : Signal Json.Encode.Value

View File

@@ -4,23 +4,37 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>ElmHub</title> <title>ElmHub</title>
<script type="text/javascript" src="github.js"></script>
<script type="text/javascript" src="elm.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="stylesheet" href="style.css">
<link rel="icon" type="image/png" href="elm-hub.png"> <link rel="icon" type="image/png" href="elm-hub.png">
</head> </head>
<body> <body>
<div id="elm-landing-pad"></div>
</body> </body>
<script type="text/javascript"> <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.embed(
// var app = Elm.fullscreenDebug("ElmHub", "Main.elm"); Elm.Main,
document.getElementById("elm-landing-pad"),
{githubResponse: []});
function searchGithub(query) {
console.log("Searching for", query);
var search = github.getSearch(query);
search.repositories({}, function (err, repositories) {
console.log("Got response", repositories);
// TODO: app.ports.portNameGoesHere.send(repositories);
});
}
// TODO app.ports.portNameGoesHere.subscribe(searchGithub);
</script> </script>
</html> </html>

View File

@@ -26,12 +26,8 @@ type Action
update : Action -> Model -> ( Model, Effects Action ) update : Action -> Model -> ( Model, Effects Action )
update action model = update action model =
case action of -- TODO make expand and collapse work
Expand -> ( model, Effects.none )
( { model | expanded = True }, Effects.none )
Collapse ->
( { model | expanded = False }, Effects.none )
view : Address Action -> Model -> Html view : Address Action -> Model -> Html
@@ -46,11 +42,13 @@ view address model =
] ]
[ text model.name ] [ text model.name ]
, button , button
[ class "hide-result", onClick address Collapse ] -- TODO when the user clicks, send a Collapse action
[ class "hide-result" ]
[ text "X" ] [ text "X" ]
] ]
else else
[ button [ button
[ class "expand-result", onClick address Expand ] -- TODO when the user clicks, send an Expand action
[ class "expand-result" ]
[ text "Show" ] [ text "Show" ]
] ]

View File

@@ -16,8 +16,10 @@ to fail; in that case, just run `elm package install` again.)
elm live Main.elm --open -- --output=elm.js elm live Main.elm --open -- --output=elm.js
``` ```
## Compiling CSS ## Running Tests
```bash ```bash
elm test css/Stylesheets.elm cd test
elm package install
elm test TestRunner.elm
``` ```

View File

@@ -12,8 +12,7 @@
"evancz/elm-effects": "2.0.0 <= v < 3.0.0", "evancz/elm-effects": "2.0.0 <= v < 3.0.0",
"evancz/elm-html": "4.0.0 <= v < 5.0.0", "evancz/elm-html": "4.0.0 <= v < 5.0.0",
"evancz/elm-http": "3.0.0 <= v < 4.0.0", "evancz/elm-http": "3.0.0 <= v < 4.0.0",
"evancz/start-app": "2.0.0 <= v < 3.0.0", "evancz/start-app": "2.0.0 <= v < 3.0.0"
"rtfeldman/elm-css": "1.0.0 <= v < 2.0.0"
}, },
"elm-version": "0.16.0 <= v < 0.17.0" "elm-version": "0.16.0 <= v < 0.17.0"
} }