Skip to content

Commit f6f56ec

Browse files
committed
initial support for an Elixir module. Elixir code now supported in form of modules.
1 parent 7ea00b2 commit f6f56ec

File tree

3 files changed

+126
-4
lines changed

3 files changed

+126
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"disabled":false,"env":[],"id":"963265474ffd443b","info":"","label":"[.elixir] support basic module code","type":"tab"},{"crontab":"","id":"571e4daf846a8006","name":"","once":false,"onceDelay":0.1,"payload":"11","payloadType":"num","props":[{"p":"payload"}],"repeat":"","topic":"","type":"inject","wires":[["34ae86365c57eb65","0aac6be93a243378"]],"x":223,"y":491,"z":"963265474ffd443b"},{"code":"defmodule MyModule do\n @typedoc \"This type\"\n @typedoc since: \"1.1.0\"\n @type t :: term\n\n @doc \"Hello world\"\n @doc since: \"1.1.0\"\n def hello do\n \"world\"\n end\n\n @doc \"\"\"\n Sums `a` to `b`.\n \"\"\"\n def sum(a, b) do\n a + b\n end\n\n def minus( c, d ) do \n d - c\n end\nend","id":"ab4986a24e578611","module_name":"MyModule","name":"","type":"elxmodule","wires":[],"x":370,"y":205,"z":"963265474ffd443b"},{"finalize":"","func":"#{ <<\"payload\">> := Payload } = Msg,\nMsg#{ <<\"payload\">> => 'Elixir.MyModule':minus('Elixir.MyModule':sum(123,Payload),1) }\n","id":"34ae86365c57eb65","initialize":"","libs":[],"name":"function 1","noerr":0,"outputs":1,"timeout":0,"type":"function","wires":[["9c1a04aa4e37eb28"]],"x":382,"y":456,"z":"963265474ffd443b"},{"id":"9c1a04aa4e37eb28","ignore_failure_if_succeed":false,"name":"","rules":[{"p":"payload","pt":"msg","t":"eql","to":"-133","tot":"num"}],"type":"ut-assert-values","wires":[[]],"x":641,"y":386,"z":"963265474ffd443b"},{"finalize":"","func":"Msg#{ <<\"payload\">> => 'Elixir.MyModule':hello() }\n","id":"0aac6be93a243378","initialize":"","libs":[],"name":"function 2","noerr":0,"outputs":1,"timeout":0,"type":"function","wires":[["412ccdda2ab5a0db"]],"x":382,"y":526,"z":"963265474ffd443b"},{"id":"412ccdda2ab5a0db","ignore_failure_if_succeed":false,"name":"","rules":[{"p":"payload","pt":"msg","t":"eql","to":"world","tot":"str"}],"type":"ut-assert-values","wires":[[]],"x":611,"y":596,"z":"963265474ffd443b"}]

src/ered_startup.erl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ install_module_node_code([], _WsName) ->
141141
ok;
142142
install_module_node_code([NodeDef | ModeNodeDefs], WsName) ->
143143
case {is_module_node(NodeDef), disabled(NodeDef)} of
144-
{true, false} ->
145-
ered_node_erlmodule:install(NodeDef, WsName);
144+
{{true, ModuleHandler}, false} ->
145+
ModuleHandler:install(NodeDef, WsName);
146146
_ ->
147147
ignore
148148
end,
@@ -361,6 +361,7 @@ node_type_to_module(<<"range">>) -> ered_node_range;
361361
node_type_to_module(<<"erlprocess">>) -> ered_node_erlprocess;
362362
node_type_to_module(<<"erlcaptureio">>) -> ered_node_erlcaptureio;
363363
node_type_to_module(<<"amqp-in">>) -> ered_node_amqp_in;
364+
node_type_to_module(<<"elxmodule">>) -> ered_node_elxmodule;
364365

365366
%% TODO: don't do this again! The kafka nodes are based on an existing
366367
%% TODO: Node-RED node package --> @asinino/node-red-kafkajs that uses
@@ -431,6 +432,8 @@ is_supervisor(_) ->
431432
is_module_node(NodeDef) when is_map(NodeDef) ->
432433
is_module_node(maps:get(<<"type">>, NodeDef));
433434
is_module_node(<<"erlmodule">>) ->
434-
true;
435+
{true, ered_node_erlmodule};
436+
is_module_node(<<"elxmodule">>) ->
437+
{true, ered_node_elxmodule};
435438
is_module_node(_) ->
436-
false.
439+
{false, undefined}.

