Archive

Monthly Archives: May 2017

We discuss the current state of the Lambda Cartel.
Specifically, we discuss Elm, F#, and project Nikeza.

Advertisements

Reference the following guide for installing the unit test package.

Here’s a couple of unit tests:

module HelloTest exposing (..)

import Controls.Login as Login exposing (Model)
import Home exposing (..)
import Test exposing (..)
import Expect


suite : Test
suite =
    describe "Login module"
        [ test "runtime.tryLogin succeeds with valid credentials" <|
            \_ ->
                let
                    ( login, runtime ) =
                        ( Login.Model "test" "test" False, Home.runtime )

                    result =
                        runtime.tryLogin login
                in
                    Expect.equal result.loggedIn True
        , test "runtime.tryLogin fails with invalid credentials" <|
            \_ ->
                let
                    ( login, runtime ) =
                        ( Login.Model "test" "invalid_password" False, Home.runtime )

                    result =
                        runtime.tryLogin login
                in
                    Expect.equal result.loggedIn False
        ]

Appendix

Here’s some of the test dependencies below.

Home.elm

module Home exposing (..)

import Domain.Core exposing (..)
import Controls.Login as Login exposing (..)
import Tests.TestAPI as TestAPI exposing (tryLogin)
import Services.Server as Services exposing (tryLogin)
import Html exposing (..)
import Html.Attributes exposing (..)


main =
    Html.beginnerProgram
        { model = model
        , update = update
        , view = view
        }



-- CONFIGURATION


configuration : Configuration
configuration =
    Isolation


type Configuration
    = Integration
    | Isolation


type alias Dependencies =
    { tryLogin : Loginfunction }


runtime : Dependencies
runtime =
    case configuration of
        Integration ->
            Dependencies Services.tryLogin

        Isolation ->
            Dependencies TestAPI.tryLogin



-- MODEL


type alias Model =
    { videos : List Video
    , articles : List Article
    , login : Login.Model
    }


model : Model
model =
    { videos = [], articles = [], login = Login.model }


init : ( Model, Cmd Msg )
init =
    ( model, Cmd.none )



-- UPDATE


type Msg
    = Video Video
    | Article Article
    | Submitter Submitter
    | Search String
    | Register
    | OnLogin Login.Msg


update : Msg -> Model -> Model
update msg model =
    case msg of
        Video v ->
            model

        Article v ->
            model

        Submitter v ->
            model

        Search v ->
            model

        Register ->
            model

        OnLogin subMsg ->
            case subMsg of
                Login.Attempt v ->
                    let
                        latest =
                            Login.update subMsg model.login
                    in
                        { model | login = runtime.tryLogin latest }

                Login.UserInput _ ->
                    { model | login = Login.update subMsg model.login }

                Login.PasswordInput _ ->
                    { model | login = Login.update subMsg model.login }



-- VIEW


view : Model -> Html Msg
view model =
    div []
        [ header []
            [ label [] [ text "Nikeza" ]
            , model |> sessionUI
            ]
        , footer [ class "copyright" ]
            [ label [] [ text "(c)2017" ]
            , a [ href "" ] [ text "GitHub" ]
            ]
        ]


sessionUI : Model -> Html Msg
sessionUI model =
    let
        loggedIn =
            model.login.loggedIn

        welcome =
            p [] [ text <| "Welcome " ++ model.login.username ++ "!" ]

        signout =
            a [ href "" ] [ label [] [ text "Signout" ] ]
    in
        if (not loggedIn) then
            Html.map OnLogin <| Login.view model.login
        else
            div [ class "signin" ] [ welcome, signout ]

TestAPI.elm

module Tests.TestAPI exposing (..)

import Controls.Login as Login exposing (Model)


tryLogin : Login.Model -> Login.Model
tryLogin credentials =
    let
        successful =
            String.toLower credentials.username == "test" && String.toLower credentials.password == "test"
    in
        if successful then
            { username = credentials.username, password = credentials.password, loggedIn = True }
        else
            { username = credentials.username, password = credentials.password, loggedIn = False }

