bundsol/boxed

An Elm library to encapsulate any primitive in a single type


License
BSD-3-Clause
Install
elm-package install bundsol/boxed 1.0.0

Documentation

Boxed

An Elm union type and library to encapsulate any primitive in a single type

Motivation

Oftentimes, you find yourself tailoring decoders for all the different schemas or records that you are expecting from the back end. But what if you simply come up with a decoder that stores those records, say, as a Dict? And forget for the moment about the type of each member of the record. You can deal with that later. You won't worry about the names of the fields in your record, either.

All of that postponed work can be encoded in your updaters or views. You look for a field with a certain name, of a certain type, in your dictionary. If not found then no update happens. If it should have been found, then you can start debugging your code (or your back end code).

Example

The sample data in the following example has been taken from the RAML 200 Tutorial.

Save this code in a file called Demo.elm,

module Demo exposing (schema, albumId)

import Json.Encode as Encode
import Json.Decode as Decode exposing (decodeValue, string, field, bool, dict)
import Boxed exposing (Boxed(..))
import Boxed.Json
import Dict exposing (Dict)

(=>) = (,)
o = Encode.object
s = Encode.string
b = Encode.bool
i = Encode.int


albumIdSample = o
  [ "type" => s "string"
  , "required" => b True
  , "minLength" => i 36
  , "maxLength" => i 36
  ]

sample = o
  [ "type" => s "object"
  , "$schema" => s "http://json-schema.org/draft-03/schema"
  , "id" => s "http://jsonschema.net"
  , "required" => b True
  , "properties" => o
    [ "songTitle" => o
      [ "type" => s "string"
      , "required" => b True
      ]
    , "albumId" => albumIdSample
    ]
  ]


type alias Schema = 
  { type_: String 
  , schema_: String
  , id : String 
  , required : Bool
  , properties : Dict String (Boxed ())
  }


invalidSchema : Schema 
invalidSchema = 
  { type_ = "" 
  , schema_ = ""
  , id = "" 
  , required = False
  , properties = Dict.empty
  }
  
  
schemaDecoder =
  Decode.map5 Schema 
    (field "type" string)
    (field "$schema" string)
    (field "id" string)
    (field "required" bool)
    (field "properties" (dict Boxed.Json.decoder))

    
schema : Schema    
schema = 
  decodeValue schemaDecoder sample
  |> Result.withDefault invalidSchema


albumId : Boxed ()    
albumId = 
  decodeValue Boxed.Json.decoder albumIdSample
  |> Result.withDefault Null

Run elm-repl and type as follows

> import Demo exposing (..)
> import Boxed exposing (..)
> import Boxed.Dictionary exposing (apply, mapGet)
> import Maybe exposing (withDefault)
> import Dict exposing(get)
> get "songTitle" schema.properties |> withDefault Null |> apply (get "type")
Just (Str "string") : Maybe.Maybe (Boxed.Boxed ())
> mapGet asInt "maxLength" albumId
Just 36 : Maybe.Maybe Int
> mapGet asBool "required" albumId
Just True : Maybe.Maybe Bool

A field like properties, seen above as member of Schema, is found many times in API specifications. And, although the top level structure of Schema may remain pretty much the same, the contents of the properties member can be expected to be more dynamic. And that is where we can use the Boxed type as a catch-all, maybe even for rapid prototyping.