src/nodes/ered_node_elxmodule.erl

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
-module(ered_node_elxmodule).
2+
3+
-behaviour(ered_node).
4+
5+
-include("ered_nodes.hrl").
6+
7+
-export([start/2]).
8+
-export([handle_msg/2]).
9+
-export([handle_event/2]).
10+
11+
-export([install/2]).
12+
13+
%%
14+
%% Erlang-Red Elixir module node for defining modules in Elixir
15+
%%
16+
%% This does not nothing except for install code into the BEAM.
17+
%%
18+
%% The install process isn't managed by the node, instead the startup
19+
%% procedure does the installation. That is because there has to be an
20+
%% order maintained in how things happen. Another node wanting to use this
21+
%% code must have that code already installed.
22+
%%
23+
24+
-import(ered_nodered_comm, [
25+
node_status/5,
26+
node_status_clear/2,
27+
post_exception_or_debug/3,
28+
send_out_debug_warning/2
29+
]).
30+
31+
-import(ered_erlmodule_exchange, [
32+
remove_module_for_nodeid/1,
33+
add_module/2
34+
]).
35+
36+
%%
37+
%%
38+
start(
39+
#{<<"module_name">> := ModuleName} = NodeDef,
40+
WsName
41+
) when ModuleName =:= <<>>; ModuleName =:= "" ->
42+
node_status(
43+
WsName,
44+
NodeDef,
45+
"module name not defined",
46+
"red",
47+
"dot"
48+
),
49+
ered_node:start(NodeDef, ered_node_ignore);
50+
start(NodeDef, _WsName) ->
51+
ered_node:start(NodeDef, ?MODULE).
52+
53+
%%
54+
%%
55+
handle_event(?StopEvent, #{<<"id">> := NodeId} = NodeDef) ->
56+
remove_module_for_nodeid(NodeId),
57+
NodeDef;
58+
handle_event(_, NodeDef) ->
59+
NodeDef.
60+
61+
%%
62+
%%
63+
handle_msg(_, NodeDef) ->
64+
{unhandled, NodeDef}.
65+
66+
%%
67+
%%
68+
install(
69+
#{
70+
<<"module_name">> := ModBinaryName,
71+
<<"code">> := ModuleCode,
72+
<<"id">> := NodeId
73+
} = NodeDef,
74+
WsName
75+
) when ModBinaryName =/= <<>>, ModBinaryName =/= "" ->
76+
ModuleName = binary_to_atom(ModBinaryName),
77+
78+
FileName = binary_to_list(
79+
list_to_binary(
80+
io_lib:format("/tmp/~s.ex", [ModuleName])
81+
)
82+
),
83+
84+
file:write_file(FileName, ModuleCode),
85+
86+
try
87+
case 'Elixir.Code':compile_file(list_to_binary(FileName), nil) of
88+
[] ->
89+
node_status(WsName, NodeDef, "no code found", "blue", "dot");
90+
[{FullModuleName, _Binary}] ->
91+
add_module(NodeId, FullModuleName),
92+
node_status(WsName, NodeDef, "installed", "green", "dot"),
93+
spawn(fun() -> clear_status_after_one_sec(WsName, NodeDef) end);
94+
R ->
95+
post_exception_or_debug(
96+
NodeDef,
97+
?AddWsName(#{somethingelse => R}),
98+
"something else"
99+
),
100+
node_status(WsName, NodeDef, "something else", "blue", "ring")
101+
end
102+
catch
103+
E:F:S ->
104+
node_status(WsName, NodeDef, "compile failed", "red", "dot"),
105+
post_exception_or_debug(
106+
NodeDef,
107+
?AddWsName(#{exception => E, error => F, stacktrace => S}),
108+
"compile failed"
109+
)
110+
end;
111+
install(_NodeDef, _WsName) ->
112+
ignore.
113+
114+
%%
115+
%%
116+
clear_status_after_one_sec(WsName, NodeDef) ->
117+
timer:sleep(1000),
118+
node_status_clear(WsName, NodeDef).

0 commit comments

Comments
 (0)