Well my example are meant to be sketches that show the principle I've been talking about and they certainly neither want to be model implementation how one ought to do this specifically, or claim that the specific implementation is best.
In essence a lot is completely missing.
So yeah I could/should have used the attributechanged in place of Refresh, but it doesn't change anything in the concept.
Note that my display never changes any data stored, except to "register" a "callback" Refresh. If I replace that with attributechanged it doesn't even "look" as if it does. Don't take this implementation literally, I just wanted to have a quick way for the Display to let the DO know where to call back if a refresh is useful and I wanted to have explicit control the DO to decide when to do the callback (rather than table-implicit handling of the same). One can absolutely do this with a callbackhandler, I just had no time to polish any of this... just tried to get a working example going.
I see very little harm in the way I have implemented this actually except that there is no handle for the callback, so currently it's tricky to safely unregister, but that's details.
I don't understand at all why there is a need to split a DO from another addon that "updates the data". As you see my implementation works as is, there really is no need to separate those, in fact I don't see any benefit from doing so.
I do know that the intention of some with respect to the lib was not full data/display abstraction. The whole point of the example is to illustrate that this is possible regardless, if one loosens certain assumptions.
I actually disagree that this is a special case, rather it's a different approach to standardizing exchangable data interfaces.
The approach here is close to what open sound control (OSC) does for music data exchange (the MIDI successor) in that contributors to the exchange can define their data and provide model implementations. Other implementations can then be made given the now defined interface. This is a widely used method and works sensibly well in practice despite being mostly bottom-up.
And it doesn't proclude a "standard" core at all. I.e. to have a standard defined functionality call very well cohabitate with what I am doing. For example you have fubar, statblock and HUD type displays for your intentions, and you have a model implementation for durability via ArmoredMan and statusbar histogram. People can choose any set of any of these combinations as they see fit. If they want a ArmoredMan with a look that fits statblock they can implement the display without touching anything else.
If kgpanels want to also react to DO data they can too by just grabbing the data.
e.g. a kgpanel can change if durability is low or if repair bills are too high.
There is a lot of stuff currently hardcoded here but this is not necessary nor intended. It grabs DurabilityDO simply because I was too lazy to scan for DOs that provide armory slot information and then letting the user choose if they wanted to link those to the armoredman and if, to what attributes of it. But the design doesn't preclude this at all. All this is display side stuff that can be easily added. I just hadn't had the time to write any kind of user config UI. As said, treat it as a concept mockup not a finished product.
Hacking that scanning in shouldn't take me too long so I'll add that as soon as I get around to it. It wasn't really the concept I felt needed demonstrating.
It is kind of important to note that this is not meant to be hardcoded at all. Hence why I said I wanted to provide multiple DOs and Displays to illustrate the idea. I'll hack up some more DOs to show that the whole point is to not hardcore stuff. There is absolute abstraction in semantics between data and display, and yes if you wanted to map time to armored man pieces, you could, or durability to to anchor positions of statblocks, same.
The only thing that got defined is that there are DOs that will provide data that relate to inventory armor slots. That's all that.
If you look carefully, only those fields (plus the refresh callback) are the points of interaction between DO and displays. Everything else is really flexible, including the choice to even make the connection.
Any display that recognizes these can render that information in any way they want and any DO that decides to provide data for it can do so to be recognized. Hence why you can write a DO that will make the ArmoredMan or statusbar histogram dance without changing a line of code in the latter. Hence ArmoredMan can handle more than one DO, I just haven't gotten around to mock up a second one, but it's trivial to do so (I'll mock one up tonight).
And ArmoredMan as it's currently implemented doesn't support any of the attributes that were discussed so far (well except for .text). This isn't by design, it's mostly because my goal was to illustrate that you can do other things too. Certainly one can embed the current ArmoredManDisplay easily in a tooltip and have it live quite happily in a mouseover tooltip interaction. Just that the way it's currently written it doesn't have to be that way and it can also show up elsewhere.
There is a rather big issue of language confusion. Because what Elk calls DO isn't a DO in my case. And what I call display is often what Elk calls a DO. So it's tricky. I have tried to work around this by calling my DOs "pure" DOs (i.e. DOs which never ever have any frame or other display related actions to handle, they only ever handle data, their semantics/descriptions and its refreshing) and displays DODisplays (which "populate the tooltip" or any other display). Yes, in Elk's language you could make an ElkDO that essentailly is a set of "pure" DOs and DODisplays, as said earlier.
Maybe I should best make a separate thread to avoid having these two strands of ideas cause too much confusion and maybe come up with a new set of words to delineate display/data?
In any case the point of the mockup is to show that clean data/display separation is perfectly feasible.
For those interested, I've forked this on github and made the changes I feel are necessary (a bit of cleanup, and more callbacks). I've already started testing it with EXPBlock, and will soon be converting over my other "Blocks".
Also note, I WILL NOT provide a standalone version of this lib. EMBED ONLY. Before you bitch, LDB-1.1 is 50 lines of code, LibStub is 30. I don't bring in CBH, so the addon has to provide that or otherwise ensure it's loaded (if it's standalone). CBH is of course the meat of the lib, but it's also going to be present with any addons using Ace3's event handler.
Also I'm not quite sure about all the callbacks for data. Wouldn't a key one be enough for virtually all interesting cases? Currently it has to check callbacks 4 times for every table write in 1.1.
I can think of cases for each...
"LibDataBroker_AttributeChanged" - Addons that just want to listen to everything... lazy assholes.
"LibDataBroker_AttributeChanged_"..name - Addons that want to listen to all changes to a specific Model, the most common case probably.
"LibDataBroker_AttributeChanged_"..name.."_"..key - Addons that just care about one or two keys on a Model, this is the one I've been using. Also handy to register different actions to different keys (set icon, set text, set script handler, etc)
"LibDataBroker_AttributeChanged__"..key - For addons that only care about one key, but wish to catch it on all Models. A bar addon might like this, catch all "text" changes across the board, then sort out which model it's for after the fact.
The all-Model callbacks might not be needed, but it's cheap enough to fire... I'd rather cover all bases (the lack of model+key callback was why I didn't use LDB-1.0).
Quote from Kagaro »
and just set the meta on that attribute table so you could build build the object core, then fire off RegisteredEvent, i think that is what Elsia was looking for
I was actually trying to think of a way to do that cleanly... that'll work perfectly.
I'm still not convinved that for my purposes I want the per table write callbacks at all. Typically a data provider (model) reads and populated a range of data, and then one callback would be appropriate, for my purposes callback per write is massive overkill, but I guess it's a case of different needs to begin with.
But even for icon+text blocks I'm not sure if it warrants 2 callbacks if someone refreshes icons and text. Rather I'd write text + icon and then call back to get a refresh. The problem there isn't the callback numbers but more that the addon that will get the callback will likely have to assume that it needs to update the display, and do that twice.
Of course one could bypass this by only registering a callback to a "refresh" entry, but...
That's really an issue with the Display's design, IMO. If the Display is doing a FULL REFRESH when only the text changed, then it's the Display's fault for being inefficient. Why would you want to reread the icon and reapply it if it didn't change? That's exactly why I added the name+key callback, when "text" is changed I want to rewrite my fontstring, when "icon" changes I want to apply the new texture. I don't want to redraw the entire frame when the only change was the icon texture. Especially if there's other keys the Model is writing that I do not use.
ok, Ill dinker with it after i get my Hunter to 68... But when pulling from the SVN in your dir, while it pulls externals I get prompted for username / password and cancel and get:
External: D:\Games\World of Warcraft\Interface\AddOns\Fortress\libs\LibDataBroker-1.0
Error: PROPFIND request failed on '/wowace/branches/LibDataBroker-1.0/Elkano/LibDataBroker-1.0'
Error: PROPFIND of '/wowace/branches/LibDataBroker-1.0/Elkano/LibDataBroker-1.0': authorization failed (http://dev.wowace.com)
EDIT: ok, tried to run it, got the below error:
[2008/07/05 22:47:50-5914-x1]: Broker_Coords-1.0\Broker_Coords.lua:1: Cannot find a library instance of "LibDataBroker-1.0".
!StopTheSpam-2.00.11200\libs\AceLibrary\AceLibrary.lua:49: in function `LibStub'
Broker_Coords-1.0\Broker_Coords.lua:1: in main chunk
manually downloaded LibDataBroker-1.0. Get this errors: (I am sure due to SVN update stopping before this one is downloaded)
[2008/07/05 22:43:58-5913-x1]: Broker_Coords-1.0\Broker_Coords.lua:5: Cannot find a library instance of "AceBucket-3.0".
!StopTheSpam-2.00.11200\libs\AceLibrary\AceLibrary.lua:49: in function `LibStub'
Broker_Coords-1.0\Broker_Coords.lua:5: in main chunk
Disabled Broker_Coords and Same error as above but with Clock.