Core.elm

module Domain.Core exposing (..)

import Controls.Login as Login exposing (Model)

...

type alias Loginfunction =
    Login.Model -> Login.Model


tryLogin : Loginfunction -> String -> String -> Login.Model
tryLogin loginf username password =
    loginf <| Login.Model username password False

Login.elm

module Controls.Login exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)


-- MODEL


type alias Model =
    { username : String
    , password : String
    , loggedIn : Bool
    }


model : Model
model =
    Model "" "" False



-- UPDATE


type Msg
    = UserInput String
    | PasswordInput String
    | Attempt ( String, String )


update : Msg -> Model -> Model
update msg model =
    case msg of
        UserInput v ->
            { model | username = v }

        PasswordInput v ->
            { model | password = v }

        Attempt ( username, password ) ->
            { model | username = username, password = password }



-- VIEW


view : Model -> Html Msg
view model =
    div []
        [ input [ class "signin", type_ "submit", value "Signin", onClick <| Attempt ( model.username, model.password ) ] []
        , input [ class "signin", type_ "password", placeholder "password", onInput PasswordInput, value model.password ] []
        , input [ class "signin", type_ "text", placeholder "username", onInput UserInput, value model.username ] []
        ]

Intro

In this post, I will attempt to document my understanding of how to do Dependency Injection within Elm.

Dependency Injection is a technique used to provide a set of dependencies that a system requires at runtime. This technique is especially advantageous when testing that the system complies with business rules without having to provide all of the dependencies that a production environment may require.

Function Interface

To support Dependency Injection within a functional programming paradigm, we can create a type to represent the interface to a function. To accomplish this, we need to specify the signature of the arbitrary function.


Here’s a type called LoginFunction within the Domain.Core module along with its signature:

type alias Loginfunction =
    Login.Model -> Login.Model

We can then use this type as an interface to a function. Hence, we can pass a function as an argument to the tryLogin function as well as other parameters.

Here’s an example:

tryLogin : Login.Loginfunction -> String -> String -> Login.Model
tryLogin loginf username password =
    loginf <| Login.Model username password False

Test API

The TestAPI is a server library that is designed to support testing by executing client requests. These requests can be for serving up some arbitrary value to the client or by executing some arbitrary operation that adheres to an interface that the client is required to comply with.

Here’s the implementation of the TestAPI’s tryLogin function:

module Tests.TestAPI exposing (..)

import Controls.Login as Login exposing (Model)

tryLogin : Login.Model -> Login.Model
tryLogin credentials =
    let
        successful =
            String.toLower credentials.username == "test" && String.toLower credentials.password == "test"
    in
        if successful then
            { username = credentials.username, password = credentials.password, loggedIn = True }
        else
            { username = credentials.username, password = credentials.password, loggedIn = False }

Registering Dependencies

We can engineer our webpage to have the capability to run in either isolation or integration mode. Isolation mode means that our webpage will not rely on system integration (i.e. web server) to function. Hence, our webpage is its own self-contained system. Integration mode, on the other hand, means that our system does require external systems in order for the webpage to be functional.

Here’s some of the code to do it:

import Domain.Core exposing (..)
import Controls.Login as Login exposing (..)
import Tests.TestAPI as TestAPI exposing (tryLogin)
import Services.Server as Services exposing (tryLogin)
...
-- CONFIGURATION

configuration : Configuration
configuration =
    Isolation -- SET CONFIGURATION HERE ! ! !

type Configuration
    = Integration
    | Isolation

type alias Dependencies =
    { tryLogin : Login.Loginfunction }

runtime : Dependencies
runtime =
    case configuration of
        Integration ->
            Dependencies Services.tryLogin

        Isolation ->
            Dependencies TestAPI.tryLogin

