Use ilias's latest json library, update docs site, use 19.1 for docs site.

This commit is contained in:
Dillon Kearns 2019-12-09 20:18:48 -08:00
parent b0213189ca
commit 1b2d08bc54
11 changed files with 12939 additions and 2482 deletions

View File

@ -35,7 +35,8 @@
"mgold/elm-nonempty-list": "4.0.2 <= v < 5.0.0", "mgold/elm-nonempty-list": "4.0.2 <= v < 5.0.0",
"miniBill/elm-codec": "1.2.0 <= v < 2.0.0", "miniBill/elm-codec": "1.2.0 <= v < 2.0.0",
"noahzgordon/elm-color-extra": "1.0.2 <= v < 2.0.0", "noahzgordon/elm-color-extra": "1.0.2 <= v < 2.0.0",
"tripokey/elm-fuzzy": "5.2.1 <= v < 6.0.0" "tripokey/elm-fuzzy": "5.2.1 <= v < 6.0.0",
"zwilias/json-decode-exploration": "6.0.0 <= v < 7.0.0"
}, },
"test-dependencies": { "test-dependencies": {
"avh4/elm-program-test": "3.1.0 <= v < 4.0.0", "avh4/elm-program-test": "3.1.0 <= v < 4.0.0",

View File

@ -5,13 +5,13 @@
"../../src", "../../src",
"gen" "gen"
], ],
"elm-version": "0.19.0", "elm-version": "0.19.1",
"dependencies": { "dependencies": {
"direct": { "direct": {
"avh4/elm-color": "1.0.0", "avh4/elm-color": "1.0.0",
"dillonkearns/elm-markdown": "1.1.0", "dillonkearns/elm-markdown": "1.1.3",
"dillonkearns/elm-oembed": "1.0.0", "dillonkearns/elm-oembed": "1.0.0",
"elm/browser": "1.0.1", "elm/browser": "1.0.2",
"elm/core": "1.0.2", "elm/core": "1.0.2",
"elm/html": "1.0.0", "elm/html": "1.0.0",
"elm/http": "2.0.0", "elm/http": "2.0.0",
@ -32,22 +32,23 @@
"miniBill/elm-codec": "1.2.0", "miniBill/elm-codec": "1.2.0",
"noahzgordon/elm-color-extra": "1.0.2", "noahzgordon/elm-color-extra": "1.0.2",
"rtfeldman/elm-hex": "1.0.0", "rtfeldman/elm-hex": "1.0.0",
"tripokey/elm-fuzzy": "5.2.1" "tripokey/elm-fuzzy": "5.2.1",
"zwilias/json-decode-exploration": "6.0.0"
}, },
"indirect": { "indirect": {
"elm/bytes": "1.0.8", "elm/bytes": "1.0.8",
"elm/file": "1.0.5", "elm/file": "1.0.5",
"elm/random": "1.0.0",
"elm/regex": "1.0.0", "elm/regex": "1.0.0",
"elm/virtual-dom": "1.0.2", "elm/virtual-dom": "1.0.2",
"fredcy/elm-parseint": "2.0.1" "fredcy/elm-parseint": "2.0.1",
"mgold/elm-nonempty-list": "4.0.2"
} }
}, },
"test-dependencies": { "test-dependencies": {
"direct": { "direct": {
"elm-explorations/test": "1.2.2" "elm-explorations/test": "1.2.2"
}, },
"indirect": { "indirect": {}
"elm/random": "1.0.0"
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
"prismjs": "^1.17.1" "prismjs": "^1.17.1"
}, },
"devDependencies": { "devDependencies": {
"elm": "^0.19.0-no-deps", "elm": "^0.19.1-3",
"elm-oembed": "0.0.6", "elm-oembed": "0.0.6",
"elm-pages": "file:../..", "elm-pages": "file:../..",
"elm-test": "^0.19.0-rev6", "elm-test": "^0.19.0-rev6",

View File

@ -16,6 +16,7 @@ import Html exposing (Html)
import Html.Attributes as Attr import Html.Attributes as Attr
import Index import Index
import Json.Decode as Decode exposing (Decoder) import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Exploration as D
import MarkdownRenderer import MarkdownRenderer
import Metadata exposing (Metadata) import Metadata exposing (Metadata)
import Pages exposing (images, pages) import Pages exposing (images, pages)
@ -27,6 +28,7 @@ import Pages.Manifest.Category
import Pages.PagePath as PagePath exposing (PagePath) import Pages.PagePath as PagePath exposing (PagePath)
import Pages.Platform exposing (Page) import Pages.Platform exposing (Page)
import Palette import Palette
import Secrets
import StaticHttp import StaticHttp
@ -111,183 +113,178 @@ view :
, head : List (Head.Tag Pages.PathKey) , head : List (Head.Tag Pages.PathKey)
} }
view siteMetadata page = view siteMetadata page =
let StaticHttp.succeed
viewFn = { view =
case page.frontmatter of \model viewForPage ->
Metadata.Page metadata -> pageView model siteMetadata page viewForPage
StaticHttp.map3 |> wrapBody
(\elmPagesStars elmPagesStarterStars netlifyStars -> , head = head page.frontmatter
{ view = }
\model viewForPage ->
{ title = metadata.title
, body =
"elm-pages 's: "
++ String.fromInt elmPagesStars
++ "\n\nelm-pages-starter 's: "
++ String.fromInt elmPagesStarterStars
++ "\n\nelm-markdown 's: "
++ String.fromInt netlifyStars
|> Element.text
|> wrapBody
}
, head = head page.frontmatter
}
)
(StaticHttp.jsonRequest "https://api.github.com/repos/dillonkearns/elm-pages"
(Decode.field "stargazers_count" Decode.int)
)
(StaticHttp.jsonRequest "https://api.github.com/repos/dillonkearns/elm-pages-starter"
(Decode.field "stargazers_count" Decode.int)
)
(StaticHttp.jsonRequest "https://api.github.com/repos/dillonkearns/elm-markdown"
(Decode.field "stargazers_count" Decode.int)
)
-- StaticHttp.withData "https://api.github.com/repos/dillonkearns/elm-pages"
-- (Decode.field "stargazers_count" Decode.int)
_ ->
StaticHttp.map
(\staticData ->
{ view =
\model viewForPage ->
{ title = "Other"
, body =
"The value is: "
++ String.fromInt staticData
|> Element.text
|> wrapBody
}
, head = head page.frontmatter
}
)
(StaticHttp.jsonRequest "https://api.github.com/repos/dillonkearns/elm-pages-starter" (Decode.field "stargazers_count" Decode.int))
-- _ ->
-- ( StaticHttp.get "" (\_ -> 456)
-- , \staticData model viewForPage ->
-- { title = "Test"
-- , body =
-- "The value is: "
-- ++ String.fromInt staticData
-- |> Element.text
-- |> wrapBody
-- }
-- )
-- [
-- header page.path
-- , Element.column
-- [ Element.padding 50
-- , Element.spacing 60
-- , Element.Region.mainContent
-- ]
-- (Tuple.second viewForPage)
-- ]
-- |> Element.textColumn
-- [ Element.width Element.fill
-- ]
-- }
-- Metadata.Article metadata ->
-- { title = metadata.title
-- , body =
-- Element.column [ Element.width Element.fill ]
-- [ header page.path
-- , Element.column
-- [ Element.padding 30
-- , Element.spacing 40
-- , Element.Region.mainContent
-- , Element.width (Element.fill |> Element.maximum 800)
-- , Element.centerX
-- ]
-- (Element.column [ Element.spacing 10 ]
-- [ Element.row [ Element.spacing 10 ]
-- [ Author.view [] metadata.author
-- , Element.column [ Element.spacing 10, Element.width Element.fill ]
-- [ Element.paragraph [ Font.bold, Font.size 24 ]
-- [ Element.text metadata.author.name
-- ]
-- , Element.paragraph [ Font.size 16 ]
-- [ Element.text metadata.author.bio ]
-- ]
-- ]
-- ]
-- :: (publishedDateView metadata |> Element.el [ Font.size 16, Font.color (Element.rgba255 0 0 0 0.6) ])
-- :: Palette.blogHeading metadata.title
-- :: articleImageView metadata.image
-- :: Tuple.second viewForPage
-- )
-- ]
-- }
--
-- Metadata.Doc metadata ->
-- { title = metadata.title
-- , body =
-- [ header page.path
-- , Element.row []
-- [ DocSidebar.view page.path siteMetadata
-- |> Element.el [ Element.width (Element.fillPortion 2), Element.alignTop, Element.height Element.fill ]
-- , Element.column [ Element.width (Element.fillPortion 8), Element.padding 35, Element.spacing 15 ]
-- [ Palette.heading 1 [ Element.text metadata.title ]
-- , Element.column [ Element.spacing 20 ]
-- [ tocView (Tuple.first viewForPage)
-- , Element.column
-- [ Element.padding 50
-- , Element.spacing 30
-- , Element.Region.mainContent
-- ]
-- (Tuple.second viewForPage)
-- ]
-- ]
-- ]
-- ]
-- |> Element.textColumn
-- [ Element.width Element.fill
-- , Element.height Element.fill
-- ]
-- }
--
-- Metadata.Author author ->
-- { title = author.name
-- , body =
-- Element.column
-- [ Element.width Element.fill
-- ]
-- [ header page.path
-- , Element.column
-- [ Element.padding 30
-- , Element.spacing 20
-- , Element.Region.mainContent
-- , Element.width (Element.fill |> Element.maximum 800)
-- , Element.centerX
-- ]
-- [ Palette.blogHeading author.name
-- , Author.view [] author
-- , Element.paragraph [ Element.centerX, Font.center ] (Tuple.second viewForPage)
-- ]
-- ]
-- }
--
-- Metadata.BlogIndex ->
-- { title = "elm-pages blog"
-- , body =
-- Element.column [ Element.width Element.fill ]
-- [ header page.path
-- , Element.column [ Element.padding 20, Element.centerX ] [ Index.view siteMetadata ]
-- ]
-- }
-- )
-- |> wrapBody
in
viewFn
wrapBody body =
body --let
|> Element.layout -- viewFn =
[ Element.width Element.fill -- case page.frontmatter of
, Font.size 20 -- Metadata.Page metadata ->
, Font.family [ Font.typeface "Roboto" ] -- StaticHttp.map3
, Font.color (Element.rgba255 0 0 0 0.8) -- (\elmPagesStars elmPagesStarterStars netlifyStars ->
] -- { view =
-- \model viewForPage ->
-- { title = metadata.title
-- , body =
-- "elm-pages ⭐️'s: "
-- ++ String.fromInt elmPagesStars
-- ++ "\n\nelm-pages-starter ⭐️'s: "
-- ++ String.fromInt elmPagesStarterStars
-- ++ "\n\nelm-markdown ⭐️'s: "
-- ++ String.fromInt netlifyStars
-- |> Element.text
-- |> wrapBody
-- }
-- , head = head page.frontmatter
-- }
-- )
-- (StaticHttp.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
-- (D.field "stargazers_count" D.int)
-- )
-- (StaticHttp.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages-starter")
-- (D.field "stargazers_count" D.int)
-- )
-- (StaticHttp.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-markdown")
-- (D.field "stargazers_count" D.int)
-- )
--
-- _ ->
-- StaticHttp.withData "https://api.github.com/repos/dillonkearns/elm-pages"
-- (Decode.field "stargazers_count" Decode.int)
pageView :
Model
-> List ( PagePath Pages.PathKey, Metadata )
-> { path : PagePath Pages.PathKey, frontmatter : Metadata }
-> ( MarkdownRenderer.TableOfContents, List (Element Msg) )
-> { title : String, body : Element Msg }
pageView model siteMetadata page viewForPage =
case page.frontmatter of
Metadata.Page metadata ->
{ title = metadata.title
, body =
[ header page.path
, Element.column
[ Element.padding 50
, Element.spacing 60
, Element.Region.mainContent
]
(Tuple.second viewForPage)
]
|> Element.textColumn
[ Element.width Element.fill
]
}
Metadata.Article metadata ->
{ title = metadata.title
, body =
Element.column [ Element.width Element.fill ]
[ header page.path
, Element.column
[ Element.padding 30
, Element.spacing 40
, Element.Region.mainContent
, Element.width (Element.fill |> Element.maximum 800)
, Element.centerX
]
(Element.column [ Element.spacing 10 ]
[ Element.row [ Element.spacing 10 ]
[ Author.view [] metadata.author
, Element.column [ Element.spacing 10, Element.width Element.fill ]
[ Element.paragraph [ Font.bold, Font.size 24 ]
[ Element.text metadata.author.name
]
, Element.paragraph [ Font.size 16 ]
[ Element.text metadata.author.bio ]
]
]
]
:: (publishedDateView metadata |> Element.el [ Font.size 16, Font.color (Element.rgba255 0 0 0 0.6) ])
:: Palette.blogHeading metadata.title
:: articleImageView metadata.image
:: Tuple.second viewForPage
)
]
}
Metadata.Doc metadata ->
{ title = metadata.title
, body =
[ header page.path
, Element.row []
[ DocSidebar.view page.path siteMetadata
|> Element.el [ Element.width (Element.fillPortion 2), Element.alignTop, Element.height Element.fill ]
, Element.column [ Element.width (Element.fillPortion 8), Element.padding 35, Element.spacing 15 ]
[ Palette.heading 1 [ Element.text metadata.title ]
, Element.column [ Element.spacing 20 ]
[ tocView (Tuple.first viewForPage)
, Element.column
[ Element.padding 50
, Element.spacing 30
, Element.Region.mainContent
]
(Tuple.second viewForPage)
]
]
]
]
|> Element.textColumn
[ Element.width Element.fill
, Element.height Element.fill
]
}
Metadata.Author author ->
{ title = author.name
, body =
Element.column
[ Element.width Element.fill
]
[ header page.path
, Element.column
[ Element.padding 30
, Element.spacing 20
, Element.Region.mainContent
, Element.width (Element.fill |> Element.maximum 800)
, Element.centerX
]
[ Palette.blogHeading author.name
, Author.view [] author
, Element.paragraph [ Element.centerX, Font.center ] (Tuple.second viewForPage)
]
]
}
Metadata.BlogIndex ->
{ title = "elm-pages blog"
, body =
Element.column [ Element.width Element.fill ]
[ header page.path
, Element.column [ Element.padding 20, Element.centerX ] [ Index.view siteMetadata ]
]
}
wrapBody record =
{ body =
record.body
|> Element.layout
[ Element.width Element.fill
, Font.size 20
, Font.family [ Font.typeface "Roboto" ]
, Font.color (Element.rgba255 0 0 0 0.8)
]
, title = record.title
}
articleImageView : ImagePath Pages.PathKey -> Element msg articleImageView : ImagePath Pages.PathKey -> Element msg

