For AMF implementation, I have to define the mapping between Actionscript classes with something equivalent in Erlang. First thing came into my mind is record. However, I immediately encountered some of the difficulties when using records in Erlang
- Read: can’t use reflection easily after parsing AMF, I received the object with properties/values and I need to map to the corresponding record
- Write: Need to introspect the record and write its properties into binaries
I found this post from trapexit. However, I need specifically more than that.
Normally records are defined in a header file “.hrl”. And the idea is to generate utility codes (getter, setter, record meta data ….) at compile time. The heart of it is epp:parse_file/3 function. The generator (or the helper) reads header files, parses all records and create a compilable .erl file which contains all functions you need to do all magic with records:
- set(Obj, PropertyName, Value) -> {ok, NewObj, {PropertyName, Value}}
- get(Obj, PropertyName) -> {ok, Value}
- fields(record_name) -> Fields = [string()]
- fields_atom(record_name) -> Fields = [term()]
- type(Obj) -> record_name
messages.hrl – the source
record_helper.erl – the generator
record_utils.erl – the generated source
Below is the demonstration of how to generate and use the record utils
1> c(record_helper.erl).
{ok,record_helper}
2> record_helper:make(["messages.hrl"], ".").
ok
3> ls().
messages.hrl record_helper.beam
record_helper.erl record_utils.erl
ok
6> c(record_utils).
{ok,record_utils}
7> rr("messages.hrl").
[abstract_message,async_message]
8> Obj = #async_message{}.
#async_message{
parent =
#abstract_message{
clientId = undefined,destination = undefined,
messageId = undefined,timestamp = undefined,
timeToLive = undefined,headers = undefined,body = undefined},
correlationId = undefined,correlationIdBytes = undefined}
9> record_utils:set(Obj, correlationId, "ADF-123-DDF-543").
{ok,#async_message{
parent =
#abstract_message{
clientId = undefined,destination = undefined,
messageId = undefined,timestamp = undefined,
timeToLive = undefined,headers = undefined,body = undefined},
correlationId = "ADF-123-DDF-543",
correlationIdBytes = undefined},
{correlationId,"ADF-123-DDF-543"}}
10> {ok,NewObj, _} = record_utils:set(Obj, correlationId, "ADF-123-DDF-543").
{ok,#async_message{
parent =
#abstract_message{
clientId = undefined,destination = undefined,
messageId = undefined,timestamp = undefined,
timeToLive = undefined,headers = undefined,body = undefined},
correlationId = "ADF-123-DDF-543",
correlationIdBytes = undefined},
{correlationId,"ADF-123-DDF-543"}}
11> record_utils:get(NewObj, correlationId).
{ok,"ADF-123-DDF-543"}
12> record_utils:type(NewObj).
async_message
record_helper.erl is not so clean as there’re all string concatenations … further improvements can be:
- Using template language like ErlyDTL or ErlTL …
- Clean up code to have more friendly arguments
- You add ….
Trung AMF, Basic, Record autogenerate, instrospection, Record