The client code was updated with the following call to the tryLogin function:

        OnLogin subMsg ->
            case subMsg of
                Login.Attempt v ->
                    let
                        latest =
                            Login.update subMsg model.login
                    in
                        { model | login = runtime.tryLogin latest }

Conclusion

In conclusion, I attempted to document my journey of learning Elm by exploring Dependency Injection. The code can be found on GitHub.

Appendix

Below, are the modules that I implemented.

Home.elm

module Home exposing (..)

import Domain.Core exposing (..)
import Controls.Login as Login exposing (..)
import Tests.TestAPI as TestAPI exposing (tryLogin)
import Services.Server as Services exposing (tryLogin)
import Html exposing (..)
import Html.Attributes exposing (..)

main =
    Html.beginnerProgram
        { model = model
        , update = update
        , view = view
        }

-- CONFIGURATION

configuration : Configuration
configuration =
    Isolation

type Configuration
    = Integration
    | Isolation

type alias Dependencies =
    { tryLogin : Login.Loginfunction }

runtime : Dependencies
runtime =
    case configuration of
        Integration ->
            Dependencies Services.tryLogin

        Isolation ->
            Dependencies TestAPI.tryLogin

-- MODEL

type alias Model =
    { videos : List Video
    , articles : List Article
    , login : Login.Model
    }

model : Model
model =
    { videos = [], articles = [], login = Login.model }

init : ( Model, Cmd Msg )
init =
    ( model, Cmd.none )

-- UPDATE

type Msg
    = Video Video
    | Article Article
    | Submitter Submitter
    | Search String
    | Register
    | OnLogin Login.Msg

update : Msg -> Model -> Model
update msg model =
    case msg of
        Video v ->
            model

        Article v ->
            model

        Submitter v ->
            model

        Search v ->
            model

        Register ->
            model

        OnLogin subMsg ->
            case subMsg of
                Login.Attempt v ->
                    let
                        latest =
                            Login.update subMsg model.login
                    in
                        { model | login = runtime.tryLogin latest }

                Login.UserInput _ ->
                    { model | login = Login.update subMsg model.login }

                Login.PasswordInput _ ->
                    { model | login = Login.update subMsg model.login }

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ header []
            [ label [] [ text "Nikeza" ]
            , model |> sessionUI
            ]
        , footer [ class "copyright" ]
            [ label [] [ text "(c)2017" ]
            , a [ href "" ] [ text "GitHub" ]
            ]
        ]

sessionUI : Model -> Html Msg
sessionUI model =
    let
        loggedIn =
            model.login.loggedIn

        welcome =
            p [] [ text <| "Welcome " ++ model.login.username ++ "!" ]

        signout =
            a [ href "" ] [ label [] [ text "Signout" ] ]
    in
        if (not loggedIn) then
            Html.map OnLogin <| Login.view model.login
        else
            div [ class "signin" ] [ welcome, signout ]

Core.elm

module Domain.Core exposing (..)

import Controls.Login as Login exposing (Model, Loginfunction)

type Submitter
    = Submitter String

type Title
    = Title String

type Url
    = Url String

type Video
    = Video Post

type Article
    = Article Post

type alias Post =
    { submitter : Submitter, title : Title, url : Url }

type alias Loginfunction =
    Login.Model -> Login.Model

tryLogin : Login.Loginfunction -> String -> String -> Login.Model
tryLogin loginf username password =
    loginf <| Login.Model username password False

Login.elm

module Controls.Login exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- MODEL

type alias Model =
    { username : String
    , password : String
    , loggedIn : Bool
    }

model : Model
model =
    Model "" "" False

-- UPDATE

type Msg
    = UserInput String
    | PasswordInput String
    | Attempt ( String, String )

