Closed JDGrimes closed 8 years ago
What we essentially have in the hooks API is several fundamentally different types of events. Or, that is, at least we want to handle them in fundamentally different ways. This makes me wonder if we need to work on an event type API (#111).
Something that isn't clear in my mind yet is what would be consuming this API. That mainly affects how we would structure it, but it does also help us to think of different info about events that would be useful.
Obviously, anything related to events is going to be related to event listeners as well. However, it isn't just the event listeners themselves that need to process the events, they are also important in the UI, and various extensions/tangents of each of these.
In the listeners, we generally only need to consider information for single events at the time that they occur, although we may need to consider the events in bulk when hooking up to listen to them (depending on how that is done). When it comes to the UI, the events certainly do have to be considered en mass. We may not always want to present all registered events in a particular UI though, which means we'll either want some way to query them by event info or else we'll have to check each event individually.
The hooks API is naturally going to be a primary consumer, and in fact, we've always considered the events just a part of the hooks API. But as I reflect on this more, I am wondering whether we'll ever find use for events beyond the basic reaction concept. How would events ever be useful apart from a UI where the user specifies how the event is handled—in short, reactions? I suppose though that a plugin could conceivably hook into the events instead of into the WordPress action API directly. (Though at present I think it would likely need its own router.) But I'm not sure that is really useful, or that it is something that we want to encourage. However, with some of the recent discussion, I guess I've sort of begun to think of the event API as being more generic, and not as tied to just the hooks/reactions API. I guess this is really partly because we've been considering some things that would split the reactions from being an integral part of the hooks API to just one fork of it, or one thing built on top of it. This would be essentially what would happen if we split the router as considered in #129. I guess this really isn't all that important though to our current discussion. We can just focus mainly on the needs of the reactions API for now.
We have to remember that we are dealing with both generic and specific information, sort of as with the entities. There is generic information about an event, but there is also information about a particular fire of that event.
Much of the information comes from the args of the event, and from the context in which the event fires. The former can be generically described, but I'm not sure that the latter really can. We can describe what type of entity each arg is, and also I suppose how those entities relate to one another (not just in the database, but in terms of the event itself), to the context, and to the event itself. The first of these is the easiest, and in fact, we already do that. For the others though, the information is not as straightforward or "absolute". It is more subjective because it isn't defined so much in terms of the bare facts but "concepts", which are ultimately dictated more-or-less by the consumers. There are a few basics that I think we can cover, however.
For example, we can offer information about whether an arg is modified in the event, and how. We already provide this information in the most basic form through the is_stateful()
method of the event args (see #64). We don't offer specific information about how the arg is modified, though. One reason that we might wish to refrain from building that API out too far is that we'll likely mainly be listening for entity changes through MySQL queries and the entity change API in the future.
We could also offer other information about how the args relate to the event, like whether they were active or passive in regard to it. Which arg initiated the event? Which one was acted upon? Things like that. However, this information really isn't needed at present, and I'm not sure that it will really ever be necessary within the reaction API.
As far as how the args are related in the database, I don't think that that is really important, because this can be determined at run-time. In fact, it can only be determined at run-time for most events. It isn't really intrinsic, and if it is, we'll tend to generally only register one of the args anyway (whichever one we consider "primary"), since that is all that is necessary. (We've discussed that before, probably in #78 somewhere.) Of course, there is nothing to prevent a router or handler from considering that in an API, but it doesn't need to be part of the event info API.
But these are all really arg-related info I suppose. Is there any other info that we could offer about events? I don't know that we've ever really been able to think of any. Perhaps that is partly why we haven't really introduced an event types API: the "event type" definitions aren't really derived from the events themselves, but from their args. I guess that we could really say that the args define the event, since we can't seem to define the events at all apart from them though. But it has still seemed strange that the arg info should "bubble up" to define the event types. It has seemed better to just let the args speak for themselves, and let each API interpret them, rather than defining explicit event types. And I suppose that makes some sense in terms of information integrity as well, because it precludes a developer from accidentally mixing an event type with the wrong arg behavior.
Anyway, I can't think of any other entity info right now, but maybe we'll come up with something eventually.
Beside the args, there is the event context of course, as pointed out above. However, this is something that is even more closely tied to each particular event fire than even the arg values are, if possible. So as with the arg relationships, there is the possibility that at times the event could be limited to a particular situation in this regard, but for the most part the context won't necessarily be intrinsic to the event. On the other hand though, maybe this would be intrinsic more often than not. The context can of course, be determined at run-time, so this doesn't have to specifically be offered in the main info API, unless we specifically need to use it in the UI or something. As far as that goes though, I suppose the concept of "network-wide" events might actually make sense. Events that occur during network context maybe should only be presented in the network admin. It doesn't really make sense to present them in site-specific admin sections, because they can't occur in the context of that site. I suppose that ultimately this isn't about presentation though, but the context of the reaction stores and how the reactions are therefore handled. So it isn't so much that they shouldn't be presented there, but that the reactions would never be able to actually fire, since they'll be bound to that context (not as a result of that presentation, rather they are presented there for this reason), while the event can only occur outside of it. So for this reason perhaps it would be a good idea to allow the event to provide this information.
Cont.
One potential issue with the context info is that it is not something that is set in stone. It is tied to the actions, not the events. And since the event can be tied to several different actions, another module can come along and add an action to that event in a different context. So maybe the solution is to allow the module to also then filter the context info for that event. Though changing the context of an event might have other implications as well, depending on where and how it is taken into account (like in the UI as discussed above).
And on second thought, I suppose that it wouldn't just be adding a new action that would affect this, but a plugin or WordPress core moving an action call or adding a call of an action in a different context. But I guess that is kind of similar to an action's args being modified or a new action call being added without all of the args. That is more obvious to the developer than a change of context though, and is therefore less likely to occur, or if it did occur, to be recognized as a bug. Shifting contexts might take some devs by surprise. Although, something as serious as network vs site context would have a noticeable impact, because certain functions (like get_current_blog()
) and some globals would be affected. But code that didn't take that into account wouldn't suffer, so it isn't something that would be as likely to be noticed (or "fixed", if it was even considered broken on the part of the dev).
Also, as we said above, some actions will simply not be tied to any particular context, and so for those it doesn't really make sense to define a context for the event. I wonder whether this might actually be the case with most (all?) repeatable events. Which would mean that we might ultimately not have much use for specifying context of events down the road, since repeatable events will likely be all that we have in the future. But if it is useful for the other events for now, we may still want to pursue it.
But what sort of events are there that would be context bound? Can we think of any examples? Actually, most everything that I can think of would be context-bound only because of being bound to a context-bound UI, not because of anything intrinsic. Which makes me wonder if it might be better to think of this info as a list of suggestions/hints rather than a single absolute value. But then in that case maybe this would be better off as a simple filter in each UI implementation or something rather than a full-fledged part of the event info API. On the other hand though, other solutions like that wouldn't be auto-recomposible, whereas providing this info through the event info API would make it much easier for a new UI to come along and begin taking advantage of it right away.
So basically what we're considering is supplying a list of contexts where each event is known to occur. This list would be extensible/modifiable, either through a filter or some other way.
I'm not sure this actually entirely solves the above conundrum though. Because if a new UI is introduced that includes the definition of an entirely new context, the contexts specified by the existing events would be irrelevant. But I'm not entirely clear on this, so let's consider the example of a new context from #57: a trainer and trainees. Actually, that doesn't seem to be about context at all, but rather storage grouping. Or would the trainer be a context after all? But then, the trainer context would be a sub-set of the site context, or actually, I suppose that it would be its own top-level context, because users are cross-network constructs. So then, I suppose that this would work as far as the trainee being able to be awarded in any context, but how would we pull up reactions for a particular trainer? That is, how would any particular trainer context be current at the time that the trainee was using the site? I suppose though that the trainer context could be determined based on the current user, though this is rather strange. Things also become complex if the trainee could have multiple trainers, because then the trainer context has multiple values at the same time! Unless of course each of these was considered a different type of trainer, and so a separate context. Anyway, events couldn't really occur relative to this particular context anyway in the sense that we are concerned about at the moment, so the whole point seems to be moot.
In conclusion, supplying a list of contexts in which an event is known to take place could be a benefit for the entity change events. However, at the moment the future of that part of the API is uncertain, and so I'm not sure we really need to pursue this part of the event info API at this time. We can still accomplish the proposed use-case for this information via a simple filter in our UI code, which would allow particular events to be skipped. That's something that we actually already do now anyway.
One potential issue with the context info is that it is not something that is set in stone. It is tied to the actions, not the events. And since the event can be tied to several different actions, another module can come along and add an action to that event in a different context. So maybe the solution is to allow the module to also then filter the context info for that event.
Actually, the fact is that it would be OK for a particular reaction to hook into an event even though sometimes it would fire out-of-context of where that reaction is stored. This would just mean that the reaction wouldn't be able to fire in those out-of-context places, only when the event occurs within context. This is actually OK, but the implications might not be obvious to the user, and it is also complicated by action types (but that is another story).
Even a plugin calling an action in a different context wouldn't necessarily be a problem. If it was out of context, the reaction just wouldn't be brought into play, as above. However, I wonder whether there is another possibility here, where the action is not called out of context, but rather in a different sub-context. I guess that wouldn't matter for the use-case that we've been considering, but perhaps it would make a difference in other applications.
Completely moving the action would have problems however, because it would mean that the reactions tied to that event but stored in a sub-context would cease to work.
So if we aren't going to pursue the contexts right now, and we really can't think of any other info except about the args, I guess we need to look at the arg info that we offer and consider what else might be useful.
Currently we define what type of entity each arg is, and we also define whether the arg is a bystander from the state (is "stateful"), or else is an active participant in the action. I guess the latter is intended to actually imply specifically that as an "active participant" the arg was modified in some way—that is, its "status" was changed. See #78. The toggle events go on to incorporate the actions that result in a reversal of the status change as well. So the toggle events supply information not only about the args, but about how particular actions relate to one another.
None of this information is provided on the event objects, but rather on the arg objects, which are registered independently of the events.
And beyond these basics, we don't specify whether a particular arg initiated the event, etc. We could specify which arg was being used, and which arg was the user. We could also in theory specify exactly how the arg was being used, or to what purpose, but that is conceptual, and concepts of that sort are yet to be defined by some future consumer of the API. We could, however, specify something as simple as whether the event was being created, read/viewed, updated, or deleted. This is similar to #118, except there we were pursuing something more elaborate, in that it also specified exactly how the entity was modified (which attributes) and attempted to provide before and after snapshots. Although we could do that too if we thought that that information would be useful. Then again, is any of this information really useful to us at the moment? The main thing that we might use it for is in relation to the toggle events, and we currently intend to handle them though an entirely different API at some point in the future. And so it doesn't necessarily make sense to require events to specify information that might not really ever be used.
Another thing that complicates this is the fact that this is information that has to be defined on the actions and not the events. Well, actually, we could define it on the events, except for the fact that the toggle events will be composing not just multiple actions that reflect the same thing, but multiple actions that reflect opposite changes (like creations vs deletion of an entity). In fact, I suppose that when it comes to the repeatable events, there might not be much need for multiple actions per event at all.
So here is another thing that is complicating the API just for the sake of the toggle events. Without them we could probably combine the actions and events together, since there would be a one-to-one relationship between actions and events. In other words, the repeatable events probably wouldn't have to relate different actions to one another at all, we only have the events doing that in the API mainly because of the toggle events.
I'm kind of leaning against adding anything to the information we currently offer about events right now. We don't have a clear use-case for anything, and we're just bogging down the effort to get the initial version of the API out. It now seems to be pretty certain that this is an API that is going to continue to evolve, and maybe we'll even end up completely revamping the events in the near future. So it something like that happens, there will be another opportunity for us to add more info to events if we find that it is needed.
Actually, as far as the context goes, the "stateful" args or args that are from the current state are context-dependent args, or, that is, they are state-dependent args. I suppose though that the really have the same potential difficulties as discussed for the contexts above: when an action is called in different locations, the state isn't necessarily going to be consistent. In fact, the state might not even be entirely consistent even when the action is being called in a single location. We've hinted at this before in https://github.com/WordPoints/hooks-api/issues/15#issuecomment-142095846, I think.
However, as with the contexts, I suppose that all this means is that the reactions referencing those args just won't work for those action fires where that arg has no value set. We've sort of been aware of that, I guess, and are comfortable with it.
In the interest of thoroughness, I've compiled a list of the actions and their contexts and how their args are modified, just to give us an idea of what information we would be offering if we did offer it.
Action | Context | Arg | CRUD |
---|---|---|---|
comment_approve |
site | comment | U |
comment_new |
site | comment | C |
comment_deapprove |
site | comment | U |
post_publish |
site | post | U |
post_depublish |
site | post | U |
add_attachment |
site (& network?) | post\media | C |
post_delete |
site | post | D |
user_register |
site or network? | user | C |
user_delete |
network or site? | user | D |
user_visit |
any? (mainly site) | - | n/a (R?) |
I suppose that any of these could take place in the network admin, but the site-context ones would have their args in context of the "main" site on the network, so in that sense they would still be in site context, despite being in the "network admin". I'm unsure if any media uploads are used in the network admin or not, and whether user delete and user register can happen in both network and site context.
For the user visit action, I say that it can be in any context, but it is actually mainly limited to the site context because it can only take place on the front end, and any front-end page is going to be in site context (or some sub-context), as far as I can see.
As far as the CRUD info for the args, I think that this generic level of info isn't necessarily useful. Well, I guess maybe it could be useful, but it would be woefully insufficient to really be of value in terms of putting together an entity change API.
As far as that last comment goes, possibly the create and delete info would be more useful than the update info. It is really the update info that we're talking about when we say that this is insufficient.
Note also that as far as the comment_new
action, this is actually not a generic comment create hook, it only fires for comments that aren't marked as spam (which can happen on creation of the comment when using Akismet or the like). So it isn't just about comment creation at all but about semantic meanings of the comment's attributes (what status the comment has), just as with the update hooks.
Perhaps the entity delete information would be useful for reactors to consider how to handle the event, or how to incorporate information from the entity into it. Just a thought.
This has been making me think, that what actions are registered reflects how we want to use those actions and the information they offer. In other words, when we are utilizing this information, it isn't always just in a secondary manner. Sometimes we might be using the information itself to determine whether to listen to an event at all or not, or whether to incorporate a particular action into an event (maybe dynamically). And when the information is primary like that, we need to have completeness and parity as discussed in #116. But sometimes it is hard to think of it that way unless we have the consumer here to remind us of it. I haven't been thinking of this that way at all. But I guess the point is that just tacking information like this onto events/actions doesn't necessarily do us much good unless we are offering all of the information that a consumer would need (parity) and are offering it for all of the actions that exist (completeness). That is, at least when we are thinking in terms of an API that relates actions to one another in any way. For a pure repeatable-event only API, I suppose that completeness would really be a moot matter, because events would generally always be composed of just a single action, as discussed above. And so for the repeatable events, just tacking information like this on without heeding what other actions might supply complimentary information (in the eyes of some action-relating consumer), is OK.
So we shouldn't worry about whether this information is useful in terms of consumers that relate actions to one another, because it either certainly won't provide the parity (as with the entity change API) or else it probably won't be complete. But we should continue to consider whether this information might be useful to consumers that might use it to determine whether or how to handle an event—of itself, not relative to other actions.
So, while it is tempting to try to be as complete as possible anyway, the problem is that we can't define "complete" apart from a particular consumer. And so unless we have specific use-case in mind where we would be composing actions dynamically, there is no use in attempting to be exhaustive in action definitions at this point. Currently, however, we use events and action types to manually compose the actions together, which means that we don't accidentally create half-events when only part of the actions are available.
Even with events like the post publish (repeatable) event, isn't it still true that the action can be reversed, and that we might want to take that into account? And so even there we might want to relate events that bring us to that particular state of being to events that move us away from that state of being. Yes, it is true that we might not always want to reverse these events, but isn't there that possibility? Still, I suppose that this would be best accomplished through the entity change API, not through an action API. So while these might remain (pseudo-?)events and not possession awards as discussed in #119, they would still be quasi–state-change events, not exactly repeatable events.
If we did add this info right now (just the basic stuff in the table above), how would we present it? One issue with deciding this is that we currently aren't sure exactly how we'll be consuming the information. It sort of makes sense to add it to the actions, and indeed, this is almost necessary as long as we are supporting different action types. The reason that I shy away from that, however, is that it makes it more difficult to offer the information at all. We can register it along with the other action info, but then we have to decide how to store it. It could be stored on the registry or the router, I guess. But either way that puts information in memory that we don't necessarily need to always be there (although it could be useful to query this information, I suppose). There isn't really any good way to get around that however, because we currently just use a generic class for the objects for most actions, so we can't just put the information in the action objects instead, as we might do if we were storing this information for the events themselves.
I think if we could just stick this information in the event objects, I might go ahead and add it now. But because we'd have to come up with a way to store it, I'm leaning toward punting this until such time as we'd really need the information, and have a clearer idea of how we'd use it.
Also, since many of these actions are specifically tailored to a particular use, even if we did register this information, it wouldn't help us much since those actions probably wouldn't be ones that we'd continue to use after the entity change API is introduced; they'll likely be deprecated and removed.
In https://github.com/WordPoints/hooks-api/issues/123#issuecomment-216324613 we discussed the possibility that there may be more information about events that we could provide in the events API. The focus there was on distinguishing different types of event args, but really, any information about an event could prove useful sometime, I suppose. In this issue we'll explore what types of event information might exist and perhaps how to present that information in a consumable format.