thoughtbot / expirable / Expirable

The concept of a value that expires or disappears over a period of time is relatively common in applications. The Expirable a type allows one to wrap a value as one that expires after a certain period of seconds.

It's expected that a List (Expirable a) be used to manage a list of things that can expire; with this, the tickAll function can be used to take in the list of Expirable a and return a new list of Expirable a. This function handles removal of items that have expired.

This is a portion of an application that manages a list of toast messages. These are displayed for a period of time and are then hidden.

Without digging into render the messages, we display the corresponding Msg, subscriptions, and modeling to manage the list of toast messages.

import Expirable exposing (Expirable)

type alias Model =
    { toastMessages : List (Expirable String)
    }

type Msg
    = DecrementToastMessages Time.Posix

initial : Model
initial =
    { toastMessages =
        [ Expirable.build (Expirable.seconds 5) "Hi there"
        , Expirable.build (Expirable.seconds 30) "This goes longer"
        ]
    }

subscriptions : Model -> Sub Msg
subscriptions model =
    Expirable.subscription DecrementToastMessages

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        DecrementToastMessages time ->
            ( { model
                | toastMessages = Expirable.tickAll time model.toastMessages
              }
            , Cmd.none
            )

Type and Constructors


type Expirable a

The core Expirable type describing a value that can expire in the future.

build : SecondsRemaining -> a -> Expirable a

Constructor to convert a value to be expirable. It requires both the time duration (in seconds) that the value should remain and the value itself.

   toastWelcome : Expirable String
   toastWelcome =
       Expirable.build
           (Expirable.seconds 15)
           "Welcome to the site!"

seconds : Basics.Int -> SecondsRemaining

Construct a value representing the number of seconds before a value expires.

Subscriptions and Management

subscription : (Time.Posix -> a) -> Platform.Sub.Sub a

Generate a subscription to tag an application-specific Msg; this is used in conjunction with tickAll to manage a list of Expirable a.

    subscriptions : Model -> Sub Msg
    subscriptions model =
        Expirable.subscription DecrementToastMessages

tickAll : Time.Posix -> List (Expirable a) -> List (Expirable a)

Within pattern-matching Msg in update, tickAll should be used to manage a list of Expirable a in order to expire the appropriate set of values.

    type Msg
        = DecrementToastMessages Time.Posix


    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
        case msg of
            DecrementToastMessages time ->
                { model
                    | toastMessages = Expirable.tickAll time model.toastMessages
                }
                    ! []

Information

value : Expirable a -> a

Extract the value from the Expirable a.

percentComplete : Expirable a -> Basics.Float

Calculate the percentage complete as a Float with a value between 0 and 1.

e.g. If a value is set to expire in 60 seconds and 15 seconds have passed, this would return 0.25.