How do you create and load modules dynamically at runtime in Elixir, or Erlang?

Have you checked: Dynamic Compile Library by Jacob Vorreuter. See example below

1> String = "-module(add).\n -export([add/2]). \n add(A,B) -> A + B. \n".
"-module(add).\n -export([add/2]). \n add(A,B) -> A + B. \n"
2> dynamic_compile:load_from_string(String).
{module,add}
3> add:add(2,5).
7
4>
Also, check out this question and its answer

As you described, there are many different approaches you could take by ultimately they boil down to two different categories: 1) code compilation and 2) code evaluation. The example you described above requires compilation, which will define a module and then you would have to invoke it. However, as you found out, it requires defining a module name and potentially purging and discarding those modules when the database changes. Also, note that, defining modules may exhaust the atom table, as an atom is created for every module. I would only use this approach if you need to compile at maximum a dozen modules.

(A small note, Code.compile_string already defines the module, so you don't need to call load_binary).

Maybe a simpler approach is to call Code.eval passing the code from the database, thus code evaluation. It works fine if all you want to is to evaluate some custom rules. The Code.eval accepts a binding, which would allow you to pass parameters to the code. Let's suppose you have "a + b" stored in the database, where a and b are the parameters, you could evaluate it as:

Code.eval "a + b", [a: 1, b: 1]

EDIT BASED ON QUESTION'S EDITS

If you are evaluating code, keep in mind that Code.eval returns the result of evaluating the code and the new binding, so your example above would be better written as:

q = "f = function do
x, y when x > 0 -> x+y
x, y -> x* y
end"

{ _, binding } = Code.eval q
binding[:f].(1, 2)

However, given your use case, I wouldn't consider using eval but I would indeed compile a module. Since the information is coming from the database, you can use this fact to generate unique modules per record for you. For example, you can assume developers will be adding the contents of a module to the database, something like:

def foo, do: 1
def bar, do: 1

After you get this information from the database, you can compile it using:

record   = get_record_from_database
id       = record.id
contents = Code.string_to_quoted!(record.body)
module   = Module.concat(FromDB, "Data#{record.id}")
Module.create module, contents, Macro.Env.location(__ENV__)
module

Where record is anything that you get back from the database. Assuming the record id is 1, it would define a module FromDB.Data1 which then you would be able to invoke foo and bar.

Regarding code reloading, both defmodule and Module.create use :code.load_binary to load the module. Which means that if you update the module, the old version is still kept around until another new version is loaded.

One of the things you should add as well is cache expiration, so you don't need to compile a module on every request. This can be done if you have a lock_version in the database that you increment every time you change the record contents. The final code would be something like:

record  = get_record_from_database
module  = Module.concat(FromDB, "Data#{record.id}")
compile = :code.is_loaded(module) == false or
            record.lock_version > module.lock_version

if compile do
  id       = record.id
  contents = Code.string_to_quoted!(record.body)
  contents = quote do
    def lock_version, do: unquote(record.lock_version)
    unquote(contents)
  end
  Module.create module, contents, Macro.Env.location(__ENV__)
end

module

Code.eval can be used to define a module:

iex(1)> Code.eval "defmodule A do\ndef a do\n1\nend\nend" 
{{:module,A,<<70,79,82,49,0,0,2,168,66,69,65,77,65,116,111,109,0,0,0,98,0,0,0,11,8,69,108,105,120,105,114,45,65,8,95,95,105,110,102,111,95,95,4,100,111,99,115,9,102,117,...>>,{:a,0}},[]}
iex(2)> A.a
1

Does this help?