sasa1977 / exactor

Helpers for simpler implementation of GenServer based processes
MIT License
683 stars 23 forks source link

Possibility of custom process registration dispatching logic? #27

Open Qqwy opened 7 years ago

Qqwy commented 7 years ago

Right now, it is possible to either return a PID, or to register using :global or :via. However, this needs to be given as option when ExActor.GenServer is included (used).

This unfortunately which means that it is impossible to build custom logic, to e.g. register at {:via, Registry, {MyRegistry, "user_#{user_id}"}}.

Maybe, a possibility would be to allow users to write a custom export function, which is used for the different functions to transform the first argument into a term like above that could be used to do the actual GenServer dispatching on.

sasa1977 commented 7 years ago

It is possible to use via tuple by using private macros (defstartp, defcastp, ...), but this requires additional wrapping, and pretty much makes ExActor as noisy as GenServer. I discussed this in #26.

Your proposal is very interesting, and at first glance I like it. We could support a callback function (e.g. server_pid/1), which would by default be an identity function. It could be overridable, so in the implementation you can override the behaviour and turn it into e.g. a via tuple. The function should be invoked only if export option is not provided.

Would you be willing to make a PR? The code is somewhat convoluted, what with all the macro logic, so if you don't feel like it, I could give it a stab.

Qqwy commented 7 years ago

Yes, I am willing to give the creation of a PR a go :-).

sasa1977 commented 7 years ago

Good luck :-) If you get stuck somewhere, feel free to ask.

Qqwy commented 7 years ago

I think the cleanest way would be to just define server_pid as a normal function that can be overridden (by ExActor specifying defoverridable [server_pid: 1]) by the user. Do you agree?

sasa1977 commented 7 years ago

Yeah, that's what I thought as well.

Qqwy commented 7 years ago

All right.

I worked a little bit on this today, as I finally had some spare time.

However, I'm still unable to find the proper location to add the new logic.

I am guessing it is close to Exactor.Operations.server_ref/2, but I am unsure what server refers to in the quote(do: server) statements. (Is this a snippet that is inserted somewhere else in an unhygienic way?)

sasa1977 commented 7 years ago

I pushed a small refactoring commit into a separate branch. With that change, the definition of the interface function for calls/casts is now done in the single place.

Roughly speaking, you need to do the following. If type is neither :multicall nor :abcast, and :export option is nil or false, you need to do something along the lines of:

  1. Split gen_server_args into head/tail (outside of the quoted block).
  2. Inside the quoted block invoke the new callback with injected head from step 1.
  3. Modify the invocation in line 587 and pass the result from step 2 followed by unquote_splicing(tail_from_step_1).

If type is :multicall or :abcast, or :export option is neither nil nor false, you should use the same quoted code as in line 587. So you'll add an if before line 586, and then either return the verbatim copy of the existing quote block, or a more elaborate quote block as described above.

I didn't try this out, so I might have missed some possible problems or fine-print details :-)

You can cherry pick my commit into your branch, and then try to see if my proposal takes off.