update : Msg -> Model -> Model
update msg model =
    case msg of
        UserInput v ->
            { model | username = v }

        PasswordInput v ->
            { model | password = v }

        Attempt ( username, password ) ->
            { model | username = username, password = password }

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ input [ class "signin", type_ "submit", value "Signin", onClick <| Attempt ( model.username, model.password ) ] []
        , input [ class "signin", type_ "password", placeholder "password", onInput PasswordInput, value model.password ] []
        , input [ class "signin", type_ "text", placeholder "username", onInput UserInput, value model.username ] []
        ]

TestAPI.elm

module Tests.TestAPI exposing (..)

import Controls.Login as Login exposing (Model)

tryLogin : Login.Model -> Login.Model
tryLogin credentials =
    let
        successful =
            String.toLower credentials.username == "test" && String.toLower credentials.password == "test"
    in
        if successful then
            { username = credentials.username, password = credentials.password, loggedIn = True }
        else
            { username = credentials.username, password = credentials.password, loggedIn = False }

Intro

In a previous post, I discussed how to implement a Login user-control. The implementation details of that post had the business logic embedded within the user control itself instead of the user-control’s host.

 
This post discusses how we can lift the business logic for logging-in from the user-control itself, to the actual parent of the control.

The following code is the updated Login module:

module Controls.Login exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- MODEL

type alias Model =
    { username : String
    , password : String
    , loggedIn : Bool
    }

model : Model
model =
    Model "" "" False

-- UPDATE

type Msg
    = UserInput String
    | PasswordInput String
    | Attempt ( String, String )

update : Msg -> Model -> Model
update msg model =
    case msg of
        UserInput v ->
            { model | username = v }

        PasswordInput v ->
            { model | password = v }

        Attempt ( username, password ) ->
            { model | username = username, password = password }

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ input [ class "signin", type_ "submit", value "Signin", onClick <| Attempt ( model.username, model.password ) ] []
        , input [ class "signin", type_ "password", placeholder "password", onInput PasswordInput, value model.password ] []
        , input [ class "signin", type_ "text", placeholder "username", onInput UserInput, value model.username ] []
        ]

Unlike the last post for building a user-control, the Login module above no longer has business logic for actually processing an attempted login. Instead, I made this the responsibility of this user-control’s parent.

Let’s view the following Update and supporting functions of our Login control’s parent:

update : Msg -> Model -> Model
update msg model =
    case msg of
        ...
        OnLogin subMsg ->
            case subMsg of
                Login.Attempt v ->
                    let
                        latest =
                            Login.update subMsg model.login
                    in
                        { model | login = attemptLogin latest }

                Login.UserInput _ ->
                    { model | login = Login.update subMsg model.login }

                Login.PasswordInput _ ->
                    { model | login = Login.update subMsg model.login }

 

Note above how our update function now performs pattern matching on the Msg case values that are defined within our Login module.

The supporting functions can be found below:

attemptLogin : Login.Model -> Login.Model
attemptLogin credentials =
    let
        successful =
            String.toLower credentials.username == "test" && String.toLower credentials.password == "test"
    in
        if successful then
            { username = credentials.username, password = credentials.password, loggedIn = True }
        else
            { username = credentials.username, password = credentials.password, loggedIn = False }

Here’s the entire module implementation:

module Home exposing (..)

import Domain.Core exposing (..)
import Controls.Login as Login exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)

main =
    Html.beginnerProgram
        { model = model
        , update = update
        , view = view
        }

-- MODEL

type alias Model =
    { videos : List Video
    , articles : List Article
    , login : Login.Model
    }

model : Model
model =
    { videos = [], articles = [], login = Login.model }

init : ( Model, Cmd Msg )
init =
    ( model, Cmd.none )

-- UPDATE

type Msg
    = Video Video
    | Article Article
    | Submitter Submitter
    | Search String
    | Register
    | OnLogin Login.Msg

update : Msg -> Model -> Model
update msg model =
    case msg of
        Video v ->
            model

        Article v ->
            model

        Submitter v ->
            model

        Search v ->
            model

        Register ->
            model

        OnLogin subMsg ->
            case subMsg of
                Login.Attempt v ->
                    let
                        latest =
                            Login.update subMsg model.login
                    in
                        { model | login = attemptLogin latest }

                Login.UserInput _ ->
                    { model | login = Login.update subMsg model.login }

                Login.PasswordInput _ ->
                    { model | login = Login.update subMsg model.login }

