Retrieve source code programmatically?

Using the open source BPM library, Mozart, users can define an executable business process such as this very simple example:

 defprocess "Payment Approval Process" do
    user_task("Submit For Approval", group: "Accounts Payable", outputs: "Ready for Approval")
    user_task("Approve Payment (level one)", group: "Management", outputs: "Loan Approved (level one)")
    prototype_task("Remit Payment")
  end

FYI- Here’s a link to a complete example:

https://github.com/CharlesIrvineKC/opera/blob/main/lib/opera/processes/home_loan_app.ex

I have a POC GUI where I allow users to inspect running business processes. I would like for users to be able to view the code the source code of process definitions such as the one above.

Is the a way to do this?

Taking in consideration you are also using macros, this is not trivial to do correctly. I would recommend to take the easier path as you are the author of this library and save the business process source code inside of a internal data structure when expanding the macros.

1 Like

This sounds very promising, but how do I capture the source code in the context that you suggest? Thanks.

After thinking about it, it occurred to me that I could call Macro.to_string on the body of the macro. If I inspect the result of that, for this process definition:

  defprocess "process with one user task" do
    user_task("a user task", group: "admin")
    prototype_task("a prototype test")
  end
"user_task(\"a user task\", group: \"admin\")\nprototype_task(\"a prototype test\")"

So this gets me pretty close. I need to then:

  1. tack on the 1st and 4th lines of the process definition
  2. unescape the double quotes and then format the whole thing.

Number 1 seems trivial, but I currently don’t have an idea how to do #2.

For formatting, you should look at how mix format task is implemented, you might be able to use it to format a string.

BTW, you might also want to take a look at sourceror, it might have what you need out of the box.

FWIW, the absolute path to the file being compiled is available in __ENV__.file during compilation - you could File.read! from that and stash the binary somewhere for later.

1 Like

Kind of liking that idea. I could stash the binary on a module attribute and insert a function into the module to retrieve business process definitions by name - or something along those lines.

I wonder if an implementation of the String.Chars protocol for your process struct might not be a more maintainable approach? And potentially produce a “management level” readable definition?

Consider again this process definition:

 defprocess "Payment Approval Process" do
    user_task("Submit For Approval", group: "Accounts Payable", outputs: "Ready for Approval")
    user_task("Approve Payment (level one)", group: "Management", outputs: "Loan Approved (level one)")
    prototype_task("Remit Payment")
  end

The DSL does convert this to a struct, but the struct is generally unreadable without a great deal of effort, even by me.

Are you suggesting that the struct be parsed in reverse to produce the macro based process definition?

What do you mean by “management level” definition?

Thanks!!

Management level → readable by managers (process owners for example) that don’t necessarily understand code.

I occurred to me this morning on a morning stroll what you probably meant. And now, after reading your post and thinking about it, I think the “management” view is the way I want to go. There are a couple of options: a textual view or a graphical one. Either way, I would provide the user with the ability to drill down into subprocesses. Thanks.