-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use behavior stacking for handler implementation #146
base: main
Are you sure you want to change the base?
Conversation
Note: Benjamin Wilde published a video about this pattern he calls "Behaviour Stacking". This also makes implementing BYO handlers much easier as you can delegate to Also note: Right now only |
First off, I think the idea of simplifying the using block to something smaller by factoring out to plain ol' functions on ThousandIsland.Handler is a good one; it's easier to read, isolates the macro complexity, and allows for reuse when people do end up rolling their own. I like that part. What I'm less in favour of is the idea of making the user's handler not be the GensServer. the wrapper pattern used here (where ThousandIsland.Handler is the one implementing the GenServer callbacks and ThousandIsland.Handler is basically duck typed to GenServer) looks an awful lot like how eg That being said, we could probably get a good chunk of the way there with a combination of |
I hear you and I will update the PR. After all that's all the related ticket is about. For the sake of conversation and my understanding: All that |
I mean yes, but that's a bit of a load bearing 'All'. There's tricky things like subtle before_compile hook ordering, all the hooks that |
This fallback is not needed anymore because of the consistent API ;)
But... the process still is a GenServer. The only thing that changes is where the functions are defined which called by the Anyway I don't want to waste your time on this. I can refactor the MR as I said. The code will get be a bit messier because in this approach we really have to keep up the pattern matching so the callbacks don't get overwritten. |
Nonono, this is great! I just want to avoid having to steward code that's duplicating GenServer (to paraphrase Virdig, I don't want to write an ad hoc informally-specified bug-ridden slow implementation of half of GenServer :)
Stepping back for a moment, I think the ordered list of goals here is to:
The constraints are:
To be clear, I'd love to see the above happen (and greatly appreciate your effort in this!). Just so we're on the same page, can you just quickly lay out the overview of 'I can refactor the MR as I said'? |
okay, cool. I like this, too. Agree on goals and contraints. So what I am trying to say is that I think the current version of the MR is still within the constraints (besides BC, see below):
My approach delegates from ThousandIsland.Handler to actual Handler, your apprach (iiuc) is the opposite direction. My point being: It's just functions calling functions. Let's choose the direction that makes most sense.
Exactly. The process must be a GenServer. No additional processes (i.e. no message sending, just function calls)
My does introduce a breaking change in order to uniform the API. But it doesn't have to! handle_info can be delegated 1:1 wo keep the API as is.
The alternative approach would be to keep the function declarations inside the using macro the same as they were but move their body / implementation out to |
I've always had it in my head that the user's Handler is the thing that's directly interacting with all of the GenServer machinery in start_link etc. This has always just seemed easier to understand ('the On the other hand, going the other way & having ThousandIsland.Handler be the thing that directly interacts with all of the GenServer machinery would mean that users's Handlers are 'wrapped' and are dependent on ThousandIsland.Handler passing through all of the relevant functions (and having to make some concessions about whether it's ThousndIsland.Handler or the user Handler's state that gets displayed in things like So both extremes are kinda lousy. I wonder if something like the 'modular macros' approach that Phoenix uses for Endpoint would be a good compromise? We'd be able to keep most of the logic encapsulated in tight helper macros within ThousandIsland.Handler, but still have them lexically declared within the user's Handler via the WDYT? |
Okay I see. In my approach I had LiveView in mind where I believe (I don't claim understanding what's going on in detail) that I don't have experience with But yes, I guess using 'modular macros` could be a way to go. It might be a bit strange to use those macros to create a BYO handler but as you said, there are supposedly not many of those use cases. Guess I need to think this approach through a bit more. The downside of having the logic in macros is that you blow up your code as macros are injected into all |
I have refactored the code now to use the modular macros approach. I've moved two helper functions out of using though. wdyt? |
cf850a6
to
01b48fd
Compare
This looks great! Assuming you're good with this standing as is, I'm going to add a bit of documentation to the top (mostly to remember what exactly our design goals were here!) over the next few days and merge. |
01b48fd
to
863c68a
Compare
Yeah I'm totally fine with this. But Credo has a problem with the complexity level of |
Hey Mat
This is a refactoring proposal which addresses #144. Rather than "merging" the behaviors of
GenServer
andThousandIsland.Handler
, we can stack them. I.e. implement theGenServer
behavior insideThousandIsland.Handler
and delegate necessary bits to the actual handler implementing only the behaviorThousandIsland.Handler
.Though minimal, the changes sre not backward compatible as we change the interface of
handle_info
.Wdyt?