Fix intro/part9
This commit is contained in:
@@ -40,7 +40,7 @@ import Html.Attributes exposing (class)
|
|||||||
import Html.Events exposing (stopPropagationOn)
|
import Html.Events exposing (stopPropagationOn)
|
||||||
import Http
|
import Http
|
||||||
import HttpBuilder exposing (RequestBuilder, withBody, withExpect, withQueryParams)
|
import HttpBuilder exposing (RequestBuilder, withBody, withExpect, withQueryParams)
|
||||||
import Json.Decode as Decode exposing (Decoder, bool, int, list, string)
|
import Json.Decode as Decode exposing (Decoder)
|
||||||
import Json.Decode.Pipeline exposing (custom, hardcoded, required)
|
import Json.Decode.Pipeline exposing (custom, hardcoded, required)
|
||||||
import Json.Encode as Encode
|
import Json.Encode as Encode
|
||||||
import Markdown
|
import Markdown
|
||||||
@@ -83,6 +83,47 @@ type Article a
|
|||||||
= Article Internals a
|
= Article Internals a
|
||||||
|
|
||||||
|
|
||||||
|
{-| Metadata about the article - its title, description, and so on.
|
||||||
|
|
||||||
|
Importantly, this module's public API exposes a way to read this metadata, but
|
||||||
|
not to alter it. This is read-only information!
|
||||||
|
|
||||||
|
If we find ourselves using any particular piece of metadata often,
|
||||||
|
for example `title`, we could expose a convenience function like this:
|
||||||
|
|
||||||
|
Article.title : Article a -> String
|
||||||
|
|
||||||
|
If you like, it's totally reasonable to expose a function like that for every one
|
||||||
|
of these fields!
|
||||||
|
|
||||||
|
(Okay, to be completely honest, exposing one function per field is how I prefer
|
||||||
|
to do it, and that's how I originally wrote this module. However, I'm aware that
|
||||||
|
this code base has become a common reference point for beginners, and I think it
|
||||||
|
is _extremely important_ that slapping some "getters and setters" on a record
|
||||||
|
does not become a habit for anyone who is getting started with Elm. The whole
|
||||||
|
point of making the Article type opaque is to create guarantees through
|
||||||
|
_selectively choosing boundaries_ around it. If you aren't selective about
|
||||||
|
where those boundaries are, and instead expose a "getter and setter" for every
|
||||||
|
field in the record, the result is an API with no more guarantees than if you'd
|
||||||
|
exposed the entire record directly! It is so important to me that beginners not
|
||||||
|
fall into the terrible "getters and setters" trap that I've exposed this
|
||||||
|
Metadata record instead of exposing a single function for each of its fields,
|
||||||
|
as I did originally. This record is not a bad way to do it, by any means,
|
||||||
|
but if this seems at odds with <https://youtu.be/x1FU3e0sT1I> - now you know why!
|
||||||
|
See commit c2640ae3abd60262cdaafe6adee3f41d84cd85c3 for how it looked before.
|
||||||
|
)
|
||||||
|
|
||||||
|
-}
|
||||||
|
type alias Metadata =
|
||||||
|
{ description : String
|
||||||
|
, title : String
|
||||||
|
, tags : List String
|
||||||
|
, createdAt : Time.Posix
|
||||||
|
, favorited : Bool
|
||||||
|
, favoritesCount : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type alias Internals =
|
type alias Internals =
|
||||||
{ slug : Slug
|
{ slug : Slug
|
||||||
, author : Author
|
, author : Author
|
||||||
@@ -170,39 +211,15 @@ internalsDecoder maybeCred =
|
|||||||
|> custom metadataDecoder
|
|> custom metadataDecoder
|
||||||
|
|
||||||
|
|
||||||
type alias Metadata =
|
|
||||||
{ description : String
|
|
||||||
, title : String
|
|
||||||
, tags : List String
|
|
||||||
, favorited : Bool
|
|
||||||
, favoritesCount : Int
|
|
||||||
, createdAt : Time.Posix
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
metadataDecoder : Decoder Metadata
|
metadataDecoder : Decoder Metadata
|
||||||
metadataDecoder =
|
metadataDecoder =
|
||||||
{- 👉 TODO: replace the calls to `hardcoded` with calls to `required`
|
|
||||||
in order to decode these fields:
|
|
||||||
|
|
||||||
--- "description" -------> description : String
|
|
||||||
--- "title" -------------> title : String
|
|
||||||
--- "tagList" -----------> tags : List String
|
|
||||||
--- "favorited" ---------> favorited : Bool
|
|
||||||
--- "favoritesCount" ----> favoritesCount : Int
|
|
||||||
|
|
||||||
Once this is done, the articles in the feed should look normal again.
|
|
||||||
|
|
||||||
💡 HINT: Order matters! These must be decoded in the same order
|
|
||||||
as the order of the fields in `type alias Metadata` above. ☝️
|
|
||||||
-}
|
|
||||||
Decode.succeed Metadata
|
Decode.succeed Metadata
|
||||||
|> hardcoded "(needs decoding!)"
|
|> required "description" (Decode.map (Maybe.withDefault "") (Decode.nullable Decode.string))
|
||||||
|> hardcoded "(needs decoding!)"
|
|> required "title" Decode.string
|
||||||
|> hardcoded []
|
|> required "tagList" (Decode.list Decode.string)
|
||||||
|> hardcoded False
|
|
||||||
|> hardcoded 0
|
|
||||||
|> required "createdAt" Timestamp.iso8601Decoder
|
|> required "createdAt" Timestamp.iso8601Decoder
|
||||||
|
|> required "favorited" Decode.bool
|
||||||
|
|> required "favoritesCount" Decode.int
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user