View File

@ -33,7 +33,8 @@
"miniBill/elm-codec": "1.2.0", "miniBill/elm-codec": "1.2.0",
"noahzgordon/elm-color-extra": "1.0.2", "noahzgordon/elm-color-extra": "1.0.2",
"rtfeldman/elm-hex": "1.0.0", "rtfeldman/elm-hex": "1.0.0",
"tripokey/elm-fuzzy": "5.2.1" "tripokey/elm-fuzzy": "5.2.1",
"zwilias/json-decode-exploration": "6.0.0"
}, },
"indirect": { "indirect": {
"elm/bytes": "1.0.8", "elm/bytes": "1.0.8",

View File

@ -23,6 +23,7 @@ import Pages.Platform exposing (Page)
import Palette import Palette
import Secrets import Secrets
import StaticHttp import StaticHttp
import Time
@ -145,7 +146,7 @@ get url decoder =
pokemonDetailRequest : StaticHttp.Request (List Pokemon) pokemonDetailRequest : StaticHttp.Request (List Pokemon)
pokemonDetailRequest = pokemonDetailRequest =
get get
"https://pokeapi.co/api/v2/pokemon/?limit=10" "https://pokeapi.co/api/v2/pokemon/?limit=3"
(Decode.field "results" (Decode.field "results"
(Decode.list (Decode.list
(Decode.map2 Tuple.pair (Decode.map2 Tuple.pair
@ -185,6 +186,8 @@ view siteMetadata page =
{ title = "Landing Page" { title = "Landing Page"
, body = , body =
[ header starCount [ header starCount
, Element.text "Built at: "
, Element.text <| String.fromInt <| Time.posixToMillis Pages.buildTime
, pokemon , pokemon
|> List.map pokemonView |> List.map pokemonView
|> Element.column |> Element.column

View File

@ -34,12 +34,13 @@ module.exports = class AddFilesPlugin {
// https://github.com/jantimon/html-webpack-plugin/blob/35a154186501fba3ecddb819b6f632556d37a58f/index.js#L470-L478 // https://github.com/jantimon/html-webpack-plugin/blob/35a154186501fba3ecddb819b6f632556d37a58f/index.js#L470-L478
const staticRequests = this.pagesWithRequests[`/${file.baseRoute}`]; const staticRequests = this.pagesWithRequests[`/${file.baseRoute}`];
console.log('staticData', staticRequests);
const filename = path.join(file.baseRoute, "content.json"); const filename = path.join(file.baseRoute, "content.json");
compilation.fileDependencies.add(filename); compilation.fileDependencies.add(filename);
const rawContents = JSON.stringify({ const rawContents = JSON.stringify({
body: file.content, body: file.content,
staticData: staticRequests staticData: staticRequests || {}
}); });
compilation.assets[filename] = { compilation.assets[filename] = {

File diff suppressed because it is too large Load Diff

View File

@ -1,92 +0,0 @@
module Json.Decode.Exploration.Located exposing (Located(..), toString, map)
{-| A type that gives one or more pieces of information, tagged with a path
through a datastructure with fields and indices.
Most importantly, it is used for both `Warnings` and `Errors` in `Json.Decode.Exploration`.
@docs Located, toString, map
-}
import List.Nonempty as Nonempty exposing (Nonempty(..))
{-| -}
type Located a
= InField String (Nonempty (Located a))
| AtIndex Int (Nonempty (Located a))
| Here a
{-| Allows turning a non-empty list of `Located a` into a flat list of human
readable strings, provided we have a way to turn an `a` into some lines of text.
Each string represents a line. This allows arbitrary indentation by mapping over
the lines and prepending some whitespace.
-}
toString : (a -> List String) -> Nonempty (Located a) -> List String
toString itemToString locatedItems =
locatedItems
|> gather ""
|> List.map (\( x, vals ) -> render itemToString x vals)
|> intercalate ""
{-| -}
map : (a -> b) -> Located a -> Located b
map op located =
case located of
InField f val ->
InField f <| Nonempty.map (map op) val
AtIndex i val ->
AtIndex i <| Nonempty.map (map op) val
Here v ->
Here (op v)
intercalate : a -> List (List a) -> List a
intercalate sep lists =
lists |> List.intersperse [ sep ] |> List.concat
render : (a -> List String) -> String -> List a -> List String
render itemToString path errors =
let
formattedErrors : List String
formattedErrors =
List.concatMap itemToString errors
|> List.map indent
in
if String.isEmpty path then
formattedErrors
else
("At path " ++ path) :: "" :: formattedErrors
indent : String -> String
indent =
(++) " "
flatten : Located a -> List ( String, List a )
flatten located =
case located of
Here v ->
[ ( "", [ v ] ) ]
InField s vals ->
gather ("/" ++ s) vals
AtIndex i vals ->
gather ("/" ++ String.fromInt i) vals
gather : String -> Nonempty (Located a) -> List ( String, List a )
gather prefix (Nonempty first rest) =
List.concatMap flatten (first :: rest)
|> List.map (Tuple.mapFirst ((++) prefix))

View File

@ -1,437 +0,0 @@
module Json.Decode.Exploration.Pipeline exposing
( required, requiredAt, optional, optionalAt, hardcoded, custom
, checked, checkedAt, ignored, ignoredAt
, decode, resolve
)
{-|
# Json.Decode.Pipeline
Use the `(|>)` operator to build JSON decoders.
## Decoding fields
@docs required, requiredAt, optional, optionalAt, hardcoded, custom
## Validating the JSON without using the values
@docs checked, checkedAt, ignored, ignoredAt
## Beginning and ending pipelines
@docs decode, resolve
### Verified docs
The examples all expect imports set up like this:
import Json.Decode.Exploration exposing (..)
import Json.Decode.Exploration.Pipeline exposing (..)
import Json.Decode.Exploration.Located exposing (Located(..))
import Json.Encode as Encode
import List.Nonempty as Nonempty
For automated verification of these examples, this import is also required.
Please ignore it.
import DocVerificationHelpers exposing (User)
-}
import Json.Decode.Exploration as Decode exposing (Decoder)
{-| Decode a required field.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> required "name" string
|> required "email" string
""" {"id": 123, "email": "sam@example.com", "name": "Sam"} """
|> decodeString userDecoder
--> Success { id = 123, name = "Sam", email = "sam@example.com" }
-}
required : String -> Decoder a -> Decoder (a -> b) -> Decoder b
required key valDecoder decoder =
decoder |> Decode.andMap (Decode.field key valDecoder)
{-| Decode a required nested field.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> requiredAt [ "profile", "name" ] string
|> required "email" string
"""
{
"id": 123,
"email": "sam@example.com",
"profile": { "name": "Sam" }
}
"""
|> decodeString userDecoder
--> Success { id = 123, name = "Sam", email = "sam@example.com" }
-}
requiredAt : List String -> Decoder a -> Decoder (a -> b) -> Decoder b
requiredAt path valDecoder decoder =
decoder |> Decode.andMap (Decode.at path valDecoder)
{-| Decode a field that may be missing or have a null value. If the field is
missing, then it decodes as the `fallback` value. If the field is present,
then `valDecoder` is used to decode its value. If `valDecoder` fails on a
`null` value, then the `fallback` is used as if the field were missing
entirely.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> optional "name" string "blah"
|> required "email" string
""" { "id": 123, "email": "sam@example.com" } """
|> decodeString userDecoder
--> Success { id = 123, name = "blah", email = "sam@example.com" }
Because `valDecoder` is given an opportunity to decode `null` values before
resorting to the `fallback`, you can distinguish between missing and `null`
values if you need to:
userDecoder2 =
decode User
|> required "id" int
|> optional "name" (oneOf [ string, null "NULL" ]) "MISSING"
|> required "email" string
Note also that this behaves _slightly_ different than the stock pipeline
package.
In the stock pipeline package, running the following decoder with an array as
the input would _succeed_.
fooDecoder =
decode identity
|> optional "foo" (maybe string) Nothing
In this package, such a decoder will error out instead, saying that it expected
the input to be an object. The _key_ `"foo"` is optional, but it really does
have to be an object before we even consider trying your decoder or returning
the fallback.
-}
optional : String -> Decoder a -> a -> Decoder (a -> b) -> Decoder b
optional key valDecoder fallback decoder =
Decode.andMap (optionalField key valDecoder fallback) decoder
{-| Decode an optional nested field.
-}
optionalAt : List String -> Decoder a -> a -> Decoder (a -> b) -> Decoder b
optionalAt path valDecoder fallback decoder =
Decode.andMap
(List.foldr (\f d -> optionalField f d fallback) valDecoder path)
decoder
optionalField : String -> Decoder a -> a -> Decoder a
optionalField field decoder fallback =
Decode.isObject
|> Decode.andThen
(\_ ->
Decode.oneOf
[ Decode.field field (Decode.null <| Decode.succeed fallback)
, Decode.field field (Decode.succeed <| Decode.field field decoder)
, Decode.succeed (Decode.succeed fallback)
]
|> resolve
)
{-| Rather than decoding anything, use a fixed value for the next step in the
pipeline. `harcoded` does not look at the JSON at all.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> hardcoded "Alex"
|> required "email" string
""" { "id": 123, "email": "sam@example.com" } """
|> decodeString userDecoder
--> Success { id = 123, name = "Alex", email = "sam@example.com" }
-}
hardcoded : a -> Decoder (a -> b) -> Decoder b
hardcoded =
Decode.andMap << Decode.succeed
{-| `check` a field in the JSON to ensure it has a certain value before allowing
the decoder to succeed.
import List.Nonempty as Nonempty
import Json.Decode.Exploration exposing (..)
import Json.Decode.Exploration.Located exposing (Located(..))
import Json.Encode as Encode
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> required "name" string
|> required "email" string
|> checked "enabled" bool True
"""
{
"id": 123,
"email": "sam@example.com",
"name": "Sam",
"enabled": true
}
"""
|> decodeString userDecoder
--> Success { id = 123, name = "Sam", email = "sam@example.com" }
"""
{
"id": 123,
"email": "sam@example.com",
"name": "Sam",
"enabled": false
}
"""
|> decodeString userDecoder
--> Errors expectedErrors
expectedErrors : Errors
expectedErrors =
Failure "Verification failed" (Just <| Encode.bool False)
|> Here
|> Nonempty.fromElement
|> InField "enabled"
|> Nonempty.fromElement
-}
checked : String -> Decoder a -> a -> Decoder b -> Decoder b
checked fieldName =
checkedAt [ fieldName ]
{-| Check a field at a certain path and validate its content before allowing
decoding to succeed.
-}
checkedAt : List String -> Decoder a -> a -> Decoder b -> Decoder b
checkedAt path decoder expectedValue next =
Decode.succeed ()
|> Decode.check decoder expectedValue
|> Decode.at path
|> Decode.andThen (\_ -> next)
{-| Ignore a field from decoding. By using `ignored`, you acknowledge that it is
in the JSON so warnings don't show up.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> required "name" string
|> required "email" string
|> ignored "enabled"
"""
{
"id": 123,
"email": "sam@example.com",
"name": "Sam",
"enabled": "No one cares, it's ignored."
}
"""
|> decodeString userDecoder
--> Success { id = 123, name = "Sam", email = "sam@example.com" }
-}
ignored : String -> Decoder a -> Decoder a
ignored field =
ignoredAt [ field ]
{-| Ignore a value at a certain path from decoding. By using `ignoredAt`, you
acknowledge that it is in the JSON so warnings don't show up.
-}
ignoredAt : List String -> Decoder a -> Decoder a
ignoredAt path decoder =
Decode.at path Decode.value |> Decode.andThen (\_ -> decoder)
{-| Run the given decoder and feed its result into the pipeline at this point.
Consider this example.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> custom (at [ "profile", "name" ] string)
|> required "email" string
"""
{
"id": 123,
"email": "sam@example.com",
"profile": {"name": "Sam"}
}
"""
|> decodeString userDecoder
--> Success { id = 123, name = "Sam", email = "sam@example.com" }
-}
custom : Decoder a -> Decoder (a -> b) -> Decoder b
custom =
Decode.andMap
{-| Convert a `Decoder (Result x a)` into a `Decoder a`. Useful when you want
to perform some custom processing just before completing the decoding operation.
import Json.Decode.Exploration exposing (..)
type alias User =
{ id : Int
, name : String
, email : String
}
userDecoder : Decoder User
userDecoder =
let
-- toDecoder gets run *after* all the
-- (|> required ...) steps are done.
toDecoder : Int -> String -> String -> Int -> Decoder User
toDecoder id name email version =
if version >= 2 then
succeed (User id name email)
else
fail "This JSON is from a deprecated source. Please upgrade!"
in
decode toDecoder
|> required "id" int
|> required "name" string
|> required "email" string
|> required "version" int
-- version is part of toDecoder,
-- but it is not a part of User
|> resolve
"""
{
"id": 123,
"name": "Sam",
"email": "sam@example.com",
"version": 3
}
"""
|> decodeString userDecoder
--> Success { id = 123, name = "Sam", email = "sam@example.com" }
-}
resolve : Decoder (Decoder a) -> Decoder a
resolve =
Decode.andThen identity
{-| Begin a decoding pipeline. This is a synonym for [Json.Decode.succeed](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#succeed),
intended to make things read more clearly.
type alias User =
{ id : Int
, email : String
, name : String
}
userDecoder : Decoder User
userDecoder =
decode User
|> required "id" int
|> required "email" string
|> optional "name" string ""
-}
decode : a -> Decoder a
decode =
Decode.succeed