From 5b29219bfa6dd729e2a3e17b17969a6b62894851 Mon Sep 17 00:00:00 2001 From: Oliver Linnarsson Date: Wed, 19 Jun 2024 11:14:36 +0200 Subject: [PATCH] feat: Finalize 2.0 ctx api --- README.md | 15 ++++-- src/handles.gleam | 4 +- src/handles/ctx.gleam | 91 ---------------------------------- test/api_test.gleam | 24 +++++---- test/unit_tests/ctx_test.gleam | 48 ------------------ 5 files changed, 25 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index c112d45..a6edc8f 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,9 @@ import handles/ctx pub fn main() { let assert Ok(template) = handles.prepare("Hello {{name}}") let assert Ok(string) = - handles.run( - template, - [ctx.Prop("name", ctx.Str("Oliver"))] - ) + handles.run(template, ctx.Dict([ctx.Prop("name", ctx.Str("Oliver"))])) - io.println(string) + io.debug(string) } ``` @@ -38,10 +35,18 @@ A property tag is used to access properties passed into the template engine and Values accessed by a property tag must be of type `String`, `Int`, or `Float`, or it will result in a runtime error. Accessing a property which was not passed into the template engine will result in a runtime error. +A property tag can refer to a nested property with `.` separating keys in nested dicts. + ```handlebars {{some.property.path}} ``` +A property can also refer to the current context element by passing a single `.`. + +```handlebars +{{.}} +``` + ### Block tags A block tag is used to temporarily alter the behavior of the templating engine. diff --git a/src/handles.gleam b/src/handles.gleam index 3f573fc..7600127 100644 --- a/src/handles.gleam +++ b/src/handles.gleam @@ -20,8 +20,8 @@ pub fn prepare(template: String) -> Result(Template, error.TokenizerError) { pub fn run( template: Template, - root_ctx: List(ctx.Prop), + ctx: ctx.Value, ) -> Result(String, error.RuntimeError) { let Template(ast) = template - engine.run(ast, ctx.Dict(root_ctx), string_builder.new()) + engine.run(ast, ctx, string_builder.new()) } diff --git a/src/handles/ctx.gleam b/src/handles/ctx.gleam index 39cb79c..ddf6712 100644 --- a/src/handles/ctx.gleam +++ b/src/handles/ctx.gleam @@ -1,7 +1,3 @@ -import gleam/dict -import gleam/dynamic -import gleam/list - pub type Prop { Prop(key: String, value: Value) } @@ -14,90 +10,3 @@ pub type Value { Dict(value: List(Prop)) List(value: List(Value)) } - -/// Transforms String, Int, Float, Bool, List, & Dict to their coresponding ctx.Value type. -/// -/// Anny other types will panic -/// -/// ## Examples -/// -/// ```gleam -/// import handles/ctx -/// -/// ctx.from("Hello World") -/// // -> ctx.Str("Hello World") -/// ``` -/// -/// ```gleam -/// ctx.from(42) -/// // -> ctx.Int(42) -/// ``` -/// -/// ```gleam -/// ctx.from(3.14) -/// // -> ctx.Float(3.14) -/// ``` -/// -/// ```gleam -/// ctx.from([1, 2, 3]) -/// // -> ctx.List([ctx.Int(1), ctx.Int(2), ctx.Int(3)]) -/// ``` -/// -/// ```gleam -/// ctx.from([ -/// 42 |> dynamic.from, -/// 3.14 |> dynamic.from, -/// "Hello" |> dynamic.from, -/// ]) -/// // -> ctx.List([ctx.Int(42), ctx.Float(3.14), ctx.Str("Hello")]) -/// ``` -pub fn from(value: a) -> Value { - from_dynamic(value |> dynamic.from) -} - -fn from_dynamic(value: dynamic.Dynamic) -> Value { - case value |> dynamic.classify { - "String" -> { - let assert Ok(val) = value |> dynamic.string - Str(val) - } - "Int" -> { - let assert Ok(val) = value |> dynamic.int - Int(val) - } - "Float" -> { - let assert Ok(val) = value |> dynamic.float - Float(val) - } - "Bool" -> { - let assert Ok(val) = value |> dynamic.bool - Bool(val) - } - "Dict" -> { - let assert Ok(val) = - value |> dynamic.dict(dynamic.string, dynamic.dynamic) - from_dict(val, from_dynamic) - } - "List" -> { - let assert Ok(val) = value |> dynamic.list(dynamic.dynamic) - from_list(val, from_dynamic) - } - _ -> panic as "Unable to construct ctx from dynamic value" - } -} - -fn from_dict( - source: dict.Dict(String, a), - value_mapper: fn(a) -> Value, -) -> Value { - source - |> dict.to_list - |> list.map(fn(it) { Prop(it.0, value_mapper(it.1)) }) - |> Dict -} - -fn from_list(source: List(a), value_mapper: fn(a) -> Value) -> Value { - source - |> list.map(value_mapper) - |> List -} diff --git a/test/api_test.gleam b/test/api_test.gleam index 26fddb9..0c7f92b 100644 --- a/test/api_test.gleam +++ b/test/api_test.gleam @@ -5,7 +5,7 @@ import handles/ctx pub fn api_hello_world_test() { handles.prepare("Hello {{name}}!") |> should.be_ok - |> handles.run([ctx.Prop("name", ctx.Str("Oliver"))]) + |> handles.run(ctx.Dict([ctx.Prop("name", ctx.Str("Oliver"))])) |> should.be_ok |> should.equal("Hello Oliver!") } @@ -13,16 +13,18 @@ pub fn api_hello_world_test() { pub fn api_knattarna_test() { handles.prepare("{{#each knattarna}}Hello {{name}}\n{{/each}}") |> should.be_ok - |> handles.run([ - ctx.Prop( - "knattarna", - ctx.List([ - ctx.Dict([ctx.Prop("name", ctx.Str("Knatte"))]), - ctx.Dict([ctx.Prop("name", ctx.Str("Fnatte"))]), - ctx.Dict([ctx.Prop("name", ctx.Str("Tjatte"))]), - ]), - ), - ]) + |> handles.run( + ctx.Dict([ + ctx.Prop( + "knattarna", + ctx.List([ + ctx.Dict([ctx.Prop("name", ctx.Str("Knatte"))]), + ctx.Dict([ctx.Prop("name", ctx.Str("Fnatte"))]), + ctx.Dict([ctx.Prop("name", ctx.Str("Tjatte"))]), + ]), + ), + ]), + ) |> should.be_ok |> should.equal( "Hello Knatte diff --git a/test/unit_tests/ctx_test.gleam b/test/unit_tests/ctx_test.gleam index 7eaec46..a6aa8fa 100644 --- a/test/unit_tests/ctx_test.gleam +++ b/test/unit_tests/ctx_test.gleam @@ -1,5 +1,3 @@ -import gleam/dict -import gleam/dynamic import gleam/iterator import gleeunit/should import handles/ctx @@ -30,49 +28,3 @@ pub fn drill_deep_test() { |> should.be_ok |> should.equal(ctx.Str(expected_string)) } - -pub fn from_string_test() { - ctx.from("foo") - |> should.equal(ctx.Str("foo")) -} - -pub fn from_int_test() { - ctx.from(42) - |> should.equal(ctx.Int(42)) -} - -pub fn from_float_test() { - ctx.from(3.14) - |> should.equal(ctx.Float(3.14)) -} - -pub fn from_bool_test() { - ctx.from(True) - |> should.equal(ctx.Bool(True)) -} - -pub fn from_list_test() { - ctx.from([1, 2, 3]) - |> should.equal(ctx.List([ctx.Int(1), ctx.Int(2), ctx.Int(3)])) -} - -pub fn from_dict_test() { - ctx.from( - dict.new() - |> dict.insert("first", 1) - |> dict.insert("second", 2) - |> dict.insert("third", 3), - ) - |> should.equal( - ctx.Dict([ - ctx.Prop("first", ctx.Int(1)), - ctx.Prop("second", ctx.Int(2)), - ctx.Prop("third", ctx.Int(3)), - ]), - ) -} - -pub fn from_mixed_types_test() { - ctx.from([42 |> dynamic.from, 3.14 |> dynamic.from, "Hello" |> dynamic.from]) - |> should.equal(ctx.List([ctx.Int(42), ctx.Float(3.14), ctx.Str("Hello")])) -}