Skip to content

Commit

Permalink
Merge pull request #187 from ErikSchierboom/react
Browse files Browse the repository at this point in the history
Add react exercise
  • Loading branch information
ErikSchierboom committed Aug 10, 2016
2 parents 4aec687 + 31ee433 commit 73e282a
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 1 deletion.
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@
"sgf-parsing",
"transpose",
"tree-building",
"grep"
"grep",
"react"
],
"deprecated": [
"binary",
Expand Down
56 changes: 56 additions & 0 deletions exercises/react/Example.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module React

[<AbstractClass>]
type Cell() =
abstract Value : int with get, set
abstract Changed: IEvent<int>

type InputCell(initialValue: int) =
inherit Cell()

let mutable value = initialValue
let changed = new Event<int>()

override this.Changed = changed.Publish

override this.Value
with get() = value
and set(newValue : int) =
if value <> newValue then
value <- newValue
changed.Trigger(newValue)

type ComputeCell(inputs: Cell list, compute: (int list -> int)) =
inherit Cell()

let computeValue() = inputs |> List.map (fun x -> x.Value) |> compute

let mutable value = computeValue()
let changed = new Event<int>()

let updateValue() =
let newValue = computeValue()

if newValue <> value then
value <- newValue
changed.Trigger(newValue)

let subscribeToInputChanges() =
[for input in inputs do
input.Changed.Add(fun _ -> updateValue())]
|> ignore

do
subscribeToInputChanges()

override this.Changed = changed.Publish

override this.Value
with get() = value
and set(v : int) = failwith "Cannot directly set value of compute cell"

let mkInputCell value = new InputCell(value)
let mkComputeCell (inputs: Cell list) (compute: (int list -> int)) = new ComputeCell(inputs, compute)

let setValue value (cell: Cell) = cell.Value <- value
let value (cell: Cell) = cell.Value
96 changes: 96 additions & 0 deletions exercises/react/ReactTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
module ReactTest

open NUnit.Framework

open React

[<Test>]
let ``Setting the value of an input cell changes the observable value`` () =
let i1 = mkInputCell 1

Assert.That(value i1 , Is.EqualTo(1))
setValue 2 i1 |> ignore
Assert.That(value i1 , Is.EqualTo(2))

[<Test>]
[<Ignore("Remove to run test")>]
let ``The value of a compute is determined by the value of the dependencies`` () =
let i1 = mkInputCell 1
let c1 = mkComputeCell [i1] (fun values -> values.[0] + 1)

Assert.That(c1.Value, Is.EqualTo(2))
setValue 2 i1 |> ignore
Assert.That(c1.Value, Is.EqualTo(3))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Compute cells can depend on other compute cells`` () =
let i1 = mkInputCell 1
let c1 = mkComputeCell [i1] (fun values -> values.[0] + 1)
let c2 = mkComputeCell [i1] (fun values -> values.[0] - 1)
let c3 = mkComputeCell [c1; c2] (fun values -> values.[0] * values.[1])

Assert.That(c3.Value, Is.EqualTo(0))
setValue 3 i1 |> ignore
Assert.That(c3.Value, Is.EqualTo(8))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Compute cells can have callbacks`` () =
let i1 = mkInputCell 1
let c1 = mkComputeCell [i1] (fun values -> values.[0] + 1)
let mutable observed = []
c1.Changed.Add(fun value -> observed <- observed @ [value]) |> ignore

Assert.That(observed, Is.EqualTo([]))
setValue 2 i1 |> ignore
Assert.That(observed, Is.EqualTo([3]))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Callbacks only trigger on change`` () =
let i1 = mkInputCell 1
let c1 = mkComputeCell [i1] (fun values -> if values.[0] > 2 then values.[0] + 1 else 2)
let mutable observerCalled = 0
c1.Changed.Add(fun value -> observerCalled <- observerCalled + 1) |> ignore

setValue 1 i1 |> ignore
Assert.That(observerCalled, Is.EqualTo(0))
setValue 2 i1 |> ignore
Assert.That(observerCalled, Is.EqualTo(0))
setValue 3 i1 |> ignore
Assert.That(observerCalled, Is.EqualTo(1))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Callbacks can be removed`` () =
let i1 = mkInputCell 1
let c1 = mkComputeCell [i1] (fun values -> values.[0] + 1)
let mutable observed1 = []
let mutable observed2 = []

let changedHandler1 = Handler<int>(fun _ value -> observed1 <- observed1 @ [value])
c1.Changed.AddHandler changedHandler1
c1.Changed.Add(fun value -> observed2 <- observed2 @ [value]) |> ignore

setValue 2 i1 |> ignore
Assert.That(observed1, Is.EqualTo([3]))
Assert.That(observed2, Is.EqualTo([3]))

c1.Changed.RemoveHandler changedHandler1
setValue 3 i1 |> ignore
Assert.That(observed1, Is.EqualTo([3]))
Assert.That(observed2, Is.EqualTo([3; 4]))

[<Test>]
[<Ignore("Remove to run test")>]
let ``Callbacks should only be called once even if multiple dependencies have changed`` () =
let i1 = mkInputCell 1
let c1 = mkComputeCell [i1] (fun values -> values.[0] + 1)
let c2 = mkComputeCell [i1] (fun values -> values.[0] - 1)
let c3 = mkComputeCell [c2] (fun values -> values.[0] - 1)
let c4 = mkComputeCell [c1; c3] (fun values -> values.[0] * values.[1])
let mutable changed4 = 0
c1.Changed.Add(fun value -> changed4 <- changed4 + 1) |> ignore
setValue 3 i1 |> ignore
Assert.That(changed4, Is.EqualTo(1))

0 comments on commit 73e282a

Please sign in to comment.