attemptLogin : Login.Model -> Login.Model
attemptLogin credentials =
    let
        successful =
            String.toLower credentials.username == "test" && String.toLower credentials.password == "test"
    in
        if successful then
            { username = credentials.username, password = credentials.password, loggedIn = True }
        else
            { username = credentials.username, password = credentials.password, loggedIn = False }

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ header []
            [ label [] [ text "Nikeza" ]
            , model |> sessionUI
            ]
        , footer [ class "copyright" ]
            [ label [] [ text "(c)2017" ]
            , a [ href "" ] [ text "GitHub" ]
            ]
        ]

sessionUI : Model -> Html Msg
sessionUI model =
    let
        loggedIn =
            model.login.loggedIn

        welcome =
            p [] [ text <| "Welcome " ++ model.login.username ++ "!" ]

        signout =
            a [ href "" ] [ label [] [ text "Signout" ] ]
    in
        if (not loggedIn) then
            Html.map OnLogin <| Login.view model.login
        else
            div [ class "signin" ] [ welcome, signout ]

Conclusion

In conclusion, I discussed how to implement a Login user-control that does not does not have the responsibility for evaluating if an attempted login was successful or not. Thus, I attempted to demonstrate how to implement that responsibility within the host (i.e. parent) of that user-control.

Intro

This post is meant to help others install Elm.

Installation

1. Install Node.js
2. Install Yarn
3. install Elm.

After the installations are complete, I recommend viewing the “Hello World” tutorial.

Compiling Elm

There exists an Elm package that enables automatic compiling and browse refresh when modifying and saving Elm files.

Given that the elm file that I’m working on is named “Main.elm”, the following needs to be done:

1: create/update index.html with the following:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="style.css">
    <script src="main.js"></script>
  </head>
  <body>
    <script>
      Elm.Main.fullscreen();
    </script>
  </body>
</html>

2: Enter the following command on whatever command-line interface

elm-live Main.elm --open --output=main.js

Note:
After performing the steps above, every time a save operation occurs, an automatic refresh should also occur on the browser displaying the webpage (i.e. index.html)

Conclusion

In conclusion, this post was meant to serve as a reference to installing and running Elm.

Intro

This post attempts to document my journey of building a user-control in Elm. The user-control that I’m building is a login control.

Building the Login Control

The following is the code for the Login control:

module Domain.Login exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- MODEL

type alias Model =
    { username : String
    , password : String
    , loggedIn : Bool
    }

model : Model
model =
    Model "" "" False

-- UPDATE

type Msg
    = UserInput String
    | PasswordInput String
    | Attempt ( String, String )

update : Msg -> Model -> Model
update msg model =
    case msg of
        UserInput v ->
            { model | username = v }

        PasswordInput v ->
            { model | password = v }

        Attempt ( username, password ) ->
            if String.toLower username == "test" && String.toLower password == "test" then
                { model | loggedIn = True }
            else
                { model | loggedIn = False }

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ input [ class "signin", type_ "submit", value "Signin", onClick <| Attempt ( model.username, model.password ) ] []         , input [ class "signin", type_ "password", placeholder "password", onInput PasswordInput, value model.password ] []         , input [ class "signin", type_ "text", placeholder "username", onInput UserInput, value model.username ] []         ] 

Note that we have the following messages within the code above:

 type Msg     = UserInput String     | PasswordInput String     | Attempt ( String, String ) 

The messages (i.e. union cases) above that have their own attached data are used to update the state of our Login model. Therefore, we can examine the code below to understand observe how the messages get sent:

 view : Model -> Html Msg
view model =
    div []
        [ input [ class "signin", type_ "submit", value "Signin", onClick <| Attempt ( model.username, model.password ) ] []
        , input [ class "signin", type_ "password", placeholder "password", onInput PasswordInput, value model.password ] []
        , input [ class "signin", type_ "text", placeholder "username", onInput UserInput, value model.username ] []
        ]

Note that the code for the Login control follows the standard structure of Elm’s (Model, View, Update) architecture. Hence, there’s no new paradigm being introduced for that code. However, there is a little bit of ceremony within the parent control that will embed the login control.

Embedding the User Control

The following code reflects the parent control of the Login control:

module Home exposing (..)

import Domain.Core exposing (..)
import Domain.Login as Login exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)

main =
    Html.beginnerProgram
        { model = model
        , update = update
        , view = view
        }

-- MODEL

type alias Model =
    { videos : List Video
    , articles : List Article
    , login : Login.Model
    }

model : Model
model =
    { videos = [], articles = [], login = Login.model }

init : ( Model, Cmd Msg )
init =
    ( model, Cmd.none )

-- UPDATE

type Msg
    = Video Video
    | Article Article
    | Submitter Submitter
    | Search String
    | Register
    | Login Login.Msg

update : Msg -> Model -> Model
update msg model =
    case msg of
        Video v ->
            model

        Article v ->
            model

        Submitter v ->
            model

        Search v ->
            model

        Register ->
            model

        Login subMsg ->
            let
                newState =
                    Login.update subMsg model.login
            in
                { model | login = newState }

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ header []
            [ label [] [ text "Nikeza" ]
            , model |> sessionUI
            ]
        , footer [ class "copyright" ]
            [ label [] [ text "(c)2017" ]
            , a [href ] [ text "GitHub" ]
            ]
        ]

sessionUI : Model -> Html Msg
sessionUI model =
    let
        loggedIn =
            model.login.loggedIn

        welcome =
            p [] [ text <| "Welcome " ++ model.login.username ++ "!" ]

        signout =
            a [ href "" ] [ label [] [ text "Signout" ] ]
    in
        if (not loggedIn) then
            Html.map Login <| Login.view model.login
        else
            div [ class "signin" ] [ welcome, signout ]

Let’s first break down the plumbing required for the parent control to use the embedded Login control.

Model

We add a property (i.e. login) to our Model record that is of type Login.Model which is defined within our embedded Login user-control.

Here’s the code:

type alias Model =
    { videos : List Video
    , articles : List Article
    , login : Login.Model
    }

Message

Our parent control needs to define a message case that correlates to the message type that’s defined within the embedded Login control.

Here’s the code:

type Msg
    = ...
    | Login Login.Msg

So in the discriminated union above (i.e. Msg), we declared a message case value named “Login” and associate an argument of type Msg that references the embedded Login control’s Msg type.

Update

Once our message case value is declared (which references the Msg type of the Login control), we then write code for our Update function on our parent control.

Here’s the code:

update : Msg -> Model -> Model
update msg model =
    case msg of
        ...
        Login subMsg ->
            let
                newState =
                    Login.update subMsg model.login
            in
                { model | login = newState }

The code above takes the value of our “Login” case value and applies it to the Update function of the Login module along with the required model argument.

View

In order for the view to embed the Login user-control, we take leverage the Html.map function. In the Html.map function we supply the message and the result from invoking the user-control’s view function. When invoking the view function, we provide the login property of our parent’s model.

Here’s the code:

sessionUI : Model -> Html Msg
sessionUI model =
    let
        loggedIn =
            model.login.loggedIn

        welcome =
            p [] [ text <| "Welcome " ++ model.login.username ++ "!" ]

        signout =
            a [ href "" ] [ label [] [ text "Signout" ] ]
    in
        if (not loggedIn) then
            Html.map OnLogin <| Login.view model.login
        else
            div [ class "signin" ] [ welcome, signout ]

Conclusion

In conclusion, I attempted to document my journey of building a user control in Elm.