From 22e5816e3f2b8cc945ff46aecea039d8998ac365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Fri, 10 Mar 2023 14:24:08 -0300 Subject: [PATCH 01/11] Interpolates messages with variables --- lib/kino_slack/message_cell.ex | 22 ++++++++++++-- test/kino_slack/message_cell_test.exs | 41 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/kino_slack/message_cell.ex b/lib/kino_slack/message_cell.ex index cbdbfbe..9f523d6 100644 --- a/lib/kino_slack/message_cell.ex +++ b/lib/kino_slack/message_cell.ex @@ -49,6 +49,7 @@ defmodule KinoSlack.MessageCell do @impl true def to_source(attrs) do required_fields = ~w(token_secret_name channel message) + interpolated_message = interpolate(attrs["message"]) if all_fields_filled?(attrs, required_fields) do quote do @@ -63,7 +64,7 @@ defmodule KinoSlack.MessageCell do url: "/chat.postMessage", json: %{ channel: unquote(attrs["channel"]), - text: unquote(attrs["message"]) + text: unquote(interpolated_message) } ) @@ -78,7 +79,24 @@ defmodule KinoSlack.MessageCell do end end - def all_fields_filled?(attrs, keys) do + defp interpolate(message) do + ~r/(?){{[^}]+}}(?)/ + |> Regex.split(message, on: [:before_interpolation, :after_interpolation]) + |> Enum.map(fn message_chunk -> + case Regex.named_captures(~r/{{(?.*)}}/, message_chunk) do + %{"var_name" => var_name} -> + "\#{#{var_name}}" + + _ -> + message_chunk + end + end) + |> Enum.join() + |> then(fn message -> "\"" <> message <> "\"" end) + |> Code.string_to_quoted!() + end + + defp all_fields_filled?(attrs, keys) do Enum.all?(keys, fn key -> attrs[key] not in [nil, ""] end) end end diff --git a/test/kino_slack/message_cell_test.exs b/test/kino_slack/message_cell_test.exs index 4f09576..689eb4a 100644 --- a/test/kino_slack/message_cell_test.exs +++ b/test/kino_slack/message_cell_test.exs @@ -48,6 +48,47 @@ defmodule KinoSlack.MessageCellTest do assert generated_code == expected_code end + test "generates source code with variable interpolation" do + {kino, _source} = start_smart_cell!(MessageCell, %{}) + + push_event(kino, "update_token_secret_name", "SLACK_TOKEN") + push_event(kino, "update_channel", "#slack-channel") + push_event(kino, "update_message", "Hello {{first_name}} {{last_name}}!") + + assert_smart_cell_update( + kino, + %{ + "token_secret_name" => "SLACK_TOKEN", + "channel" => "#slack-channel", + "message" => "Hello {{first_name}} {{last_name}}!" + }, + generated_code + ) + + expected_code = ~S""" + req = + Req.new( + base_url: "https://slack.com/api", + auth: {:bearer, System.fetch_env!("LB_SLACK_TOKEN")} + ) + + response = + Req.post!(req, + url: "/chat.postMessage", + json: %{channel: "#slack-channel", text: "Hello #{first_name} #{last_name}!"} + ) + + case response.body do + %{"ok" => true} -> :ok + %{"ok" => false, "error" => error} -> {:error, error} + end + """ + + expected_code = String.trim(expected_code) + + assert generated_code == expected_code + end + test "generates source code from stored attributes" do stored_attrs = %{ "token_secret_name" => "SLACK_TOKEN", From b3847b70e66d2d1322ef13aeec2d5a913c30c3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Wed, 15 Mar 2023 16:17:31 -0300 Subject: [PATCH 02/11] Interpolates messages by dinamically building the string interpolation AST --- lib/assets/main.js | 5 ++ lib/kino_slack/message_cell.ex | 21 +------ lib/kino_slack/message_interpolator.ex | 60 +++++++++++++++++++ .../kino_slack/messsage_interpolator_test.exs | 45 ++++++++++++++ 4 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 lib/kino_slack/message_interpolator.ex create mode 100644 test/kino_slack/messsage_interpolator_test.exs diff --git a/lib/assets/main.js b/lib/assets/main.js index 5b20439..53fdbed 100644 --- a/lib/assets/main.js +++ b/lib/assets/main.js @@ -36,6 +36,11 @@ export function init(ctx, payload) { create a Slack app and get your app's token.

+
+

+ To dynamically inject values into the query use double curly braces, like {{name}}. +

+
diff --git a/lib/kino_slack/message_cell.ex b/lib/kino_slack/message_cell.ex index 9f523d6..5e91cb3 100644 --- a/lib/kino_slack/message_cell.ex +++ b/lib/kino_slack/message_cell.ex @@ -49,7 +49,7 @@ defmodule KinoSlack.MessageCell do @impl true def to_source(attrs) do required_fields = ~w(token_secret_name channel message) - interpolated_message = interpolate(attrs["message"]) + message_ast = KinoSlack.MessageInterpolator.interpolate(attrs["message"]) if all_fields_filled?(attrs, required_fields) do quote do @@ -64,7 +64,7 @@ defmodule KinoSlack.MessageCell do url: "/chat.postMessage", json: %{ channel: unquote(attrs["channel"]), - text: unquote(interpolated_message) + text: unquote(message_ast) } ) @@ -79,23 +79,6 @@ defmodule KinoSlack.MessageCell do end end - defp interpolate(message) do - ~r/(?){{[^}]+}}(?)/ - |> Regex.split(message, on: [:before_interpolation, :after_interpolation]) - |> Enum.map(fn message_chunk -> - case Regex.named_captures(~r/{{(?.*)}}/, message_chunk) do - %{"var_name" => var_name} -> - "\#{#{var_name}}" - - _ -> - message_chunk - end - end) - |> Enum.join() - |> then(fn message -> "\"" <> message <> "\"" end) - |> Code.string_to_quoted!() - end - defp all_fields_filled?(attrs, keys) do Enum.all?(keys, fn key -> attrs[key] not in [nil, ""] end) end diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex new file mode 100644 index 0000000..ed272b6 --- /dev/null +++ b/lib/kino_slack/message_interpolator.ex @@ -0,0 +1,60 @@ +defmodule KinoSlack.MessageInterpolator do + def interpolate(message) do + ast = quote do: <<"">> + interpolate(message, ast) + end + + defp interpolate("", result_ast) do + result_ast + end + + defp interpolate("{{" <> rest, ast) do + with [inner, rest] <- String.split(rest, "}}", parts: 2), + {:ok, expression} <- Code.string_to_quoted(inner) do + ast = append_interpolation(ast, expression) + interpolate(rest, ast) + else + _ -> + <> = "{" + ast = append_char(ast, char) + ast = append_char(ast, char) + interpolate(rest, ast) + end + end + + defp interpolate(<>, ast) do + new_ast = append_char(ast, char) + interpolate(rest, new_ast) + end + + defp append_interpolation(ast, expression) do + interpolation_node = { + :"::", + [], + [ + {{:., [], [Kernel, :to_string]}, [], [expression]}, + {:binary, [], Elixir} + ] + } + + {_, _, args} = ast + args = args ++ [interpolation_node] + + {:<<>>, [], args} + end + + defp append_char(ast, char) do + {_, _, args} = ast + last_arg = List.last(args) + + new_args = + if is_binary(last_arg) do + last_string = last_arg <> <> + List.replace_at(args, -1, last_string) + else + args ++ [<>] + end + + {:<<>>, [], new_args} + end +end diff --git a/test/kino_slack/messsage_interpolator_test.exs b/test/kino_slack/messsage_interpolator_test.exs new file mode 100644 index 0000000..db422af --- /dev/null +++ b/test/kino_slack/messsage_interpolator_test.exs @@ -0,0 +1,45 @@ +defmodule KinoSlack.MesssageInterpolatorTest do + use ExUnit.Case, async: true + + alias KinoSlack.MessageInterpolator, as: Interpolator + + test "it interpolates variables inside a message" do + first_name = "Hugo" + last_name = "Baraúna" + message = "Hi {{first_name}} {{last_name}}! 🎉" + + interpolated_ast = Interpolator.interpolate(message) + {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + + assert interpolated_message == "Hi Hugo Baraúna! 🎉" + end + + test "it interpolates expressons inside a message" do + message = "One plus one is: {{1 + 1}}" + + interpolated_ast = Interpolator.interpolate(message) + {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + + assert interpolated_message == "One plus one is: 2" + end + + test "it interpolates expressions with functinos and vars inside a message" do + first_name = "Hugo" + message = "Do you {{first_name}}, know {{1 + 1}} ?" + + interpolated_ast = Interpolator.interpolate(message) + {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + + assert interpolated_message == "Do you Hugo, know 2 ?" + end + + test "it handles messags with only the beginning of interpolation syntax" do + first_name = "Hugo" + message = "hi {{ {{first_name}}" + + interpolated_ast = Interpolator.interpolate(message) + {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + + assert interpolated_message == "hi {{ Hugo" + end +end From fba2da6ea6d00dfff01e66f4eac094a0d4ead546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Wed, 15 Mar 2023 16:20:29 -0300 Subject: [PATCH 03/11] Fixes typo in test name --- test/kino_slack/messsage_interpolator_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/kino_slack/messsage_interpolator_test.exs b/test/kino_slack/messsage_interpolator_test.exs index db422af..013c090 100644 --- a/test/kino_slack/messsage_interpolator_test.exs +++ b/test/kino_slack/messsage_interpolator_test.exs @@ -33,7 +33,7 @@ defmodule KinoSlack.MesssageInterpolatorTest do assert interpolated_message == "Do you Hugo, know 2 ?" end - test "it handles messags with only the beginning of interpolation syntax" do + test "it handles messages with only the beginning of interpolation syntax" do first_name = "Hugo" message = "hi {{ {{first_name}}" From 6c94347de0f0b720d84cfb6360ce5d0170a7eb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Thu, 16 Mar 2023 11:51:58 -0300 Subject: [PATCH 04/11] Refactor to append_string instead of append_char --- lib/kino_slack/message_interpolator.ex | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex index ed272b6..bdc1596 100644 --- a/lib/kino_slack/message_interpolator.ex +++ b/lib/kino_slack/message_interpolator.ex @@ -15,15 +15,13 @@ defmodule KinoSlack.MessageInterpolator do interpolate(rest, ast) else _ -> - <> = "{" - ast = append_char(ast, char) - ast = append_char(ast, char) + ast = append_string(ast, "{{") interpolate(rest, ast) end end defp interpolate(<>, ast) do - new_ast = append_char(ast, char) + new_ast = append_string(ast, <>) interpolate(rest, new_ast) end @@ -43,16 +41,16 @@ defmodule KinoSlack.MessageInterpolator do {:<<>>, [], args} end - defp append_char(ast, char) do + defp append_string(ast, string) do {_, _, args} = ast last_arg = List.last(args) new_args = if is_binary(last_arg) do - last_string = last_arg <> <> + last_string = last_arg <> string List.replace_at(args, -1, last_string) else - args ++ [<>] + args ++ [string] end {:<<>>, [], new_args} From 3394ac54dcb9deb7eddccc0f823824b7681e61d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Thu, 16 Mar 2023 11:52:44 -0300 Subject: [PATCH 05/11] Apply suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonatan Kłosko --- lib/kino_slack/message_interpolator.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex index bdc1596..62e7da0 100644 --- a/lib/kino_slack/message_interpolator.ex +++ b/lib/kino_slack/message_interpolator.ex @@ -1,4 +1,6 @@ defmodule KinoSlack.MessageInterpolator do + @moduledoc false + def interpolate(message) do ast = quote do: <<"">> interpolate(message, ast) From fe357c13c8efbbae3668aebf58d05b526d49cd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Thu, 16 Mar 2023 11:53:10 -0300 Subject: [PATCH 06/11] Apply suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonatan Kłosko --- lib/kino_slack/message_interpolator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex index 62e7da0..196431a 100644 --- a/lib/kino_slack/message_interpolator.ex +++ b/lib/kino_slack/message_interpolator.ex @@ -37,7 +37,7 @@ defmodule KinoSlack.MessageInterpolator do ] } - {_, _, args} = ast + {:<<>>, _, args} = ast args = args ++ [interpolation_node] {:<<>>, [], args} From 6885c18bdd9107e5147a4c9384e6fdc4f396801c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Thu, 16 Mar 2023 14:17:32 -0300 Subject: [PATCH 07/11] Collect the interpolation AST args instead of the interpolation AST --- lib/kino_slack/message_interpolator.ex | 49 +++++++++++--------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex index 196431a..12cae9b 100644 --- a/lib/kino_slack/message_interpolator.ex +++ b/lib/kino_slack/message_interpolator.ex @@ -2,32 +2,32 @@ defmodule KinoSlack.MessageInterpolator do @moduledoc false def interpolate(message) do - ast = quote do: <<"">> - interpolate(message, ast) + args = build_interpolation_args(message, []) + {:<<>>, [], args} end - defp interpolate("", result_ast) do - result_ast + defp build_interpolation_args("", args) do + args end - defp interpolate("{{" <> rest, ast) do + defp build_interpolation_args("{{" <> rest, args) do with [inner, rest] <- String.split(rest, "}}", parts: 2), {:ok, expression} <- Code.string_to_quoted(inner) do - ast = append_interpolation(ast, expression) - interpolate(rest, ast) + args = append_interpolation(args, expression) + build_interpolation_args(rest, args) else _ -> - ast = append_string(ast, "{{") - interpolate(rest, ast) + args = append_string(args, "{{") + build_interpolation_args(rest, args) end end - defp interpolate(<>, ast) do - new_ast = append_string(ast, <>) - interpolate(rest, new_ast) + defp build_interpolation_args(<>, args) do + args = append_string(args, <>) + build_interpolation_args(rest, args) end - defp append_interpolation(ast, expression) do + defp append_interpolation(args, expression) do interpolation_node = { :"::", [], @@ -37,24 +37,17 @@ defmodule KinoSlack.MessageInterpolator do ] } - {:<<>>, _, args} = ast - args = args ++ [interpolation_node] - - {:<<>>, [], args} + args ++ [interpolation_node] end - defp append_string(ast, string) do - {_, _, args} = ast + defp append_string(args, string) do last_arg = List.last(args) - new_args = - if is_binary(last_arg) do - last_string = last_arg <> string - List.replace_at(args, -1, last_string) - else - args ++ [string] - end - - {:<<>>, [], new_args} + if is_binary(last_arg) do + last_string = last_arg <> string + List.replace_at(args, -1, last_string) + else + args ++ [string] + end end end From 4295319982f4f00009fcbe9bfec350f5bdc654a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Thu, 16 Mar 2023 14:30:10 -0300 Subject: [PATCH 08/11] Tests that the code generated by the interpolator looks nice --- test/kino_slack/messsage_interpolator_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/kino_slack/messsage_interpolator_test.exs b/test/kino_slack/messsage_interpolator_test.exs index 013c090..d470d47 100644 --- a/test/kino_slack/messsage_interpolator_test.exs +++ b/test/kino_slack/messsage_interpolator_test.exs @@ -9,8 +9,10 @@ defmodule KinoSlack.MesssageInterpolatorTest do message = "Hi {{first_name}} {{last_name}}! 🎉" interpolated_ast = Interpolator.interpolate(message) + generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + assert generated_code == "\"Hi \#{first_name} \#{last_name}! 🎉\"" assert interpolated_message == "Hi Hugo Baraúna! 🎉" end @@ -18,8 +20,10 @@ defmodule KinoSlack.MesssageInterpolatorTest do message = "One plus one is: {{1 + 1}}" interpolated_ast = Interpolator.interpolate(message) + generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + assert generated_code == "\"One plus one is: \#{1 + 1}\"" assert interpolated_message == "One plus one is: 2" end @@ -28,8 +32,10 @@ defmodule KinoSlack.MesssageInterpolatorTest do message = "Do you {{first_name}}, know {{1 + 1}} ?" interpolated_ast = Interpolator.interpolate(message) + generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + assert generated_code == "\"Do you \#{first_name}, know \#{1 + 1} ?\"" assert interpolated_message == "Do you Hugo, know 2 ?" end @@ -38,8 +44,10 @@ defmodule KinoSlack.MesssageInterpolatorTest do message = "hi {{ {{first_name}}" interpolated_ast = Interpolator.interpolate(message) + generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) + assert generated_code == "\"hi {{ \#{first_name}\"" assert interpolated_message == "hi {{ Hugo" end end From 05c4ad191b68685917ad7e417a67a47ca0a5f4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Fri, 17 Mar 2023 10:21:53 -0300 Subject: [PATCH 09/11] Improves test readability --- test/kino_slack/messsage_interpolator_test.exs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/kino_slack/messsage_interpolator_test.exs b/test/kino_slack/messsage_interpolator_test.exs index d470d47..bd1180c 100644 --- a/test/kino_slack/messsage_interpolator_test.exs +++ b/test/kino_slack/messsage_interpolator_test.exs @@ -12,7 +12,7 @@ defmodule KinoSlack.MesssageInterpolatorTest do generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) - assert generated_code == "\"Hi \#{first_name} \#{last_name}! 🎉\"" + assert generated_code == ~S/"Hi #{first_name} #{last_name}! 🎉"/ assert interpolated_message == "Hi Hugo Baraúna! 🎉" end @@ -23,20 +23,20 @@ defmodule KinoSlack.MesssageInterpolatorTest do generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) - assert generated_code == "\"One plus one is: \#{1 + 1}\"" + assert generated_code == ~S/"One plus one is: #{1 + 1}"/ assert interpolated_message == "One plus one is: 2" end - test "it interpolates expressions with functinos and vars inside a message" do - first_name = "Hugo" - message = "Do you {{first_name}}, know {{1 + 1}} ?" + test "it interpolates funtion calls inside a message" do + sum = fn a, b -> a + b end + message = "1 + 1 is: {{sum.(1, 1)}}" interpolated_ast = Interpolator.interpolate(message) generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) - assert generated_code == "\"Do you \#{first_name}, know \#{1 + 1} ?\"" - assert interpolated_message == "Do you Hugo, know 2 ?" + assert generated_code == ~S/"1 + 1 is: #{sum.(1, 1)}"/ + assert interpolated_message == "1 + 1 is: 2" end test "it handles messages with only the beginning of interpolation syntax" do @@ -47,7 +47,7 @@ defmodule KinoSlack.MesssageInterpolatorTest do generated_code = Macro.to_string(interpolated_ast) {interpolated_message, _} = Code.eval_quoted(interpolated_ast, binding()) - assert generated_code == "\"hi {{ \#{first_name}\"" + assert generated_code == ~S/"hi {{ #{first_name}"/ assert interpolated_message == "hi {{ Hugo" end end From bf443f1f66ab8289661c0266f9ae0e25b638b2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Fri, 17 Mar 2023 10:49:53 -0300 Subject: [PATCH 10/11] Avoids appending to the end of a list --- lib/kino_slack/message_interpolator.ex | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex index 12cae9b..725d6bc 100644 --- a/lib/kino_slack/message_interpolator.ex +++ b/lib/kino_slack/message_interpolator.ex @@ -2,7 +2,8 @@ defmodule KinoSlack.MessageInterpolator do @moduledoc false def interpolate(message) do - args = build_interpolation_args(message, []) + args = build_interpolation_args(message, [""]) + args = Enum.reverse(args) {:<<>>, [], args} end @@ -13,21 +14,21 @@ defmodule KinoSlack.MessageInterpolator do defp build_interpolation_args("{{" <> rest, args) do with [inner, rest] <- String.split(rest, "}}", parts: 2), {:ok, expression} <- Code.string_to_quoted(inner) do - args = append_interpolation(args, expression) + args = add_interpolation(args, expression) build_interpolation_args(rest, args) else _ -> - args = append_string(args, "{{") + args = add_string(args, "{{") build_interpolation_args(rest, args) end end defp build_interpolation_args(<>, args) do - args = append_string(args, <>) + args = add_string(args, <>) build_interpolation_args(rest, args) end - defp append_interpolation(args, expression) do + defp add_interpolation(args, expression) do interpolation_node = { :"::", [], @@ -37,17 +38,16 @@ defmodule KinoSlack.MessageInterpolator do ] } - args ++ [interpolation_node] + [interpolation_node | args] end - defp append_string(args, string) do - last_arg = List.last(args) + defp add_string(args, string) do + [head | tail] = args - if is_binary(last_arg) do - last_string = last_arg <> string - List.replace_at(args, -1, last_string) + if is_binary(head) do + [head <> string | tail] else - args ++ [string] + [string | args] end end end From b449bb1c4a66ba2e501a7041d5668087417f6208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Bara=C3=BAna?= Date: Fri, 17 Mar 2023 14:58:26 -0300 Subject: [PATCH 11/11] Refactors Interpolator to use a buffer for the string chunk being processed --- lib/kino_slack/message_interpolator.ex | 36 ++++++++++---------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/kino_slack/message_interpolator.ex b/lib/kino_slack/message_interpolator.ex index 725d6bc..0867529 100644 --- a/lib/kino_slack/message_interpolator.ex +++ b/lib/kino_slack/message_interpolator.ex @@ -2,33 +2,32 @@ defmodule KinoSlack.MessageInterpolator do @moduledoc false def interpolate(message) do - args = build_interpolation_args(message, [""]) + args = build_interpolation_args(message, "", []) args = Enum.reverse(args) {:<<>>, [], args} end - defp build_interpolation_args("", args) do - args + defp build_interpolation_args("", buffer, acc) do + prepend_buffer(buffer, acc) end - defp build_interpolation_args("{{" <> rest, args) do + defp build_interpolation_args("{{" <> rest, buffer, acc) do with [inner, rest] <- String.split(rest, "}}", parts: 2), {:ok, expression} <- Code.string_to_quoted(inner) do - args = add_interpolation(args, expression) - build_interpolation_args(rest, args) + acc = prepend_buffer(buffer, acc) + acc = prepend_interpolation(expression, acc) + build_interpolation_args(rest, "", acc) else _ -> - args = add_string(args, "{{") - build_interpolation_args(rest, args) + build_interpolation_args(rest, <>, acc) end end - defp build_interpolation_args(<>, args) do - args = add_string(args, <>) - build_interpolation_args(rest, args) + defp build_interpolation_args(<>, buffer, acc) do + build_interpolation_args(rest, <>, acc) end - defp add_interpolation(args, expression) do + defp prepend_interpolation(expression, acc) do interpolation_node = { :"::", [], @@ -38,16 +37,9 @@ defmodule KinoSlack.MessageInterpolator do ] } - [interpolation_node | args] + [interpolation_node | acc] end - defp add_string(args, string) do - [head | tail] = args - - if is_binary(head) do - [head <> string | tail] - else - [string | args] - end - end + defp prepend_buffer("", acc), do: acc + defp prepend_buffer(buffer, acc), do: [buffer | acc] end