Skip to content

Instantly share code, notes, and snippets.

@kkruups
Last active November 17, 2016 06:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkruups/89ae8e58f54bb9b15357406abc914caf to your computer and use it in GitHub Desktop.
Save kkruups/89ae8e58f54bb9b15357406abc914caf to your computer and use it in GitHub Desktop.
Elm: Medium post: Elm Insight A Road to a Better Understanding: How the elm JSON API, Elm-Decode-Pipeline and Json.Decode API Work
{--
Copy-n-Paste this code into Elm Online Editor to see run:
http://elm-lang.org/try
Updated to reflect changes in elm release version 0.18
field replaces (:=)
mapN replace objectN functions
All code except data blob -- jsonBlob is written by author (@Brian Hichs mention below)
--}
import Html exposing (text, div, span)
import Html.Attributes as Attributes
import Json.Decode as Json exposing (field, map2, string, int)
import String exposing (toLower)
{--
To simulate Json error just change data type of any field
for example change "id":1 to "id": "one" or "id": one in jsonBlob below
--}
--data blob from Brian Hicks wonderful article on "Decoding Large JSON Objects: A Summary"
-- https://www.brianthicks.com/post/2016/08/22/decoding-large-json-objects-a-summary/
jsonBlob =
"""{
"login": "octocat",
"id": 1,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false,
"name": "monalisa octocat",
"company": "GitHub",
"blog": "https://github.com/blog",
"location": "San Francisco",
"email": "octocat@github.com",
"hireable": false,
"bio": "There once was...",
"public_repos": 2,
"public_gists": 1,
"followers": 20,
"following": 0,
"created_at": "2008-01-14T04:33:35Z",
"updated_at": "2008-01-14T04:33:35Z"
}"""
--elm record data type
type alias User =
{
login : String
,id : Int
,name : String
,company : String
,blog : String
,followers : Int
}
{-- pipe decoder helper : enables a tagger/function decoder
to be piped using the pipe operator, in order to be constructed by
its members" value decoders. It eliminates the limitations of using Json.Decode.objectN
functions by extending the limits of the number JSON objects which can be decoded by
enabling the chaining of tagger/function component decoders via the pipe operator --}
pipeDecoderBldr: String -> Json.Decoder a -> Json.Decoder ( a-> b) -> Json.Decoder b
pipeDecoderBldr key valueDecoder taggerDecoder = Json.map2 (|>) ( field key valueDecoder) taggerDecoder
--userDecoder : Json.Decoder User
{--
userDecoder =
Debug.log "object3: " Json.object3 User
("login" := Json.string )
( "id" := Json.int )
("name" := Json.string)
--}
-- Helper converts a tagger/function to a tagger decoder, wraps Json.succeed,
-- the purpose of wrapping it is to improve the name of the function,
-- to have it match what it actually does
toDecoder: a -> Json.Decoder a
toDecoder tagger = Json.succeed tagger
-- userDecoder reduces the User function Json Decoder, created by toDecoder, down to a direct User Record Json Decoder
-- by piping subsequent returned function decoder to chained calls of pipedecode_bldr which explicity passes the Json field name and component Json decoder
-- corresponding to the data type of each field desired for extraction/decoding from the Json String
user_decoder =
toDecoder User -- returns/passes Decoder (String-> Int -> String -> String -> String -> Int -> User)
|>pipeDecoderBldr "login" Json.string -- returns/passes Decoder (Int -> String -> String -> String -> Int -> User)
|>pipeDecoderBldr "id" Json.int -- returns/passes Decoder (String -> String -> String -> Int -> User)
|>pipeDecoderBldr "name" Json.string -- returns/passes Decoder (String -> String -> Int -> User)
|>pipeDecoderBldr "company" Json.string -- returns/passes Decoder (String -> Int -> User)
|>pipeDecoderBldr "blog" Json.string -- returns/passes Decoder (Int -> User)
|>pipeDecoderBldr "followers" Json.int -- returns Decoder (User) /Final Step: User Decoder Constructed/
userDecode json_string = Json.decodeString user_decoder json_string
userDecoderHandler: String -> String
userDecoderHandler j_blob =
let jresult =
userDecode j_blob
in
case jresult of
Ok user_record->
toString <| user_record
Err error ->
"userDeocderHandler: " ++ error
(==>) =
(,)
--Helpers to allow strings to be passed to Html Helpers (style attributes and span )
padding pad =
let
pad_ = toLower pad
in
if pad_ == "nothing" then
Nothing
else
Just pad_
bkgnd_clr bkgnd =
let
bkgnd_ = toLower bkgnd
in
if bkgnd_ == "nothing" then
Nothing
else
Just bkgnd_
txt_clr tx_clr =
let
tx_clr_ = toLower tx_clr
in
if tx_clr_ == "nothing" then
Nothing
else
Just tx_clr_
display disp =
let
disp_ = toLower disp
in
if disp_ == "nothing" then
Nothing
else
Just disp_
font_size fsize =
let
fsize_ = toLower fsize
in
if fsize_ == "nothing" then
Nothing
else
Just fsize_
{-- refaactored to above: refactoring allows string arguments to Html helpers to be non-case specific
font_size fsize =
if fsize == "nothing" || fsize == "Nothing" || fsize == "NOTHING" then
Nothing
else
Just fsize
--}
-- #############################################################
-- Html helpers; styleIT, styelIWithDefault, & spanIt
-- =============================================================
--demonstrates building a list as well as setting defaults using Maybe String pattern
styleIt: String -> String -> String -> String -> String -> List (String, String)
styleIt p b t d f =
let list = ( "padding" ==> Maybe.withDefault "25px" (padding p) )
:: ("background-color" ==> Maybe.withDefault "black" (bkgnd_clr b))
:: ( "color" ==> Maybe.withDefault "white" (txt_clr t) )
:: ( "display" ==> Maybe.withDefault "block" (display d) )
:: ( "font-size" ==> Maybe.withDefault "25px" (font_size f) )
::[]
in
list
--just change backgound color
--(defaults :padding: "25px", background-color: "black", text-color: "white", display: "block", font-size: "25px")
styleItWithDefault bkgnd_clr =
styleIt "NOTHING" bkgnd_clr "nothing" "nothing" "nothing"
{--
--spanIt: List String -> String -> Html.Html msg
spanIt decorate txtmsg =
span[Attributes.style <| styleIt decorate ] [ text txtmsg ]
-}
--spanIt: (Maybe String, Maybe String, Maybe String, Maybe String) -> String -> Html.Html msg
-- demonstrates decomposing a tuple
spanIt styleTup txtmsg =
let
(p, b, t, d, f) = styleTup
in
span [ Attributes.style <| (styleIt (p) (b) (t) (d) (f) ) ] [ text txtmsg ]
-- =====================================
main =
div[ Attributes.style <| styleItWithDefault "NOThinG"
]
[ spanIt ("2px", "WhitE", "BLacK", "block", "40px") "Result Ok (User Record)"
, text <| toString <| userDecode jsonBlob
, div[ Attributes.style[
("padding", "20px")
, ("background-color", "Orange")
, ("color", "black")
, ("display", "block")
]
][
spanIt ("0PX", "blue", "oranGe", "block", "35px") "Result.withDefault"
,text <| toString <| (
Debug.log "Result.withDefault:" <|
Result.withDefault {
login="not logged in"
,id = 0
,name = "no name"
,company = "no company"
,blog ="no blog url"
,followers = -1
} <| userDecode jsonBlob
)
]
, div[ Attributes.style[
("padding", "20px")
, ("background-color", "beige")
, ("color", "brown")
]
][
div[ Attributes.style <| styleIt "0px" "beige" "Green" "nothinG" "35PX"
][
text "userDecoderHandler: (case .. of pattern) "
]
,text (
Debug.log "userDecoderHandler: (case..of pattern)"
<| userDecoderHandler jsonBlob
)
]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment