gorenje/erlang-red

用于 Erlang 的可视化低代码流程编程环境,灵感来源于 Node-RED。

License

查看许可证

73 stars 6 forks

Erlang-RED - 使用 Erlang 编写的 Node-RED 后端

这是一个实验项目,旨在用一个 100% 兼容现有流程代码的 Erlang 等价物来替换 Node-RED 现有的 NodeJS 后端。

目标是将低代码可视化流程编程的优势带给一种从一开始就为消息传递和并发设计的编程语言,也就是 Erlang。

[1] = 100% 兼容是不可能的,因为用 JavaScript 编写的 function 节点不被支持 (或者不打算支持,除非有人有变通方法)。

为什么?

Node-RED 是一个创建描述并发处理流程的惊人[*]工具,只是 NodeJS 是单线程的有点可惜。 那么为什么不使用从一开始就是多进程的东西呢? 并发是得到保证并且包含在内的。

此外,除非一个人掉进一个加了 Lisp 调味的 Prolog 大锅里,否则 Erlang 不是最容易理解的编程语言。

那么,拥有低代码可视化流程编程的简单性和 Erlang 的性能(和并发性)岂不是很棒吗?

[*] = 在_可扩展性_(节点可以很容易地被第三方添加和开发)、可理解性(Node-RED 术语 很直观)和_可用性_(一旦一个矩形成为一个计算单元,一条线成为数据流的路径)方面,它都是惊人的。

开发策略

我的开发过程最好描述为流程驱动开发,它基于一系列测试流程,以确保节点功能得到正确实现 - 意味着它与现有的 Node-RED 功能相匹配。

为了更好的可维护性,以及与现有 Node-RED 安装的集成,测试流程被镜像到一个单独的仓库中。

架构

架构以及流程编程和 Node-RED 的背景已撰写

就目前而言,由于测试流程,代码库具有许多相互依赖性:assert 节点 需要了解 web-socket 通信,或者由于节点的性质:complete 节点需要知道 已完成的消息

而当 异常 发生 时,exception 节点 需要知道异常。

这种互连的架构图可能和代码本身一样令人困惑。

支持的节点 & 功能

这是一个部分或完全工作的节点的不完整列表:

Node | Comment ---|--- catch | 捕获选定节点和整个流程的异常,但不捕获组的异常 change | 支持许多运算符,但不是全部。 基本形式的 JSONata 也受支持。 complete | 可用,并且可以用于某些节点,但不是全部 debug | 仅调试整个消息,不支持单个 msg 属性。 支持将 msg 计数作为状态。 delay | 支持静态延迟,不支持通过 msg.delay 设置的动态延迟 exec | 支持执行和终止命令,但仅适用于 spawn 模式下且在节点上设置的命令。 不支持将参数附加到命令。 支持超时。 也支持终止消息。 file in | 适用于位于 /priv 中的文件 function | 适用于任何 Erlang 代码。 停止和启动也受到尊重。 不支持超时和多个输出端口。 http in | 适用于 GET 和 POST,不适用于 PUT、DELETE 等 http request | 对执行请求的基本支持,任何复杂的操作可能都无法工作 http response | 正常工作 inject | 适用于大多数类型,除了 flow、global ... join | _计数为 X 的手动数组_可以工作,不支持 parts json | 正常工作 junction | 正常工作 link call | 正常工作 - 也支持动态调用 link in | 正常工作 link out | 正常工作 markdown | 正常工作,并支持 earmark 支持的任何内容。 mqtt in | 应该可以工作 mqtt out | 应该可以工作 noop | 非常支持什么都不做 split | 支持将数组拆分为单独的消息,不支持字符串、缓冲区和对象。 status | 正常工作 switch | 大多数运算符都有效,以及基本的 JSONata 表达式 template | mustache 模板可以工作,但不支持解析为 JSON 或 YAML trigger | 默认设置应该可以工作

Elixir & Erlang-RED

Elixir 助手可以添加到 erlang-red-elixir-helpers 仓库。

没有什么能阻止任何人用 Elixir 创建一个完整的节点,前提是有一个 Erlang "节点包装器",即 src/nodes 目录中的一小段 Erlang 代码,它引用了 Elixir 节点。

最初的 markdown 节点 示例是一个引用 Elixir 代码的 Erlang 节点。 我还编写了一个 Elixir 包装函数,因此我也可以直接从 Erlang 代码中引用 Earmark。 这是一个风格选择。

我打算使用 Elixir 代码将 Elixir 库导入到项目中,并减少用 Elixir 编写节点。 我只是更喜欢 Erlang 语法。 但各有各的喜好 :)

构建

$ rebar3 get-deps && rebar3 compile

测试

$ rebar3 eunit

开发

$ rebar3 shell --apps erlang_red

在浏览器中打开 Node-RED 可视流程编辑器:

$ open -a Firefox http://localhost:9090/node-red

Docker

我使用 Docker 来进行开发,所以对我来说,以下方法有效:

prompt$ git clone git@github.com:gorenje/erlang-red.git
prompt$ cd erlang-red
prompt$ docker run -it -v $(pwd)/erlang-red:/code -p 9090:8080 -w /code --rm erlang bash
docker> rebar3 shell --apps erlang_red

然后从 Docker 主机机器上,打开一个浏览器:

prompt$ open -a Firefox http://localhost:9090/node-red

这应该会显示 Node-RED 可视化编辑器。

Release

可以捆绑一个release

$ rebar3 as prod release -n erlang_red

所有静态前端代码 (用于 Node-RED 流程编辑器) 和 priv/testflows 中的测试流程文件都被捆绑到 release 中。

Cowboy 服务器将在端口 8080 上启动,除非设置了 PORT 环境变量。

Fly.io 部署

提供了一个示例 Dockerfile Dockerfile.fly,以便轻松启动一个实例作为 fly 应用程序。 提供的 shell 脚本 (fly_er.sh) 设置了一些常见的预期参数用于启动。 高级用户可能希望检查其中的 fly launch 行,并根据他们的要求进行调整。

Heroku 部署

在使用 heroku 上的容器堆栈后,部署变成 git push heroku,在通常的 heroku 设置之后:

但是 Dockerfile.heroku 不会启动流程编辑器,该镜像旨在运行一组流程,在这种情况下(在撰写本文时)是一个简单的网站,只有一个页面。

基本上,这个 流程red-erik.org 站点。

该镜像通过设置以下 ENV 变量来实现这一点:

另请注意,Erlang-RED 支持一个 PORT 环境变量,用于指定 Cowboy 将监听连接的端口。 默认值为 8080。

Heroku 使用它来指定 Docker 镜像连接的端口,以便其负载均衡器可以正确处理。

示例

img

该 gif 显示的是使用 Erlang 作为后端执行一个 简单流程。 该流程演示了 switch 节点中 "check all" 或 "stop at first match" 的区别。

所有节点都是进程 - 这在终端窗口的左侧显示。

这个例子非常简单,但它为扩展奠定了基础。

测试

为了为此创建单元测试,Node-RED 前端已通过导出对话框上的 "Create Test Case" 按钮进行了扩展:

img

测试流程存储在 testflows 目录中,下次调用 make eunit-test 时将被拾取。 通过这种方式,可以直观地创建单元测试。

流程测试也可以在流程编辑器中进行测试,有关更多详细信息,请参见下文。

流程测试套件现在维护在一个单独的 仓库中,但此处有重复。

Assert 节点

为了更好地支持流程测试,创建了两个新节点:

img

"Assert Failed" 节点使单元测试失败,如果消息到达该节点,无论任何消息值如何。 它基本上与 assert(false) 调用相同。 目的是确保不会到达流程的特定部分。

第二个节点(绿色)相当于一个 change 节点,只是它包含对消息对象属性的测试。 可能的测试包括 "equal"、"match"、"unset" 及其各自的倒数。 这里的目的是测试消息是否通过特定的值,否则单元测试将失败。

这些节点是必要的,因为没有其他方法可以测试流程是否正常工作。

另请记住,这些流程测试旨在确保 Erlang 后端正确实现节点功能。 这些节点的目的是_不是_确保流程是正确的,而是确保已实现节点的_功能_可以正常工作并继续正常工作。

可视化单元测试

我的计划是创建代表 Erlang-RED 需要实现的特定 NodeRED 功能的测试流程。 这提供了回归测试和实现的 todos。

我创建了一个键盘快捷键,用于直接从流程编辑器创建和存储这些测试流程。 但是我仍然使用终端来执行测试 make eunit-test - 这变得很痛苦。 因此,相反,我将此测试拉入 Node-RED,正如 gif 演示的那样:

img

该 gif 显示的是我的单元测试列表,只需按下一个按钮,就可以对所有测试进行测试。 每个测试的通知都会显示结果。 此外,树形列表显示哪些测试失败/成功(红色 "x" 或绿色复选框)。 也可以单独执行测试,以便可以单独检查故障。

但最好的是所有错误都被推送到调试面板,从那里我可以直接转到导致错误的节点。 可视化单元测试已完全集成到 Erlang-RED 中。

我的目的是创建许多小型流程,这些流程代表 Erlang-RED 需要实现的功能。 这些单元测试显示了与 Node-RED 的兼容性,并减少了 Erlang 代码的正确性。

贡献

非常欢迎以 Erlang 代码或 Node-RED 测试流程的形式提供的贡献,理想情况下是使用 Erlang 实现。 也欢迎 Elixir 代码,只是它有自己的

每个测试流程应仅测试一个功能,并使用 assert 节点来检查预期结果的正确性。 测试也可以是 pending 的,以表明相应的 Erlang 功能仍然缺失。

可以在 Erlang ForumNode-RED Forum 上提出问题和解答。

分支技术

分支还是不分支,这实际上不是一个问题。 我目前直接在 main 上工作,但确保所有测试在推送之前都成功,因此 main 分支将始终有效。 我在本地使用分支工作,但不想公开这些分支,因为我是在自己工作。

版本控制完全是随机的,目前几乎没有意义。 我更喜欢使用 Milestones,因为这些都是已经实现的,而不是计划好的,我不知道何时会达到下一个 milestone。

如果这个项目变得更具协作性或成为 "生产就绪的软件",那么将对开发过程应用更多的确定性,即引入语义版本号。

如果有人在分支上开发了某些东西并且无法合并,我很乐意处理冲突 - 我知道每天多次直接推送到 main 不是一件可行的事情,但我不喜欢让代码闲置数周之久,因为代码不在发布计划中而无法合并。

编码是一个创造性的过程,创造性是无法计划的。 想象一下梵高按照发布计划工作。

致谢

NickDave 带来了 Node-RED - 惊人的质量和灵活性以及整个 Node-RED 社区

非常感谢

免责声明

在此代码库的创建过程中,没有人工智能受到伤害。 此代码库是旧式搜索引擎 (ddg)、stackoverflow、博客文章和 RTFM 技术。

另请注意,该项目部分使用了 Don't do Evil 无法执行的许可。 该许可的重点不是可执行,而是让读者思考什么是邪恶。 毕竟,利奥教皇(新的那位)确实说过"邪恶不会得逞" - 这_到底_是什么意思?

关于

用于 Erlang 的可视化低代码流程编程环境,灵感来源于 Node-RED。

Topics

erlang node-red fbp flowbasedprogramming visual-flow-based-programming erlang-red

Resources

Readme

License

查看许可证

Activity