#V10-Document-Model-RFC
1 messages · Page 1 of 1 (latest)
Ah finally more data.data breaking stuff, I was very disappointed by v9 in this regard 
I will definitely take the time to read this in detail but from a first glance, I quite like it. The circular references are something that is extremely hard to model correctly in TypeScript, so I would be very happy if that is simplified.
I'm strongly in favor of making this change, in particular because of the repeated issues and mistakes surrounding data.data.
however, I'd like to suggest rethinking the suggestion of "system", to be something slightly more descriptive like systemData.
(I'm referring to this:)
- actor.data.data.attributes.strength = 10;
+ actor.system.attributes.strength = 10;
my concern is that "system" sounds like it should contain the name of the system the actor belongs to, or stuff like that; as opposed to the intended meaning of, well, system data fields
I don't really have too much to add than "YES. PLEASE."
The data.data in particular have been a great confusion. Especially if you work around it in some places but forget in other places.
Yes, I'll need to rework a lot of code, but this will make it infinitely more readable.
my inner grinch hates all kinds of change, but a clearer distinction where what is Foundry data and what is system data does seem benificial in the long run
I would also very much welcome these changes, getting around to use actor.data.data amongst other things would make it so much more readable / understandable. I really like that data then becomes system, making it much clearer what it actually refers to.
I just got done with my initial read over and I strongly approve.
I know the number of warnings in the console could create some support hell until major systems and modules make their adjustments, but I know that would be a temporary bump in the road that would be well worth it in the long run.
As a system dev, I'm going to agree with the suggestion of moving data.data to system, with the known rub that is would break downgrading to v9 as it renames the data field. I'm not quite sure that is the best idea right now with that thought in place.
+1 to systemData over system as it's more explicit
Odds are, it could be shimmed in V10 to support both, but only use data on new documents, then v11 can make the move to system or what ever the new prefix is.
Could a "downgrade shim" be added in the last v9 patches that would account for this, I wonder?
Yeah, after reading the RFC, Spices concerns about support were also my immediate thought, people will need to be very intensely reminded to take a backup of their data so they will have something to return to since downgrading will not be immediately working.
But my inner dev does likes the changes proposed.
At least I've made a tool that us Mods have access to that would be easy to adapt into downgrading a move like that.
It's going to be very difficult to support moving back to V9 (or older) from a world which has been migrated to V10 - for reasons other than this particular change as there are other one-way data model migrations in V10
We don't have a good mechanism for going backwards except to restore from backup, and I worry the cost of trying to engineer one might be prohibitive.
Smash everything then!
ui.notifications.error("MAKE A GOD DAMN BACKUP BEFORE UPGRADING!!!", {permanent: true}) 😉
Take it as an opportunity to make changes to the data model that would be drawn out over some number of versions.
Yeah, that makes sense. It would be nice, buuuut it is what it is :)
I would definitely make sure there is a great big "BACKUP YOUR DATA BEFORE UPGRADING! HAVE YOU BACKED UP? SERIOUSLY, BACK UP!" message to the upgrade dialog :)
That was actually the reason we brought this proposal forward into the V10 scope as opposed to (as was initially proposed) waiting until V11 to consider it.
I'm all for it if only for this one reason
- document.data.data.*
+ document.system.*
- document.data.*
+ document.*
Yeah I love that
I hate it. I'm sorry, but for all the good it does, I'm used to data.data and my time to fix stuff is both, precious and limited. I suspect that using replaceAll in my IDE won't do the trick and I'd have to spend a significant amount of my free time to adjust to it. On top of the other changes I'd have to take care of. And then the modules which are only slowly updated on which youd have to wait. Nope, don't like it at all.
If we can have enough shims in v10 that most v9 code works as normal, I'd like that as I like to straddle 2 major versions.
There are good comments about system (more terse) vs systemData (more clear) - we would want to think about that before committing, although it does not alter the spirit of the proposal.
The changes sound pretty reasonable and desirable to me. Though the wiki's definitely gonna need a page on system updates for V10 to help devs out.
And I do share concerns regarding the one-way update, given the number of issues that sometimes causes
I'll write it up as a comment on the issue so it does not get lost for you.
How does this affect temporary documents which do not exist on the database? Would apply be the only way to update their data, or would update be shimmed to redirect to apply for local-only documents?
First thing I'm going to test is how much breaks if I just make a script to do those substitutions on all my 5e automation macros 😛 (and if the answer is "not much" then maybe release that as a "quick and dirty update to your automation" module)
Overall I think the ongoing benefit is worth the breakage, but also realise I have a lot less exposure to this than many others.
There's some code provided in the issue allowing you to make the redirection 'permanent' (and removing warnings) where you would not need to do any find/replace.
That would be a separate issue (one which you're familiar with) of whether the CRUD operations should (or should not) function for un-identified documents.
Threw a comment on the GL issue about moving .data for system data to .system/.systemData so it's not going to get lost.
As a developer at the "Code Monkey" level of competency, I had great difficulty adjusting to the changes made from v7 to v8 and only resolved them once a guide had been published in the wiki and I had sought some specific assistance. The changes from v8 to v9 were more subtle but rose up to bite me unexpectedly and are still not completely resolved (migration of system compendia for example).
That said, I am completely in favour of the proposed changes for all the reasons you state in the document.
However, I would ask that there be a transition guide created to help us understand what changes will be required of us as a result. (I do note that there were some tips in the document that would be a good start).
Thank you for creating this wonderful, complex and configurable foundation upon which we can build our own dreams.
a transition guide would be a must, and it will be particularly helpful if it also includes a basic script to resolve 90% of the migration (e.g. highlighting every usage of data.data in your module code, or in every macro in the world)
I mean, that's spelled out already. Do you expect them to write "hit ctrl+shift+r (or whatever your replace all command is), type in data.data, replace that with system, hit "replace all""
@mortal tinsel There's a date discrepancy between the GL issue and your original post here, in regards to the release of Prototype 1
There is more going on than just a find and replace.
Would it be that easy?
Kinda doubt it.
While it's somewhat frustrating to refactor the same things multiple times, I would not miss the data.data thing.
Well yeah, but the comments in there seem pretty all-encompassing already
even so, there is a lot of value in creating an automated tool that tells the developer exactly how to migrate (and maybe even edits files for them).
additionally, I think actual users will want something like this for their macros. migrating dozens of little unique snippets of code in every foundry world is a big problem that doesn't only involve module/system devs.
I'm just saying, it is reasonable for people to want a clean "make the changes and you're good" guide
Isn't it there already a dev migration article... Somewhere? If not, we need it.
I don't really disagree, but he specifically listed the example of highlighting every usage of data.data which doesn't need more than what he typed out in the question
The wiki has ones for 0.8.x and V9
The main issue with find and replace is when you have times you are referring to e.g. ActorData, so the system data is at thing.data, not thing.data.data (and such a bit of code will need refactoring anyway for this). Find and replace and check for errors is a decent first step, but only a first step.
Except that the string "data.data" will not nessesarily only exist in places that can be replaced with system. For isntance, if you have a function with a parameter caleld data you might have (as mentioned) data.data.data or similar. A nieve find/replace will break stuff.
I think that a more reliable approach than "find+replace and check for errors" which could introduce unexpected side effects which are hard to identify and catch is to instead force the behavior that you want to eliminate to fail (or log a warning) and then test your system until you have caught and eliminated the problems.
Given that the old data.data path would continue to work (with a logged warning) it removes the time pressure to fix it immediately
I think the main technique is going to be chasing deprecation warnings.
After reading the issue, I agree it's a good step forward. A tad painful maybe, but worth it on the long run. data.data will not be missed, and it would simplify the code internally and also externally (modules/systems and TS types)
you can just whittle away by replacing any usages over time when you see them show up in the logs
That being said, I have had entity/document issues that got missed for multiple major versions before someone got an error and reported it.
So just chasing warnings isn't enough unless you have unit testing already set up so that you can ensure every control path gets run.
Want me to be frank? Another version another months of fixing things in huge systems like 3.5e, probably breaking all my premium module importers and other utility mods. The deprecation path does not help - the problem is the approach to breaking stuff. It means I have to constantly spend time on upkeep, not on developing new things. If you to do giant refactors each time new version of Spring/Angular/Django/Windows/MacOS etc appear you would be infuriated. Here it seems like it’s norm.
You can always make the redirect permanent.
I think that's how most of the listed frameworks handle it. They just maintain the deprecated paths for a loooong time.
I appreciate your honest opinion, I think there are some counter-arguments to what you've mentioned but we do want this feedback so we can reflect fairly on how this change would resonate with developers in the community.
Very long. I could run apps I wrote for win XP on WIN10 no problem, same with old Spring apps and even old old (14+ years old) PHP scripts run on PHP 7 with minor changes
On the other hand, I think we can all agree that Windows and PHP are incredibly bloated pieces of software.
As someone who's day job is literally dealing both, yes.
I can't count the number of times as an Android & iOS dev back in the days I had to update my apps due to changes new versions of the OS's would bring
Yeah, I will see myself out for wanting to have stable APIs and not spend month looking for each tiny small log message in the limited free time I have.
I don't think anyone is criticizing you for that opinion. Or even disagreeing with it.
This will absolutely sink Sandbox for a long time. With every new core version I have struggled to find the time to update it. I am no coder, and it costs me tons of effort to keep the system updated. This change in the data system is massive in comparison with the previous changes (from my system point of view), and to be honest, the number of constant changes and need for updates is starting to be overwhelming. Apologies, but is how I feel about it
I know that refactoring feel, but consider doing this while being paid for it is different than having to spent your own free time on doing it.
There were jokes here where V8 landed that "Yeah, in V10 we will do DocumentArchetypes". The thing is, it stopped being jokes... 
Does anyone have a working unit testing structure in place for Foundry?
The question isn't "do breaking changes suck" the question is "do the benefits outweigh the consequences"
No idea. I know some modules have tests of some form or another. But I have no idea how deep any of them go.
Thanks for sharing your perspective. Would it be possible for you to provide a couple illustrative examples of how this change would most painfully impact you? Is it more because of changing reference paths, or is it more because of the potential for attribute collisions between the data model and the document?
I think you can pretty reliably count on the fact that each major version of Foundry VTT we are going to pretty dramatically change something. That is relatively fundamental to our ethos as a product. We obviously don't want to do it in an offhanded or reckless way, and we want to think carefully about ensuring our changes are substantial net positives for the software...
So, if I get ill, all my premium content one day will just stop working. And the more content are release, the more time I have to spend on the upkeep. You see that this is unsustainable in the long run.
I have 12 premium modules, and 3.5e system to update now. If I keep release schedule I will have 24 next year.
Do you suspect that some of your current premium content will cease to function if we make this change? And if so, would you be able to share an illustrative example of where you think the most significant breakage will be?
All because I do actor/scene updates in the import script, and read actor/scene data. I mean, they will for for the time being, but if I dont update them before V11 (that will bring another set of changes...)
😬
Chesus
I do understand that its pervasive - but I do want to clearly re-emphasize that this part of the proposal is non-breaking. It will continue to work as is without any change.
To be fair, this probably indicates that I should be passing the inner data around in my data flow instead of referencing it like this all the time...
so until v11 this will just give deprecation warnings?
Yes.
compatibility would be preserved much longer than just v11 (if not "forever")
and we are providing a way to disable the warnings
Yeah, so let's talk about the actually breaking thing.
Anyone got system data keys that will conflict?
Please make a huuuge note about that in the proposal.
Cause last time Deprecated meant "until next major"
And that is why I panicked, cause it always was like that until now.
Both references for Sandbox specific items and the collisions. Right now Sandbox creates info and templates using the data.data structure, building things from scratch, so not as easy as to do a replace. But the thing is it also creates new unique ids that are stored in the document, so I will need something to ensure the integrity of old Sandbox worlds by moving this ids to the new system data. I am not saying is not possible, is just that it will take me some time to analyse and assess
Yea, no note about how long Foundry will keep the compat shims.
And for that change 6 months (or 8 months if I can start before) is too little in case of 3.5e
Maybe an automatic backup could be a solution
I think it's ok like it is but just an idea
That's a fair call-out, we should have documented the proposed compatibility timeline more clearly. I would envision if we make this change we would keep the backwards-compatible shim around indefinitely or until we are confident that the impacts of removing it would be very limited to a few holdout packages.
That sounds a lot better!
You can always grep through all existing packages for data.data 😆
we would certainly encourage any new code to be written using document.systemData rather than document.data.data though.
Thats fair!
This is going to become an interesting educational challenge, given how much of learning the API tends to be looking at other modules.
globalThis.logger.warn(`You are accessing the ${this.constructor.name}#data object which is no longer used. ` +
"Since V10 the Document class and its contained DataModel are merged into a combined data structure. " +
"You should now reference keys which were previously contained within the data object directly.");
I imagine this log message will be shown in the thousands to end users who had no idea this change happened and just used an unmigrated macro. I suggest adding a URL, logged or in a comment next to this, that links directly to a short migration guide (i.e. not this entire RFC).
I'd like non-techy players who look at the console log to be able to see this message and then fix the data.data in their macro within a few minutes.
Also effects Macros
Ah my bad
My reading skills are failing me then 
As someone who makes macros and modules by smashing together code from existing ones (like seriously I can barely code my way out of a cardboard box), I think as long as it's communicated well enough "use systemData instead of data.data", it'll be fine. That's partly the job of the Foundry Team, but also the warning messages
I like it overall, but I definitely think the compatibility shim should stick around as long as is feasible to help cut down on maintenance work devs need to make.
Something else that I also saw in the V8 transition was that it was difficult to piece together what to look for aside from helping people understand the warnings in their console. Something like a well-commented diff of the changes needed to get Simple Worldbuilding (and/or my Boilerplate system) working in V10 could be a huge help to get people started with where to look for their updates.
I'd ask for more aggressive deprecation warnings as time passed, mild logging in V10, warning popup one time in V11, get's an error popup v12, then a large permanent pop up in the last version that supports it. Space the ramp up as practical.
How will this affect active effects which use e.g. data.attributes.foo.bar as a key? (Where data here is the system data). Those would also presumably need a migration
Hmm... that's an important point. We would need to add that as a supported migration to strip the leading .data from active effects.
could also affect things like configured token bars
It also affects custom effect alternative solutions…
Like the rule elements that are part of pf2e
As someone who started with little Javascript knowledge and only wrote some macros with a bit of help, fixing them myself felt kind of impossible, since I didn't even know why it broke or what broke. It just told me "This isn't working", but no idea how to replace it.
And the depreciation warnings generally weren't too helpful to me. I do know a good bit of Javascript now and could fix some, but most fixes I had to ask others because it wasn't obvious why things didn't work anymore.
Since the depreciation warnings were only shown in the console, and if I didn't check there, I'd never know my macro would break in a new version, since it's working just fine currently
The first launches of v10 will have so many warnings in the console 😅
Yea, I'm going to quash thread jacking here. Submit your own issue on the GitLab.
I believe, but need to test, that un-migrated active effects or token bars etc... would continue to work
But providing a migration for them would be preferred.
@mortal tinsel Would it be possible to surface warnings/errors in the UI when they are caused by a Macro?
Yeah, I've been a bit concerned about that and will be anxiously awaiting some input from the pf2e dev team on this thread at some point.
@unique parcel also might have input on this as he has a similar solution in his system
Fair I will use the thread properly: I am all for this change.
In the end does this mean that the structure and methods across actors and settings will be the same or just the object data subject to this change? I suppose I am asking if this also means there will be similar ways of saving data too, i.e. saving actor data vs saving data in a custom settings form?
How will changing the document about to be created in preCreate hooks work with this change? Currently, the intended way is to call update on the data passed into the hook callback. With this change, that doesn’t exist as a separate thing anymore. So how will that work instead?
In an ideal world for deprecations, I would want getProperty(actor.data, "data.foo") to return actor.system.foo - I'm guessing how well this works is down to how exactly the deprecation is handled, but wanted to be sure it was flagged. (It would currently break Memento Mori, though that's an easyish fix if we don't get this)
The DataModel#update method has been renamed to DataModel#apply, so in a _pre-operation you would apply() those changes to the Document. calling document.data.update() redirects (via the shim) to document.apply()
If there is a change from data to say system or systemData, then there would be a structure change to the data model. There are shims to allow data access noted, but if the data model changes, I'd expect a shim for that too.
Yeah, it can be tough to work through for sure. Here's a more specific example of what I mean on a diff of the changes. I put this together when I was doing my refactor of Boilerplate for v8, and it was pretty helpful to people who had used it as a base system: https://gitlab.com/asacolips-projects/foundry-mods/boilerplate/-/commit/b6cc41a8af860883cf7b7f790ac179185608627f?view=inline
Can confirm that getProperty(document.data, attributePath) will continue to work via the shim
I'm a little confused on the semantics of apply vs. update?
As with anything that involves needing to choose a name for something (like system vs systemData) - we are open to discussing and iterating on feedback. We chose apply because we felt it was the best available option which did not collide with update (which is reserved for actual database modification operations i.e. create/update/delete).
The idea being to disambiguate between modifying a database document, and mereley modifying an in-memory object? I'm not sure I see the need for that. In fact, I feel like it would be better if they shared the same contract and were interchangeable.
you can join Calego's bandwagon 😛
I would say side topic (not to try and dismiss your question)
Should there be another GL issue regarding it?
There is one (again, Calego's bandwagon)
As it relates to this conversation, however, there is an important technical requirement to have separate methods for (1) updating the data model locally in memory and (2) committing a change to that data model to the database. What is negotiable is what to name each of those methods.
Oh god, I come home from work, get two pings and this bombshell. 😄
I'd prefer the deprecations be phased in, across 3-5 versions...
Phase 1 (v10): Shim data and data.data as proposed, but make the deprecation warnings silent by default (or maybe just a single warning the first time it happens). Add in an easy way to globally enable these warnings by developers, and make sure there is a call stack included in the log.
Phase 2 (v11 or v12): Make the warnings noisy by default, with an easy way to temporarily silence them (so developers trying to fix other, more urgent issues can have cleaner logs).
Phase 3 (v12, v13, or v14): Remove the shims entirely - don't allow modules and systems to indefinitely use the old way, force an eventual upgrade to a single standard. It's no fun trying to add support for a new system only to find it doesn't follow the same standards as other systems, and it makes it harder for a developer of one system to jump in and help another system's developers.
Adding in migrations for macros to the new standard would also be hugely beneficial to non-developers who just copied macros from somewhere and don't understand how they work
So apply is an in memory update whereas update saves the update with the server? If so I 100% disagree with Lordzeel and think they should be named differently. If they both have the name "update" then we basically get back to the same problem this is solving just with methods instead of whole objects
Yeah, that’s what I understood
@keen rain will be awake soon. My gut feeling is that it will hurt in the short term but long term is preferable. The difference between us and most systems is that we can have a few people go and start sorting through, and if there are deprecation warnings we have the manpower to work through it if it is not a breaking change better than, well, literally everyone else - we simply have more “coding resources”. And given upcoming system evolutions I’d anticipate more people becoming involved with the project in the next few months and this is a good “learn the code base” one
My understanding is that there are two kinds of things:
- Documents that exist in the DB
- Data Models that exist only in memory.
(or maybe the naming is the reverse of that?)
But with this proposal, both will be usable as "a structured data object with a schema" but one will be modified by an update method, and the other by an apply method.
If my understanding is incorrect, then my thinking on this may change.
My understanding is slightly different:
So far, we had Documents, which had DocumentData (the data property). These may or may not be stored in the database. Calling update on a Document persists the changes if it's stored in the database, calling update on the DocumentData just changes it in memory, regardless of whether the document is persisted.
Both types of data model may be modified (locally) via .apply(changes). Only a Document which can be persisted to the database can be called with .update(changes) which are pushed to the server and written to disk.
With the suggested changes, both classes become one, but there is still a need for both methods. So DocumentData#update becomes Document#apply
that understanding is correct
maybe a slight amendment: there can be DocumentData subclasses that are not Documents, so those would not have an update method, just an apply method
From the perspective of systems and modules though, needing to know that one data model does or does not have a database backing and another does seems... like they need to know the underlying implementation too well.
In other words, if I call update I expect the document to know what to do, but I expect the result is that the data is stored appropriately.
So I guess for a local data model, update should be an alias for apply and for a database document, update will apply then also commit the change to the DB.
so basically you are saying, you want to be able to persist changes on all instances of DataModel / DocumentData by calling update?
I want the concept of persistence to be invisible.
My code shouldn't need to know if the data model persists or not.
I think depending on the context, you need to know. At least with how preCreate hooks currently work, for example
@stiff cliff the problem here is that there are use cases where you want to make a change locally but not persist to the DB. so having persistence "just work" for Documents would deprive you of that option (perhaps without some special parameter)
Just so I understand, from a user perspective if I purchased a large scale campaign like Rise of the Drow to the tune of $70 (or any expensive premium content for that matter). Would there be a point where assuming the dev doesn’t keep it updated, it would become unusable or inconvenient to use upon a Foundry update because of this change? And if so, when?
It's certainly possible that a particular package will cease to function as Foundry VTT evolves regardless of the topic of this particular proposal.
The design of Foundry VTT as a self-hosted software provides the user with freedom to choose which version of the software they want to use, allowing them the option of remaining on their current or preferred version indefinitely if they wish.
Certainly we don't want to force changes which break packages (premium or otherwise). As it relates to this particular proposal, it is unlikely that Rise of the Drow would be affected.
Would it be possible to have something like document.update(data, {commit: false}) for a local only update (which can default to local only for things that aren't present in the database, and to committing to the database for things that are in the database, and throw if commit:true is specified for a thing not in the database), which would be vaguely analogous to the temporary option for document creation.
I guess my take is that we have both update and apply, and for data models the update method is an alias (wrapped in a promise presumably) for apply.
Could I also suggest maybe sysdata instead of systemData. Only because I'm lazy and would rather not type in extra letters if I don't have to. 🙂
Would you also want a delete method on DataModel instances then? What about createEmbeddedDocuments and all of the other functions? There's a lot that only makes sense for Documents but not for DataModels
(Personally think you either want to go full semantic or full brief for things like this.)
info instead of sysdata? Lol.
.stuff
There could be a commit() method for persisting? Just a random spitball idea for that (going off of FE framework store methods)
Speaking more as a consumer of premium products than a developer I do worry the rather frequent large-scale changes to the API are going to be scary to prospective premium partners? From scanning through the above it seems there's a plan for a long-dated support period at least, but it probably doesn't calm nerves on an unstable API for investment purposes.
But at any rate, this change is pretty fundamental, how long of a "transition" or adoption timeframe would we potentially be looking at?
We discussed .commit(), which makes sense from a naming perspective, but we think that create, update, and delete are such standard verbs for these operations though that I'm reluctant to move away from that not to mention that Document#update is much more widely used than DocumentData#update was, so that would be a more impactful change which - for me - wouldn't be merited.
@mortal tinsel to come back to this: Wasteland-Ventures (my system) already scopes the keys that users can input to the system data. So that part wouldn't break for me.
As described in the RFC, there are two components. One is a non-breaking change which would have a long (if not indefinite) transition time allowed. There is an immediately breaking component which - in the more rare cases where it applies - would need to be immediately addressed.
There's a calculus between API stability and technical debt that is well-considered, as evidenced by the transparency here.
I do rely on system settings a LOT (for the Cortex Prime system). And I think a lot of this will help as I have multiple lib files for helper functions, i.e. different version of my helper functions, since system settings and actor sheet JS changes are currently handled so differently
I am for this, but for some reason the name systemData bothers me. Don't know why... I think Atro has just trained me that Foundry properties should be terse. This coming from the guy who writes the longest variable names known to developer-kind
As an analogy, another change coming in V10 is some rationalization/organization of attributes of the Scene data model which pertain to grid configuration. Variables like gridType, gridSize, gridColor, etc... are being grouped as an inner data object with the following inner schema:
grid: new fields.SchemaField({
type: new fields.NumberField({required: true, choices: Object.values(CONST.GRID_TYPES),
initial: CONST.GRID_TYPES.SQUARE, validationError: "must be a value in CONST.GRID_TYPES"}),
size: new fields.NumberField({required: true, nullable: false, integer: true, min: CONST.GRID_MIN_SIZE,
initial: 100, validationError: `must be an integer number of pixels, ${CONST.GRID_MIN_SIZE} or greater`}),
color: new fields.ColorField({required: true, nullable: false, initial: "#000000"}),
alpha: new fields.AlphaField({initial: 0.2}),
distance: new fields.NumberField({required: true, nullable: false, positive: true,
initial: () => game.system.data.gridDistance || 1}),
units: new fields.StringField({initial: () => game.system.data.gridUnits ?? ""})
})
The anology here, as it pertains to system vs systemData is - should we have named this attribute gridData as opposed to just grid. Should the reference path be scene.gridData.color or scene.grid.color. I would argue that the second reads more elegantly.
If that is the case, than why should actor.system.attributes.strength be viewed as less semantic than actor.systemData.attributes.strength. Perhaps it's not as clear cut as in the grid case.
I think there is a general argument to be made, however, that appending "Data" as a suffix should be superfluous unless the field name is too ambiguous without it.
if you can name a thing that isn't data, then you can append Data to the name without it being superfluous.
A fair counter-argument. When you are no-longer within the scope of a dedicated data object where everything in here is data, it becomes more useful to differentiate
For me personally, I think the main point is that I have not been thinking about a variable called system as the system data so far. For example, game.system is a data structure that has all of the data relevant for the system. I guess partly because of that, I think of a variable called system as a more generic thing than just the data defined by the system for this particular type of document
That doesn't mean I can't get used to the new notion
maybe we are also just too attached to data by now, so we don't want to let it go.... 😄
If it were data.system this would be clear. But if it's just myThings.system it's a bit unclear what system is
ah, yes, systemData not as in "data of the system" but as in "data provided by the system for the object", makes sense
I guess this is a fair criticism of the whole change. Because a lot of properties will be dragged out of the context of data
The argument I made when discussing this with someone else earlier was that for example for an actor you have
token
system
parent
Two of those are documents one isn't. Now which is which might not be clear at first glance but then again with how much stuff a token document has anyhow does it matter? Especially because a token presently has a permission field ( I actually don't know for what) that unlike actor.data.permission is a simple numerical value opposed to the usual object
I feel like this boils down to not wanting to let data go
yeah, thing.system makes it sound like it's a pointer to the system that the object is a part of.
this makes very little sense in foundry context, but still
I think if system already describes an object with System data in it... we shouldn't be duplicating names for different uses. There are enough words in the English language that something could be found that suits, without causing any confusion. Like, we've already trained ourselves to recognize system to mean one thing... we shouldn't be adding more things that it "could" mean depending on context.
well said
I also love how a post about breaking changes has boiled down to a discussion on what exact word to use. 🙂
preparedData since it's what you get after you run the prepareData method.
One personal (TypeScript related) counterargument to this: In the foundry-vtt-types, we currently support specifying the data model for actors / items / etc. on a type level. In particular, you can specify the data for each type. We do this in a way that allows type inference based on checking the type of DocumentData. This is extremely useful for TypeScript users. With these changes, we will need to completely redesign how we do this. In the best case, it will be a major rewrite and at worst it's not possible at all.
If you want, I can go into more details but it will get fairly specific
True. The only way around that, which I currently see is specific
class inheritance for each type, combined with your Proxy solution or my/PF2e's constructor hack.
Come to think of it, if we already do such a major overhaul: @mortal tinsel would it be feasible for you guys to allow systems to configure different constructors depending on the actor or item type?
(might be a bit off-topic)
That one d of difference is begging for muscle memory typos
I vote for the nostalgic kids arts and crafts show option of dataIPreparedEarlier
theDataPreparedByPrepareDataMethodOfTheSystem
Daniel is on to something.
Seems like the rest of that proposal is pretty agreeable to those of us fixated on the new name. 😛
We really do need to get to the more important topic of property name conflicts. Since that will actually break things.
(meanwhile the TS devs start crying in their own channel...)
Unless the lack of conversation on the topic indicates that it's not a very common problem.
I think they cry every update regardless of the changes.
I myself have only one property that collides in my system.
Not quite as much. We just realized that the data binding might completely become impossible for system data.
I don't really see what kind of problems you expect here. Maybe I am not completely understanding what the problem is about
@mortal tinsel I tested the script you posted and it gave a lot of false positives for colliding properties. Mainly stuff that is core foundry.
how could the properties collide? By having properties defined in some Document overrides that are also part of the data?
It's in the thing: https://gitlab.com/foundrynet/foundryvtt/-/issues/6841#immediately-breaking-properties-at-the-document-level-which-collide-with-model-fields
e.g. if Actor.data.data has hitpoints
and in preparation, an attribute on the actor instance is set with hitpoints as the name, this change will cause a collision and probably unexpected results.
@bleak patio all documents have a name. My system data also has a name property at the top level.
but that would still be nested in system or whatever we call that property?
good point
Summary: If the system defines a data property that is named the same as a core data property, the two names conflict. Example: img is a common core data property, if a system has its own img it will now conflict.
Oh yeah, so this would only really apply to things that are... ^ yep that
It actually wouldn't since it sits in systemData as ghost pointed out. Unless you also want to make it available directly on the document.
Sorry, not a data property. A Class property.
basically, it's not related to the stuff inside data, but to properties that we explicitly defined on our classes extending Document
Any package which extends a Document class with their own module or system-specific extension may have defined certain properties on the class which also exist as keys in the data schema. For example, a class like MySystemItem extends Item may have implemented a property like:
OK yeah. So I wouldn't have any conflicts at all.
Yeah, I jumbled it up in my head 😆
in some edge-cases a collision is probably still possible, but it seems like you would need to do something relatively weird
I mean either it's hard to understand how this could happen or the lack of people talking about already tells you a bunch on how often this occurs.
Anyway, he provided a script to check your system for conflicts, so presumably everyone can just run the script and get hard data
I think typical cases would be where the system defines an accessor to something that is inside data, for convenience. Foundry core is doing that with several properties, so it's not unlikely some systems do it, too. That said, these are trivial fixes.
it's certainly a good idea to remove the data.data thing but I hope this doesn't happen in v12 again. this will create a lot of bugs and require a giant amount of modules to be updated. it's everywhere
Though the script needs some improving:
well, that is actually correct. It's just that it also shows all of the stuff from foundry core
these are conflicts in core
Or not, depending on whether you want to see core problems.
its just got a linebreak it shouldn't have
Not sure if it's feasible to filter out core stuff...
I was more on about it potentially filtering out core stuff. But then again, that would also filter out possible monkey patches.
if we do this, but the time V10 Prototype 1 is released these core conflicts will be resolved
and the remaining list would be non-core conflicts
Does the script output the total count at the end? If so, we just need a baseline of the number of Core instances so we can see if there is a difference.
Just for reference: 0 conflicts in my system (ds4). But I have only created very few properties on the document classes, and those I have created have pretty specific names. So I wasn't expecting any conflicts.
I only have the ones in my extended Actor and Item classes and those come from core.
This has been a request I'm interested in for _dev-mode, making deprecation warnings failures somehow. How would you feel about adding in a core CONFIG.debug option that makes these warnings instead fail to help devs who want to update do so more ... jarringly?
By that you mean throw instead of warn?
So, here is my take on the whole thing:
If we can ensure (with compatibility shims etc.) this doesn't cause too many packages to break in the short to mid term, I am in favour. But we really need to be sure about this. Even if the changes that are necessary aren't all that complicated, it's still a huge amount of work for people who maintain lots of modules, systems, and potentially content. We need to be sure they get enough time to migrate their stuff.
Regarding the concern about separation of concerns of functionality and data: That is a valid concern, but foundry is already mixing this anyways, for example with all the convenience accessors that just forward to data. Even the name Document already sounds like data. So if separation of data and functionality was the goal, I think the battle is lost anyways. So I think it's better to embrace the fact that in foundry, data and functionality are tightly coupled.
One more thing I want to note: In my opinion, this should be a "do it or don't" decision, not a "do it or do it potentially later" decision. While this is a huge change and may result in a lot work for module and system authors, waiting longer just makes it worse. Foundry is a rapidly growing platform, and with time, more and more systems and modules will be created, so the amount of work needed to address this change will just increase in the future. tl;dr: If you want to do it, do it now.
@mortal tinsel My main question is, was there a reason that this kind of model wasn't used in the first place?
Greets. While the proposed changes doesn't affect my module dev as I use flags for custom data storage I am curious about implementation details insofar as modifications to ClientDocumentMixin are concerned; I assume the proposed changes likely touch this area of code, but perhaps not. I'd like to discuss ideas on how to decouple the App v1 infrastructure from ClientDocumentMixin while maintaining the functionality the current implementation brings and how this can help App v2 and the API in general. I can put together an RFC in that regard.
On the client side as the document model is transformed to be more streamlined from a data access perspective IMHO the extra grafted on functionality / methods to support App v1 gets in the way of this goal.
Whether it makes sense to also evaluate these kinds of changes for v10 when there already will be a fair amount of potential other changes is certainly open to debate.
I'm all for trying to condense as many changes as possible into one release. The ideas I have maintain functionality, cleanup the client side document model, and provide additional flexibility for JS usage / subscriptions to documents / document collections clearing a pathway for App v2 while maintaining the App v1 implementation w/ a few changes.
You mention that getters will be shimmed to be backwards compatible, but what about calls to update() that then refer to data.x?
Thanks for sharing your summary.
I expect that if the idea to move the system data to something like system. follows through, it would be shimmed on the backend too.
Yeah - it's a bit of a long story dating back to the initial design that used the Entity concept- perhaps one that I can tell on a dev stream at some point.
That would certainly be interesting.
When you mention "App v1" and "App v2" to me that sounds like the terms we use for the Application class which is unrelated to this change and perhaps/probably not what you're referencing?
As for the ClientDocumentMixin is still has an important role to play so that we can reuse the same BaseDocument on both client and server side but assign it scope-specific functionality which is different in each case. I don't anticipate there being significant changes here.
This would be important for us to ensure that changes passed to update() are redirected towards their new destination data paths.
It’s definitely about Application, but I’m also not sure in what way it is related to the suggested change.
But I guess I’ll better let Michael speak for himself 😅
Tangentially related; also why I tried to just make the post in #dev-support as I know this is a more focused discussion thread. I was told to post here FWIW despite my statement that this is tangentially related to the general cleanup of the document model / purpose of the change being discussed here. As the document model transforms to be more concerned about exposing the data directly then IMHO it may make sense to reduce the other add on methods from ClientDocumentMixin particularly App v1 related like createDialog, deleteDialog, importFromJSONDialog, and the apps tracking object can potentially be handy. I have some ideas in this regard that maintain existing support while actually increasing or generalizing document subscriptions w/ a pure JS API making such functionality useful beyond the GUI layer. I see the current design of ClientDocumentMixin / App v1 coupling as more of a convenience of initial implementation, but a hard coupling to App v1. So again probably wrong forum to discuss this in particular, but work that likely needs to get done to support App v2 as well that is tangentially related to streamlining the document model.
metadata instead of system? There's a case for it, since it's already being used in Compendium collections.
as one of the pf2e devs, I think the state following the merge will be better than the one before it, but it's going to take a lot of refactoring. I think we won't have as much problem as the javascript developers who will most likely just be chasing deprecation warnings, but I mostly worry about the "non-code" things where we've stuck paths to things in data objects such as amount of damage weapons do and things like that.
it's not metadata. it's the data that's relevant to the system (i.e dnd5e) like attributes etc
But then... is the metadata in Compendiums really metadata.
The equivalent of active effects are a type of rule element--we'd be able to do the same kind of migration on them
(just string-replace leading "data." parts of path properties)
I guess it might be a little tricky to use AEs (or AE-likes in our case) to set flags if they're going to be moved to the document instance level
or would AE paths also be recontextualized as operating at the instance level?
I just want to make sure that the reasons for going with that design (if they are still valid) are also considered. It would be unfortunate if this change ended up restricting Foundry development in some future way (or worse, had to be reverted).
I welcome this change - thank you for the breakdown. I'm sure that it'll be painful, but perhaps it would be worth having the deprecation warnings for this area specifically be driven by a developer setting somewhere, perhaps even on a per-system and per-module level? This would reduce the amount of noise that one would face when perhaps reworking intra-module/system behavior?
Of course, I fully expect this to be enabled globally after a few releases, but it would at least function as a stopgap in the interim.
After talking about it with @fathom eagle some, we found that it might be very beneficial for typing efforts if the type property of an item or actor would be in the systemData property (either instead of or in addition to the document). Thoughts? I'm not sure how valid that is outside of typescript.
To explain briefly why it'd help, it's to make systemData a "discriminated union" https://github.com/basarat/typescript-book/blob/master/docs/types/discriminated-unions.md
To explain a bit more thoroughly, currently a very ergonomic part of Typescript is allowing narrowing from one type of actor or item easily. This means something like if (actor.data.type === "specificType") { actor.data.data.uniqueProperty } will infer exactly like expected. (where uniqueProperty may only exist on "specificType" or have differing types but within the if statement it knows its exact type)
To explain why this change breaks this behaviour, the machinery of Typescript doesn't currently allow a "class union" (at least while being extend-able). This means if type is at the top level we can't do the same thing simply rewritten as if (actor.type === "specificType") { actor.system.uniqueProperty }.
To expand a bit on this: We currently make this work by making ItemData, ActorData, etc. discriminated unions. As Luke explained above, that actually prevents anybody using the type definitions from extending these classes. For the DocumentData classes that has been an acceptable trade off because extending these hasn't been common until now. But for Document classes, that's obviously not an option. With the changes coming in V10 regarding DocumentData -> DataModel, I suspect that extending DataModel might become much more common anyways, so we will probably run into this problem regardless of weather Document becomes a DataModel or not.
Having type available inside system would indeed help a lot in that regard. But of course it's also a bit weird to inject this one property into the data that is otherwise controlled by the system. Might result into conflicts. And outside of TypeScript, there probably isn't any benefit to this.
I think in terms of conflict, type would be a bad property to add yourself. It's true though that I think the only benefit outside of Typescript would be being able to identify the type is given only the system.
Semantically I do think that it makes some sense inside of system because a system defines a set of types that they can be but I do admit the main reason I want it there is for Typescript.
I thought metadata had been replaced by collection; at least I just had to make that change in 20+ places in my v9-compatible code 🤪
On the topic of the RFC, I have particularly repeatedly run into Problems 4 & 5, and in particular the nesting of data . I'm no Javascript expert (correction, I'm no expert, but I know Java and Swift better), and code like this is no doubt necessary in current Foundry, but makes my head spin:
if ( obj instanceof Document ) data = obj.data.toObject();
else if ( obj instanceof DocumentData ) data = obj.toObject();
(mentioned in Problem 4)
So if the proposed change simplifies stuff like this, I'd be happy.
for that particular problem, it should actually be possible to just call obj.toObject() because Document conveniently has a toObject method that simply forwards to .data.toObject()
but the need for this forwarding highlights the problem
One other comment about the ability to set console warning levels; I think I brought this up around the v0.8 data model changes, but a V10_COMPATIBILITY_ERROR setting would be helpful to basically not allow ignoring (or in my case over-looking) deprecation warnings.
In other words, with this (debug) setting, your code would always break if you have deprecation warnings buried among 100+ other warnings. I'll see if I can find the suggestion I made previously.
Eh, type can make sense in an item/actor's definition in the context of a system. I can easily think of situations where you would want to store distinct "types" of a given item/actor type where they're mechanically identical other than that certain aspects of the game care about them.
I don't think it's an inherently bad property name to add to a system's data in the context of certain game systems and mechanics.
I think that would be better under a name like subType or something.
Or even better some more descriptive term for the system, type is used a fair amount over Foundry so I'd probably think it's document related if I saw it in code.
Oh, look I found my original suggestion which i will just link here: #dev-support message
Fundamentally: a mode where a known coming deprecation was promoted to an error rather than just another warning. So a "we're going to remove this in the next version" is an error
@unique parcel @fathom eagle @bleak patio does the option of having the systemData be an instance of a custom DataModel class potentially help solve this issue with regards to typing?
how different is the typing in that project than the typing in the pf2e system? We don't seem to have many troubles
i.e. if item.systemData is an instance of WeaponModel when item.type = "weapon", etc..
It would allow doing instanceof based checks instead of checking type, so yeah, kind of
That'd work pretty well, I believe. The condition would have to change to item.systemData instanceof WeaponModel--
Ah Ghost beat me to it haha
but yeah that'd be a way around the issue!
The current prototype design is that you can configure, for example:
CONFIG.Item.systemDataModels: {
weapon: WeaponModel
}
and your type-specific system data model will be automatically used (client-side only)
sounds like a solid idea. I guess we could probably live without using a discriminated union if we have this
I presume big systems like DND5e will move to that as well which would be great especially since it lets you add extra methods to specific items/actors
It will still be a bit weird to have to use instanceof for people who are used to just checking type, but I guess we have to make some trade off
An alternative option is that you can, of course, place type inside your systemData as a document initialization step which would allow you to then typedef the systemData object in the way that you have proposed, for example:
class MyActor extends Actor {
_initialize() {
super._initialize();
this.systemData.type = this.type;
}
}
Would there be any impact to the static Document#create method? I have this in one of my modules and seen it other places before:
const itemData = {
data: {
// system data goes here
},
img: "some/image.webp",
name: "The Item name",
type: "equipment",
};
return await Item.create(itemData);
Would the itemData.data have to be be renamed to itemData.system?
the problem is that we need to provide the types completely independent of such solutions.
it would work if we had everything under our control. but we can just provide types, no runtime implementation
Yes, the major benefit here is for better code architecture and maintainability, so you can have all your methods and attributes that are specific to use of weapons contained within your WeaponModel class.
Yeah I always thought data.data was a bit awkward and non-specific so I'm definitely glad it's happening
from my understanding, that will continue to work throughout the deprecation period but eventually, you should replace data with system (or systemData, whatever name is chosen) in that call
This would be handled by the migration layer, so that when you pass in data it will be redirected to become systemData without you needing to make a code change.
I figured as much. Just wanted to confirm since it wasn't called out as an impact in the proposal
on that note, as @unique parcel asked earlier: Not necessarily now, but possibly in the future, would you also consider allowing to configure individual classes for the Actor, Item, etc. classes per type? Or do you think that becomes obsolete because with your suggested way of allowing to specify DataModel classes per type, the type specific functionality could simply be moved there?
different classes per item/actor type has been really good to us for pf2e with regards to scaling, so that being encouraged in the core architecture only sounds like a good thing
there are quite a few systems that implement this by using either a Proxy, or implementing this by returning individual subclasses from the constructor of a base class
I think that becomes unnecessary with our V10 improvements to supporting inner data models. The Actor class would handle behaviors common to all actor types, and the type-specific model would handle behaviors specific to a certain type
I could see arguments being made for that still being useful though, but I think the need for it is significantly lessened
I've been doing something like that in my module and while my scale is much smaller than a whole system, I definitely like having a different class per type
Well, it's still possible to do it with the methods I mentioned above, anyways. So having it supported explicitly in core would mostly be convenience
Returning something other than an instance of the prototype from its constructor is pretty gross
People still do it. And as it's subclass instances of that class, it's not really an issue. Personally I favor the Proxy based solution but both seem to work fine.
I think the point is that people shouldn't have to do it anymore with the inner data being able to return different classes
Its kinda gross in that its hacky, but its one of those hacks where the gains far outweighed the losses, and there really wasn't a way to get that without the silly hackiness
so its nice to see it being considered for core without the hackiness
I'd personally be inclined to agree (with Atropos that it's pretty gross) but I've never toyed around with subclasses much myself, building on top of DND5e most of the time. I might not know the advantages of it.
yeah it's really a step in the right direction to allow different classes under systemData
its because foundry news up the system defined type, which means that only one actor/item instance was supported. To support more, we had to fetch a subclass constructor and call that constructor
Oh absolutely, I mostly mean advantages to how it's done today beyond what allowing the same in systemData (less hackily) allows
if there are advantages to today over what's being proposed that might suggest a different design, but I'm currently unaware of any extra advantages currently
I've run the compatibility test, and it seems to be saying that every single field name in my fcoActor class, which extends Actor, is a breaking change. This would be an issue; surely you wouldn't expect me to use different fields than 'name', 'type' etc. in my actor class?
I think it was noted that the test does not work right now.
Oh, I didn't see that.
these are just the incompatibilities reported for the foundry core accessors
that will go away in V10 because they obviously remove those accessors
if you want to check this now, you should search for properties defined by yourself in that list
or wait until the first prototype
I don't believe I define any, to be honest.
function names could also clash in theory
though pretty unlikely
The thought of yet another set of changes to the core document model is honestly just depressing and tiring. It will mean eventually refactoring every call to *.data.data or *.data and I don't trust find and replace to do it because I probably use data all over the place as a parameter in functions etc.
So I'll have to do them all manually, most likely.
1,170 results for data.* and 253 for data.data.*
The good thing is, you would be able to take your time with this, due to the long deprecation period
Assuming the non-breaking changes don't actually break things. Which they did, last time in the change to 0.8
Would this need to be changed in the proposed model?
for ( let s of game.data.settings ) {```
I think game is a document of some kind, isn't it?
no
from what I understood so far, the changes would have no impact at all on that code. game is an instance of Game, which is simply a class, unrelated to Documents
Right, but it illustrates my point that the x.data pattern is all over the place for things that aren't documents, making an automated refactoring pretty hard.
Regex, lots of regex.
How would regex know the difference between something like game.data (unless I specifically allow for 'game' in the search) vs doc.data.flags?
yeah, that's true. Just a simple search and replace probably doesn't cut it
I think I'd also need to refactor any update call like:
await this.document.update({"data.description.value":DOMPurify.sanitize(event.target.innerHTML)})```
as has been mentioned before, that would also have a deprecation path
As I believe that would become an update to description.value
No.
Yes, I know, but that just kicks the can down the road for the refactor.
It will not move, unless the idea of changing the name of the system data happens.
Unless the deprecation is left in indefinitely.
sure, at some point it will, but Atro said the deprecation period would be really long, possible indefinite for now
If you happen to be using something like Typescript or something (which tbf you likely aren't) you can mark data on actors and items as deprecated and search for instances of deprecation, that's going to be my migration path.
The .data.data change to .data will not alter the data model.
I am not using Typescript, no.
I was referring to this, which says that calls to document.data.foo will be changed to document.foo:
- document.data.data.*
+ document.system.*
- document.data.*
+ document.*```
If the proposed idea of changing the name of the prop to hold the system data to something like .system or .systemData, then that part of the data model would change and would need to be reflected in the update calls.
The old top level was the root of the document.
It would be weird if the update stayed data.foo when foo is now just a member of document, wouldn't it?
Not your system data.
I think it's just supposed to illustrate the necessary changes. It's not a literal "do this search and replace"
you only include data in the update for properties inside the data of the system. that will stay in data (or rather that will be renamed to system or systemData). So you don't need to change the update call conceptually
the paths in update have always been relative to the DocumentData, not the DOcument
Right, because it's actually a call to update data.data I guess, you just omit the first data. So if the name does change then those will need to be refactored.
Nope.
Full stop.
The call directed to the data model, but nothing was transformed.
well, if the name of the inner data is changed to system, that will need to change when / if the deprecation period runs out
Please export one of your own actors and look at the JSON.
That JSON is what was getting updated.
I will likely endeavour to update my system to avoid deprecation warnings as early as possible, because I don't believe in putting stuff off just because it can be, but I don't think that 'you don't have to do this until later' is an argument against how much work is involved, just when the work has to be done.
I think there is a misunderstanding here. The export indeed contains a property called data containing all the system specific data. Other properties are no nested inside that. To update properties inside data, you need to include data in the path to the property.
As the suggestion is to rename this data property to system, the call to update needs to be changed eventually.
Yea, I'm talking about that.
Skimble was walking like if data was dropped that it would reflect on the inner model for updates.
doc.update({'data.foo': 2}) becoming doc.update({'foo': 2}), which is not true.
I think that was the assumption in the beginning, and you are right, that's incorrect. But I think everything is clear now (no structural change, just data potentially being renamed to system)
No, I was saying when you make an update call you're updating data by default, so you omit the first data' in 'data.data'
So yeah, any update call I have which is currently data.x would become system.x if the nomenclature was changed to data.system.x
I think we are all on the same page now, so let's move on 🙂
Ah, wrong reply.
data being renamed to system or systemData was simply a suggestion, prompted by the thought that it would be best to do it now.
It's not the crux of it, but would probably be one of the easiest shims to maintain for a long time.
Biggest rub would be backend for updates.
It is part of the proposal, though, so discussing it is fair.
but you are right, it's not the core of it
The proposal is saying it would HAVE to be changed to maintain backwards compatibility
A significant consequence of this proposal is that we would add backwards compatibility for Document#data so that pre-existing code functions without required changes. Adding this backwards compatibility, however, will require us to change the name used for SystemDataFields inside Actor, Item, Cards, and Card data models. We intend to rename this field from data to system which would - in combination with the other facets of this proposal - change the example usage above to:```
I guess it's true that this is necessary because otherwise the inner data (which is now at the top level) would clash with the outer data (which is kept for backwards compatibility)
Personally I'm not sure how it will help to have people sometimes getting data.system.data instead of data.data.data, but I probably don't understand enough of the core underlying architecture to see the benefits to developers
that won't be necessary
I also think system is a bad name in that it implies data relating to the system rather than the system data of a specific actor.
at worst it will be data.system, if somebody has a document called data
The renaming .data to system is not part of the core proposal and was a suggestion.
that's incorrect
it is part of the proposal
I thought if you pass a document as 'data' and you want what is currently in data.data, given that the second data is renamed to system, couldn't you still end up with data.system.data?
the outer data is completely removed
If you read it, it literally does not mention renaming the inner data that is used for system data.
the inner data (or system) is available directly on the document
Oh yeah, gotcha.
it does:
A significant consequence of this proposal is that we would add backwards compatibility for Document#data so that pre-existing code functions without required changes. Adding this backwards compatibility, however, will require us to change the name used for SystemDataFields inside Actor, Item, Cards, and Card data models. We intend to rename this field from data to system which would - in combination with the other facets of this proposal - change the example usage above to:
- actor.data.data.attributes.strength = 10; + actor.system.attributes.strength = 10;We feel this is a great change because it simplifies the data structure while removing ambiguity about what "data" we are talking about. Because of the backwards-compatible shim that would be added, pre-existing references to actor.data.data would redirect properly to actor.system.
...I totally missed that that when this first dropped. Hell, I think a number of people did.
Or maybe just me.
yeah, potentially. It's not very obvious, among the other big changes. Maybe the discussion about the name (system vs systemData) also made it seem like this was a community suggestion
but good we are on the same page now 🙂
Probably.
I know I skimmed it while at work, so I know my reading comprehension on it was low.
Don't worry, it happens 😉
Code blocks are not the best for reading not-code.
That and I still had it in my head nothing in the data storage model was moving.
Well, let's not harp on this. The misunderstanding has been cleared up. Let's move on and keep this thread productive 🙂
Is there an easy way to see if a getter is actually used anywhere? For my extra class I've defined some getters as shorthand ways of accessing parts of item.data.data, like item.data.data.active. As these are defined in the schema I believe I'd have to refactor to use different names. However, searching my codebase it's hard to see whether a call to x.active is using the getter or the inherent property. Or does this change only affect things currently at the item.data level rather than the data.data level, due to the change to accessing core fields at the document level?
I'm referring to the breaking change mentioned under the heading [Immediately Breaking] Properties at the document level which collide with model fields
Override the getter, add logging or a throw and see?
Stuff that is currently in the .data.data level will not affect the immediately breaking stuff because that will still be nested inside system
I was thinking that might be the case, ghost.
that section is about properties /functions you might have defined on the document (Actor class etc.), that might be conflicting with properties currently in the outer .data
Yeah, that makes sense given that it references img. I only do this with stuff in the system data model.
unless by chance the outer data has a property with the same name, you shouldn't have any problems then
Ah, I lie, I have a thing that overrides the visible getter.
if you still want to see whether a getter is being used, adding some logging to it like Spice_King suggested is a decent way, I think
Hmm, that's a tricky one, because I'm overriding the core visible() getter to hide actors that represent objects.
I can't change the name of the field there, because then Foundry won't recognise it.
I don't think there will be a problem with that particular getter
No?
at least not on actor
because it doesn't correspond to a property on the DocumentData
or rather ActorData
Ah yes, I see, wheras img does
there is no actor.data.visible, is there?
No, there isn't, I looked for that.
then there shouldn't be any issues with that getter. I think it's being added by ClientDocumentMixin. That won't change, as far as I understood.
And this doesn't apply to actorSheets, because they aren't part of the document model.
(I have a get actorType() method in my sheet class)
Alright, well, I'm probably getting far too into the weeds for my own system for this thread, but my overall impression remains that this is going to be a PITA to refactor because I'll basically have to do it line by line, about 600 lines of code, to ensure no find/replace cock-ups. Having said that, for me the biggest issue is that it means I'll probably have to leave users behind again that don't update to 10 quite fast, as I don't really want to gatekeep every single call to data behind a version check.
the script that is mentioned in the proposal should give you an overview of all properties that are problematic. Unfortunately, it also lists all of the core properties that currently still are problematic. But if you look through the properties it lists, and none of them is from you, you should be good.
If backwards compatibility is your concern, I would probably wait and not change the stuff that is deprecated immediately. IMO, there is no reason not to use the deprecation period.
I suppose it would be possible to write a core getter that returns the correct syntax depending on version and then uses that in string literals wherever the code calls for it.
which is exactly which the backwards compatibility shim will do. No need to write that yourself
Right, but if I update my system to not have deprecation warnings, it becomes incompatible with prior versions because 9 won't have a clue what a call to system.x means (for example).
For game systems or modules which extend a Document class, you may elect to suppress these console warnings by setting the LOG_V10_COMPATIBILITY_WARNINGS static attribute of the model to false. This compatibility shim could be modified or adopted by a package developer (without warnings) in order to make the redirection "permanent" without any other code changes required.
You can get rid of the deprecation warnings this way, too.
get data() {
if ( this.constructor.LOG_V10_COMPATIBILITY_WARNINGS ) {
Literally provides a way to shut them up until you make the jump over once you stop caring about v9 support.
I've encountered a troublesome corner of the proof of concept that I'm not sure how to handle from a compatibility/shim perspective.
When a API user makes changes to the system data of an actor, passing the old data. path for system data, i.e.:
actor.update({"data.attributes.strength": 10});
We can migrate that when handling the update to be treated as:
actor.update({"systemData.attributes.strength": 10});
But when the server is done with its work and it returns back to the client the true changes that were applied, that "true change" is to systemData.attributes.strength.
This becomes breaking for Actor subclasses which define Actor#_onUpdate or API users who hook onto updateActor and then inspect the set of changes to test something like:
if ( hasProperty(changes, "data.attributes.strength") ) {
...
}
I'm not sure what to make of this yet because its a consequence of the change which, unlike others, is not conveniently shimmed for compatibility.
Pretend that any change to system.X is also a change to data.x
You misunderstand, that's the part that is handled - what I'm mentioning above is the server response back to the client.
And I'd shim it on the receiving side of the client, as to not double send over the wire.
you could have the server return both properties in the changes? Not sure how easy that is to implement on the server side, but I would assume it's possible?
Or am I also misunderstanding?
yeah, its possible, just would require extra shim logic elsewhere in the code. Starts to get a bit less clean around the edges.
Yea, my thought is that in the client side code, you also expand out system.X changes to also have data.X.
I've not ripped into guts of the websocket stuff.
Could be an issue if it changes the order or number of parameters passed to the hook function, couldn't it?
Ah, here we are.
it does not
yeah, I guess it becomes a bit messy. In theory, even having both properties could be breaking, for example if people rely on it being the only property that is part of the change. Something like
if(Object.keys(foundry.utils.flattenObject(changes)).length === 1) { ... }
the best way to shim this is with Object.defineProperty which does some JS black magic to allow you to add the path to the changes object without it becoming a member of, for example Object.keys() or otherwise being enumerable.
but anyways, it's just something that needs to go on the checklist of things
I'm pretty sure there can still be edge cases where this is breaking. But yeah, they become pretty unlikely / obscure
this is uncomfortable though, because while data -> systemData is the most high profile change and therefore merits this treatment, it's certainly not the only data model change we are making, have made, or will make.
to date, we have simply allowed the returned data from the server-side operations to be the true and current data model.
I guess this would also have been an issue with the changes to light data in v9?
my perspective had been sanitize/migrate input - everything else should take care of itself
yeah, correct. Nobody made a fuss about it, so either it barely impacted anyone or the people whom it did impact were fine making an update
You could patch ClientDatabaseBackend#_postUpdateDocumentCallbacks as that calls the Hook for updates.
I guess people just don't rely that much on the layout of that data in _onUpdate / update hooks...
but I think hooking onto changes to an AmbientLight and reacting to specific changes is a different and much more niche type of thing than hooking into changes to actor system data.
almost right, but basically that's the type of approach that would need to be taken
_preUpdateDocumentArray does the preUpdate hook.
Well, it's good we noticed this problem now. It's better to have some time to think about how to deal with this in general, instead of having people scream at you why their hooks are broken. And it's really not limited to this particular change but a problem with changes to the data model in general.
There's no way we'll catch everything before Prototype 1 is released - which will result in some breakages we did not expect or anticipate.
I hope that we'll be able to collect some feedback from developers on what breaks if they run their system through Prototype 1 for a 15 minute test.
but our track record on that has not been very good.
It also applies to the preUpdate hooks btw. and preCreate, too, I think
I'll definitely be running Fate Core Official through the prototype so will happily scream incoherently provide feedback if anything breaks before I start making changes.
we know you'll scream incoherently, you don't have to pretend 😛 ❤️
the preUpdateDocument hook also has the change differential data, and people might be relying on certain properties in there
I won't lie and say that I don't think that this should have been included in the v8 changes. It's going to be a change for the better over all, but it probably would have been nice to have had the pain to refactor it all at once, but I doubt that it would have been thought of.
easy to say in hindsight 😄
Its very easy to look back and wish that our past selves had known to implement a better design. We did not have the knowledge, perspective, or clarity on how to make this change at that time in the past.
The fact Foundry is willing to make changes to improve stuff for the better is why I'm here rather than still using Roll20 as a GM, though I've yet to really get a game togeather. Curse of the system dev.
According to my notes from last time, precreate, create, predelete and delete do not pass a change object in the hook.
Yea, hindsight is 20/20 after all.
The only thing that has opened the door for this proposal now is new work on the DataModel and its underlying schema which has generalized away some things that were historically Document-specific behaviors.
- Note to self: New standardised hook signatures:
- preCreate[documentName](document:Document, data:object, options:object, userId:string) {}
- create[documentName](document:Document, options:object, userId: string) {}
- preUpdate[documentName](document:Document, change:object, options:object, userId: string) {}
- update[documentName](document:Document, change:object, options:object, userId: string) {}
- preDelete[documentName](document:Document, options:object, userId: string) {}
- delete[documentName](document:Document, options:object, userId: string) {}
we're talking specifically about the change object in preUpdate and update.
As well as the data object in preCreate and create
Ah, I thought ghost was saying it would affect the change object in precreate earlier.
the data object passed to preCreate and create hooks is whatever the user provided initially as the input data for document creation.
There is no change object for preCreate, just the whole data.
this input data might contain old data references, rather than new systemData ones
maybe a slight misunderstanding. My comment was mainly about the change object in the preUpdate hook, and I also noted that there might be a problem with preCreate, too, unrelated to change
Could probably use Proxy BS to mirror changes to data into system for the preCreate.
preCreate currently has the DocumentData as second parameter, so I guess in the new world, it would just receive the document as the first and the second parameter?
...Or not, just make what's passed in to the preCreate is not a plain old object, but one with a getter and a setter for data.
I currently use preCreate for this:
Hooks.on("preCreateActor", (actor, data, options, userId) => {
if (data.type == "Thing"){```
To prevent creation of the actor if it's of type Thing. I think that would work if the second parameter was also the document, but anyone who's expecting that to be a data model would have to strip a layer of .data from their code, I think.
Mind you I could also just refer to the type of the document, I guess.
Yea, the current nature of the document class has a number of accessors that forwarded to document.data, type is on of them.
Going forward, it would stop being an accessor and would be the real value.
So, in effect, nothing about .type changes.
it's not a plain object, it's the DocumentData
or... not?
sorry, that was incorrect
my mistake
I'm a cranky old 0.5.5 dev, we did not have Documents back then, so I probably still treat them as not existing still.
This is similar to what I was thinking of. Your prototype looks like it would instantiate classes under systemData. Honestly if I'm not overlooking something, I would also be totally fine with core instantiating sub classes of the documents themselves per type. That is what pF2e and my system are currently doing. Both cases you can then check with a simple instanceof, but having the document be a subclass would be even more preferable to me.
Not sure how you are coming to that conclusion. WeaponModel is a DataModel class not an instance. So it would be working by foundry instantiating these.
This is the configuration for which constructor function (class) to use. And from the naming it looks to me that this would drive the instances created in Item#systemData, not the instances of Item itself. The latter would be preferable to me.
Ah, yes, i misread your message, sorry. Yeah, this is only about the systemData for now, as Atro explained. See #953299044611031120 message for his take on different Document classes per type
I guess I just like to have that behavior on the top class instead of having to reach into the data model first.
Yeah, I tend to agree. But this is something that could be done later on, independent of the change that is being proposed here.
I think I've adequately addressed the issue of shims for pre/post hooks for Document create and update operations.
Good to hear!
Oh, I got a small request, the ability to yank all the shims for testing reasons. Nothing helps you fix is than setting everything on fire.
not a small request, unfortunately, given the way the shims are integrated into the architecture
Darm.
but there should be a good way to cause shims (when accessed) to error rather than warn
Depending on how many shims there are, "just delete them from your local foundry.js and watch the world burn" could possibly be an option for local testing
which is arguably better than removing them
Fair.
I only really care about supporting two major versions, but having a hard shutoff on the shims would be nice for pre V11.
https://gitlab.com/foundrynet/foundryvtt/-/issues/6854 a design I'll try and see how well it works.
Kinda also need it on the back end too, if there is shims put in there.
Also, might be a thought to namespace the compatibility flag for each shim, if Foundry starts to accumulate more and more.
the server-side of the software should work perfectly without any compatibility issue - otherwise it is our fault
EX: CONFIG.debug.compatibility.V10_DATA_MOVED_TO_SYSTEM_DATA = CONST.COMPATIBILITY_MODES.FAILURE;
start simple
we've... uh... still got a lot of work to do
to be fair it's pretty much entirely dnd5e doing things with .data. which can be suppressed
Follow-Up Question for the <@&596076164104323104>s
We are discussing this internally and haven't decided our opinion as a team yet, but given that this RFC thread is open I think I'll collect thoughts on this aspect of the refactor as well.
While we are making these reorganizations, do we want to try and address our naming debt that documents which specifically are embedded inside a Scene are currently named as AmbientLightDocument as opposed to simply AmbientLight as all other Document types are named? The reason for this was that in the pre-v8 days of Foundry we had AmbientLight extends PlaceableObject and we didn't want to rename that class for fear of breaking packages on the canvas.
It's hard to say in hindsight whether that decision (to avoid further V8 breakage) was a good one or not. The naming conventions regarding documents embedded inside a Scene are a clear area of technical debt that we have been living with.
In an ideal world (without fear of breaking modules), the document class would be named AmbientLight and its canvas representation would be AmbientLightObject (or something TBD). We need to carefully balance the amount of V10 breakage though, as we are already tipping the scales pretty heavily towards disruption. Thoughts?
At least it's easy to shim.
With a spot of proxy magic, you and even make warnings for touching it.
I don't know that this has sufficient benefit really.
Would this come with a similar indefinite depreciation to the document model changes? (I'm generally on team "if it's worth breaking it's worth breaking now", as a big disruptive patch and several not very disruptive patches is probably better than several moderately disruptive patches. Not entirely sold on it being "worth breaking" though)
It's a thing I occasionally trip up on, and sometimes see folks in #macro-polo fall over... But yeah, not sure it's worth doing unless there's some architectural reason to be poking around in that space anyway (like there is for the data.data to system change).
And tbh most of that is just people who were around pre-0.8 using "Token" when they mean "TokenDocument"
Or .entities, I see a chunk of that in there too. It does suck when old code breaks, but it's often for the better overall in the long run
It's not easy to shim, if it were I don't think I'd be asking this question we would have simply done it (probably in v8).
I am inclined to agree with others that it's probably not worth the added disruption given what we already have on the plate.
It's worth considering though, because it's definitely lingering tech debt where documents are concerned
Ah, I've not poked the guts of the lights, going to guess you saying it's not is due to there being AmbientLight being used already.
On the other hand, I've been working over on The League on a proposal regarding PlaceableObjects.
And if there is a decision to break any eggs, might as well break the whole basket.
AmbientLight extends PlaceableObject is a piece of code which predated documents so when we added Document class in V8 we could not (as we otherwise would have wanted to) define AmbientLight extends Document and we had to name it AmbientLightDocument instead.
you'd better be careful suggesting that. It's a big basket.
While I agree with the core idea, I have zero skin in that game. I kinda expect that a number of people may get bent out of shape over it being a part of V10.
It may be a thought to chose wholly new name for it, then it should be a bit easier to shim.
SceneLight, PlaceableLight, LightSource or something of the like. (I'm not looking in the source to see if they are in use or anything.)
Yeah, "ambient" doesn't seem like the ideal word in the first place. It's technically correct verbiage, but most people will initially associate "ambient" with indistinct/general things, rather than point-source lights
I'd make an argument for the name PointLight, makes room for any other shaped light source.
Personally I'd rather just update things and not worry about what it breaks. If it's worthwhile that the broken things need to be fixed, then they'll be updated. I realize this may be seen as a selfish/naïve approach, but I'd rather things just get overall better than worry about the few things that will get left behind
Counterpoint: If it deals with AmbientLighting, shouldn't it be called such?
Consistency helps learning
It's related to, but not a whole ambient lighting system.
There's a balanced middle-ground to be had. There are a ton of modules/systems out there which are maintained by people volunteering their work in their free time. Progress is important, but it's also worth being mindful of people who will be negatively impacted. There are hundreds of thousands or millions of lines of code running through Foundry that have to be maintained by people who have lives to live inbetween Foundry code updates.
I do know I would like light lines/boxes for sci-fi maps that a point light is not ideal for.
There has already been a lot of discussion about the proposed v10 changes, but I'd still like to voice my opinion:
I fully understand that people are frustrated about core changes like these, but Foundry is at a really early stage right now. 1-2 years after release is nothing for a product that will probably be around for 15-30 years. I welcome any change that helps to put FoundryVTT on a spot where its core API is as clean and precise as possible.
And lets be honest, the data vs data.data thing is something that all of us stumbled over. Its hard to understand, hard to document and lots of systems (especially the ones not using types) are passing around documents and data indiscriminately, so often people guess which they have to use (data or data.data).
In the long term and in hindsight, I'm confident that this will be seen as the right step. Yes backwards-compatibility is important, but the API should be as precise as possible. And I'd rather have things break sooner than later.
Naming aside, is this also to suggest reverting back from the placeable-document split?
Definitely no. I think a separation of concerns between the Document which owns the data schema and methods related to working with that data and the way that document is represented on the game canvas is the right organizational choice.
A PIXI.DisplayObject is more analogous to an Application (in the DOM) than it is to the underlying Document which that application renders UI for.
yeah, I agree the split is warranted
Oh, sorry, I see at the end. Swapping names has its own baggage, but I guess the harder question is what to name the placeables
There's a future confused #dev-support member wondering what went wrong when they called AmbientLight.toObject() expecting an AmbientLightObject 😁
have a style convention to always invoke placeables from a namespace, yielding Placeable.AmbientLight 🤷
I think, generally, with there being so many different concepts in the Foundry API more namespacing would definitely be a good thing.
what movement in that direction, if anything, we'll be able to do in V10 I'm not sure.
I don’t really see how it would be possible to provide backwards compatibility for both the Document classes and the Object classes. Would you intend to just make one of them a hard breakage?
To be honest, I also don’t see that big of a value in this change. Sure, those names are a technical dept, but compared to other things, it’s pretty small. It’s just names after all, and personally, I have gotten used to the …Document names already by now. Yeah, it might be a bit confusing to new developers why some document classes have Document in the name and some don’t, but I don’t think it’s a biggie.
Personally, I don’t really care either way. If that change is made, it’s just some small changes in my system and modules. Not a lot of time needed to fix that. But the benefit also is pretty small, as mentioned above.
But for the overall community, it seems like an unnecessary disruption with little benefit
that is my conclusion as well
The 23rd of March is the day after my birthday and I have the week off. My system will probably be working fully without deprecation warnings by that evening, heh.
It's Schrodinger's release date; it's both the 22nd and 23rd 'til it releases on one of them and we find out which one it is
GL issue says the Prototype 1 release will be available on the 23rd, and i guess it technically is if it is released 22nd 😄
No biggie, but i suspect 22nd is the correct date here
If the builds have names, this one has to be schrödingers build xD
Can't really grock things that don't make sense. Or rather, if there is simply an arbitrary difference between two like-objects it makes it harder to remember concrete patterns
If I had no prior knowledge of the API, I'm fairly certain that I would think that applying is persisted to the database and updating is in memory... It's certainly not obvious on first glance and I think that this will be a point of confusion.
In fact, I just read the RFC on GitLab (I was on a trip yesterday) and I intended to come here to ask why a new word "apply" would be used over "update" which is more clear.
I also think that "data" is a more meaningful term than "system"...
Otherwise, I'm on board with this proposal.
But I resonate with the comments about how it becomes increasingly harder the more packages you have to update. I have close to 20 myself and I am not looking forward to the refactoring. Even pasting your block of code into each package and publishing a release will take me around an hour (not to mention how much time that would be without the auto-publish scripts you have asked us not to use).
I'm not sure if there is a point to my complaint, other than perhaps a request for a package release API on the website.
What about info instead of system? It's shorter and it describes what it is more effectively.
(I would think that system would refer to the current game system in use or something along those lines, not storage)
(great minds think alike 😉)
honestly I'm happy with systemData. system is more meaningful to me in that data is a very generic term and just system feels too terse. I'd expect item.system to maybe refer to the system that created it not the system's data. sysData works if everyone knows the abbreviations but they don't. I can spare three keypresses; sy[tab]. The direction is goes is very bikesheddy though.
I think the main thing that is confusing here is that apply... well applies the change but update doesn't do the type of update that some people expect, that is persisting it.
@solid sun I think you make some fair arguments, but the one that I struggle most is the emphasis on a package publication API (relative to its time savings)
not to mention how much time that would be without the auto-publish scripts you have asked us not to use
Let's presume for a moment that your build pipeline does everything that is needed to release a new Foundry module version except update the foundryvtt.com/admin page.
You would then need to go update the admin page for 20 packages which would take you, well, let's say 10 minutes because you're going slowly and you double check your manifest URLs. Everyone's time is valuable, I get it, but 10 minutes is presumably only a small portion of the time you would be spending to update that number of modules in the first place.
I'm in favor of making things faster, but I'm also in favor of actually doing the calculus to identify where marginal improvements in productivity or functionality are most valuable - and this just ain't it.
Fair enough. I'll reply in #dev-support since it's off-topic here.
What about simply calling it Light?
I think a bad pattern is to choose a sub-optimal name when renaming things in order to keep backwards compatibility. It's probably better to just give it the best name and disregard whether that name was previously used for something else.
I mean it's somewhat a bad pattern but sometimes the first name was the best name because old code will still break but worse it won't break noisily. Some of my least favourite things to fix are when it's possible it's an old way of doing things, an interface that another module exposes, or literally just a mistake.
Changing names makes the name worse but at least if there's behaviour change people can tell by looking at current docs what it probably should be
and it'll break in a fairly obvious way
You aren't wrong about apply... It does sound like it should be actually updating the DB. Just like typical settings dialogs in Windows, "Apply" saves the settings.
Plus, in this context it's really not clear what it does in the first place.
Maybe in-memory modifications could be done with modify?
This is a problem as old as time, I haven't yet heard a good word for "yes that definitely makes changes but not permanently on the backend" as opposed to "yes that definitely makes changes permanently on the backend" in my career. I expect a word will have to get picked and then documentation will have to suffice. update change modify apply save commit push sync upsert, all have pros and cons.
I believe in the staff to make a good pick and then its
a matter of documenting and educating
I suspect there'll also be a strong tendency to keep the current nomenclature as much as possible. Even if it's not perfect, it's far better to not touch stuff than it is to change it to be slightly more appropriate
Using update for what it is today is good, but the "this makes a change and doesn't save it to the DB" verb... that's tricky.
pretend
I like this because it actually does describe what is happening really well and is not as ambiguous as other options
I know it's not the most technical wording etc, but it's great
@stiff cliff Why don't you like it?
It implies more of a degree of "this isn't being saved" than is actually intended.
There are situations perhaps when this is exactly correct.
But not all.
I also know the pattern where you can supply a parameter telling the function to just "pretend" / not actually commit. e.g. prepare locally but do not send to master or only when I tell you to
Also, it doesn't imply change of a value.
prepare might be a better choice.
But isn't logical in a non-DB context
kind of difficult really to find something that makes sense.
Yes
localUpdate
Someone said update({}, { commit: false }) iirc.
Which is much more verbose but much more accurate.
I prefer the idea that the default assumption is always to mutate the database, and only when giving an explicit and clear "don't do that" would it do otherwise.
Plus, on the previous side-discussion of update vs. apply for data models: It would just be update, and commit: true would simply do nothing extra.
Generally coming from other languages/frameworks I never understood the need for update. Everywhere else I just modify object itself and the just run save()/persist() to save into database (or even use orm methods data.save(object)
I get that there may be some calculations that need to be rerun but in that case setters just should run them
It does seem a little odd that update() doesn't just "save the current state"
Other languages generally don't have a remote server that's managing stuff. They're usually running stuff on the server directly
Sometimes I would really like to just be able to have object and direct database access
Maybe even just use setters so that an update operation happens every time you modify the object
Modern web languages like angular form a lot of stuff in front then persist data using rest api and server may do something with that soooo
The database is basically always a seperate process though, so the communication is always via message passing.
that feels a bit excessive 😄
That's basically how Foundry works. There's a frontend (foundry.js) sending stuff to the backend (the Node.JS server process itself) as-needed (via WebSockets instead of discrete REST operations)
They can not be async
Foundry does not give me direct database access and requires to use some weird update function with weird object state
I would really want to have stupid simple objects and full control over them, but this is probably another discussion
Foundry doesn't let modules/systems do anything server-side. Foundry only makes the frontend open for people to do stuff with
Yeah. That's indeed the main issue. Maybe setters could be used synchronously instead of apply and then update to asynchronously save the current state to the server?
Yes, that's not the "weird" part. The unusual thing is that most applications would allow you to update the local version of an object, and then call save() when ready. But in Foundry, you group all changes together as an update object, and send it to update or you copy the local object, mutate the copy, and send that via update.
It works just fine, but it is unusual.
There's degrees of data saving? It either is saved or it isn't, I thought 😛 🙂
now also schrödingers save states? 😉
Nah, computers are more nuanced than that. I can think of a couple varying states of data persistence
Race conditions would be unavoidable in that case unfortunately.
Also, you might not want to save everything.
Transient is your friend then
Actually, yes! Also see: Two Generals Problem.
It’s commonly used
true...
I suspect this discussion is drifting a ways outside of the intent of this thread at this point. Foundry's fundamental server an data persistence architecture isn't up for discussion, just a little bit about the nomenclature
Will note that none of this is really on topic.
I thought race conditions are only a problem with regards to saving to the database. Shouldn't in-memory changes be nearly instantaneous?
For me the name persist/save would really mean that we are saving to db
Update is more like “update current object, but don’t save”
No. Race conditions happen in memory too when you're not single-threaded. Trust me. It's a nightmare sometimes
So you run update then persist and you are sure server is in correct state
FVTT is single-threaded (at least it was before the upcoming webworker stuff)
Yes, but if it persists to the DM in a way you can't await then you can fire off a bunch of changes without a way to wait for them to be completed.
I strongly suspect that Document.update() isn't gonna change functionality. It would be too drastic a change for little practical benefit. Too much code leans on that existing functionality
I'll say this, stop bringing up this topic here since it's really far off topic.
That or y'all can eat mutes.
I don't think anyone is suggesting that it do so. The discussion about the archetecture mereley informs the question of what to call a given action, and what all the actions really mean.
Probably. Apply is ok in that case for in memory change.
Except that as @solid sun pointed out, apply sounds like "save this" which it specifically is not doing.
I don't think it's off-topic to discuss the meaning of terms in a thread dedicated to an RFC which is proposing to change the meaning of those terms.
Changing the semantics of document updates is not part of this RFC, thus it is off topic.
There's also a finite amount of going around in circles on a given topic that's sane in a thread that core devs are trying to catch up on. It's best to avoid them needing to read a dozen pages of comments on the semantics of words and how people would rather things work if they were designing things when the staff are trying to catch up and look for actionable suggestions.
I've read all the conversation for the past while about update vs apply vs other suggestions. My impression from reading is that there isn't a better proposal, or if there is a better proposal, it hasn't been presented in a way that makes it clear that its better.
- We have a clear requirement to persist document changes to the database. We have used
updatefor this since the dawn of time as it's the "U" in "CRUD" which is pretty much the most standard way to think about functions for interacting with persistent storage. I cannot imagine us changing this. - We also have a clear use case (although not a requirement) for a method which makes local changes, either as part of a handling a server response or as part of making changes which don't require a persistence operation.
None of the alternatives for how to name thing #2 which have been offhandedly mentioned for this are presented in a way that is clearly a better choice than apply(). If there is an option that is clearly a better choice than apply this would be a great time to identify it.
Yeah, I don't think there's a clear better one here. I'll admit I'm more fond of update with commit: true or not but having update and apply is just fine imo!
thanks for the succinct reply
Looking at synonyms, modify, revise, adjust, mutate, or something along those lines could work as a slightly more accurate verb than apply, but I don't think apply is inherently bad either.
I think that whatever term is used, most people will get used to it and adjust to it but nothing will be perfect in everyone's eyes
I think that apply is the wrong word ^
commit: false as an option would be great. pretend is also very self-explanatory.
pretend is bad because sometimes this method is used to apply what is fundamentally the true state of the data (i.e. not pretend)
update({}, {commit: false}) is okay. I would personally prefer to have a different named method.
Why is that? Are there advantages to a different named method, but with practically the same call signature?
it's not the same call signature though, not really
update(changes, databaseModificationOptions) vs apply(changes, changeApplicationOptions)
the keys/values of the options are not really overlapping
yes, the changes are maybe the same
What's the difference between an apply method and directly updating the document through normal assignments? I guess I don't understand what the use case for a non commit update is.
I think personally what would be most frustrating about reusing .update is when reading code to have to read carefully to differentiate between local and persisted changes
Having separate method names makes it easier for me to quickly read and understand intent
My concern there is not speculative. The current state of v9 is that I have to read carefully to see whether it’s a document.data.update() or a document.update()
It’s currently a problem
I thought document.data.update wasn't going to exist anymore?
The functionality needs to exist, it cannot remain at document.data.update and needs a new home
I think in the case that we have multiple updates sitting around with different purposes, yeah apply is probably necessary... I like another parameter though so there might be a different refactoring here? Like changing document.data.update to a completely different name or to require commit as false (I believe it's local only?).
Ah, yeah that would be confusing.
Though I feel like apply in particular implies the wrong thing.
It quite literally is applying changes to the data model…
I also agree that update is the right verb for the true CRUD operation
mutate is interesting. It is the closest verb to what is happening locally. But perhaps only to JavaScript devs?
But my instinct would be that it means "actually save these changes" like in a windows setting dialog.
Yeah I honestly think between update and apply anything else would be prettttty unusual so I'd personally stick with one of the first two.
With the data versus document update I'd actually swing a lot closer to requiring apply
perhaps unhelpfully
Other than basic semantic arguing for the sake of arguing
Does it really matter what the hell it's called as long as everyone knows what it does and the jsdoc note on it explains it clearly?
Names are important!
In the same way that data.data is fine, yes.
Got by for more than two years on it! 😄
Haha it's not like names almost ever destroy the capability to work with something but trying to get a semantic consistency does feel important
Especially when you really really don't want to have to change it again later.
I think that having separate named methods could be confusing for beginners since they'll likely not know about apply immediately (because it's less useful/popular) and look for workarounds...
It just feels like, perhaps, spending as many hours as have been spent arguing for or against .apply() might be a bit unnecessary.
i feel like at this point it could be dataModel.ephemeralTurkey() and as long as we know that turkey means it's applying changes to the data model without persisting them we'd be good
Under that logic, this entire thread and it's parent channel are unnecessary
Well, let's apply some thought to this and after awhile we can come back and update the thread with a final answer...
Nah, works fine for me 🙂
I think the point is proportionality
Anathema is pointing out that apply and update are small points of a big RFC that've gotten a lot of attention
and that ofc they want feedback but it's been a bit long of a discussion
I have never felt so seen. ❤️
I was the one that suggested {commit} as an options parameter, and my thinking was update({},{commit: true}) would throw an error if the thing being updated didn't exist in the database (i.e. was a temporary Document that only existed on the client)
I think that may be because we are all mostly in agreement on the rest of the RFC which is something for staff to be happy about
That is already true of the update method if the document does not exist in the DB
Listen i gave the prediction that by putting the RFC to the dev community we could be prepared for blood in the streets and devs standing on codebases yelling ANARCHY while burning their git repositories so i'm really over the moon that didn't happen.
Instead the devs can't agree on which world to yell 😛
I’m annoyed that people are going to be annoyed when the method is DataModel#apply
I don't think it's used very often, so most people will probably just not know about it until after they have been banging their head for a few hours and finally ask on Discord to find out it's a weirdly named method instead of an option for update 😉
I think nearly everyone will just mutate the properties of local objects directly and won't know that apply exists.
Don't think it's all that weird for what it does, and the documentation for .update could probably include a "temporary updates to the data model should instead use apply()"
I disagree on the "weirdly" named.
The docs will reflect it, viewing the function within the code will show it, and there's a logical thought pattern to apply something temporarily vs updating permanently - especially when the default method for permanently changing a document is, in fact, update.
We are our own most significant users of the API which matters a lot to us as creators of the software. Apply is used a ton by the core software. I think at the end of the day that this design is probably more for me and for the team than it is for the community of module devs
but i'll also say as someone who doesn't code by reading the API or the doc strings (because i'm a terrible coder)- I'm only gonna see a new method if i'm struggling to figure out how to do something on my own, or if i see it in someone else's code and go "The hell does that do?"
so i don't think that's any different for .apply or otherwise
Perhaps the solution is to make it private and just use it internally and let module devs manually update the data state however they want to.
My worry is that the two verbs would be mixed up. Which one updates the DB? Update or apply? It's not that we can't find out, but especially from users of other frameworks, apply sounds like the one that "completes" the update.
I mean, the name doesn't matter if you don't know it exists in the first place, no matter what you think it might be. Mutate, apply, alter, what have you. And what might seem the most likely to look for for me might not be the same to others.
But the base API docs will show the method (if it's not private) and be able to be seen.
_apply!
i think if the documentation is there to indicate the difference between apply and update; logically with no other understanding i'm going to assume update updates the data persistently and i'm gonna go look up what apply does.
meh... having a convenient way to change the data in preCreate hooks is still a good thing. I don't want to have to do that by hand... 😄
but to be straight honest no new coder is coming in and going "Oh boy i can't wait to read this entire API documentation."
An overwhelming majority of community devs get their start by looking at what other community devs are doing; the code in their modules and systems
so i don't really think it'd be all that much of a risk for confusing newbie coders like my dumb ass
There is a bit of "knowing too much" that creates new confusion. If Foundry were the only API I were familiar with, I wouldn't see the problem. Having used various frameworks and ORMs, apply sounds like it does the thing that it doesn't do.
Genuine question because I can’t think of one: Is there a prominent JS framework that uses .apply() for the update pillar of persistence?
I don't know one that uses the word apply, but commit and execute are common in the context of prepared database queries and transactions. It's less that it's a verb used in place of update and more that it's a verb that feels like it means "okay now do the thing" and in this context it's the one that doesn't do the thing. Where mutate, change, modify, etc. feel like synonyms for update, apply feels like it means something more.
I know of none. Maybe it's being avoided because in JS, apply is often used in terms of Function.prototype.apply(), so it might be confusing to people in other contexts?
So I think this thread has accomplished its task. No blood in the streets over the concept itself, just the word for it
I'm going to declare a moratorium on this conversation and say that I am entirely unconvinced with the provided arguments against .apply() and so that will be the method name in V10 Prototype 1. If it ends up sucking for reasons we don't anticipate we can revisit the conversation after people have hands on feedback time.
Fair enough
As an aside, generally I don't think that's the case. Making a mistake and then realizing why something didn't work is by far the more common method of self-education.
I'll chip in that ActiveEffect#apply exists already, and may pose a problem, as DnD5e overrides it.
Oh, that's indeed a problem... I also override it in my system. Thanks for noticing.
I look forward to the opportunity to use model.apply.apply
This was a collision, yes. As part of my prototype refactor this has been renamed to ActiveEffect#applyToActor
OK, it was not documented on the RFC.
Yeah, I’ve been tracking how various collisions are being resolved in a sibling issue
Don’t have a link, but it’s open on the milestone
I think in this case, it's shimable.
Since if the new apply function gets an Actor instance, it can instance check it and fix it up.
Eh…. I suppose. That feels a bit ugly and would maybe be best to just be a breaking change. I’ll consider it though
It is ugly, not going to lie.
But it would at least mean that things can bridge both V9 and V10.
that will still likely break for people who currently override it
I know my implementation would break 😄
Still better than nothing I think.
And that's only the most basic of shimming.
The question is if it's worth doing.
To be honest, I don't think mangling that up together is a good idea. I would much prefer a true breaking change if we need to name the thing apply
I admit, I have no skin in the game of AEs yet, but I have been wanting to play with them.
quitely mentions that there still are other potential names that don't result in conflicts
Yeah. I don't see why having this name is worth the breaking change...
I would say this completely changes the discussion. Semantics are one thing, but having to rename existing methods for the sake of this name is another story. Especailly when ActiveEffect#apply is itself a very well named method.
Plus... are active effects really going to be limited to Actors only forever? Refactoring this method to be used with other types of document wouldn't break its current contract, just expand it. But changing its name to applyToActor would mean future expansions of the AE system might require multiple nearly identical but differrently named methods rather than one unified method.
Whereas something like change shouldn't cause a conflict, the only instances of it that I can find are the jQuery change method. I can't find any modify or mutate, rolls have alter though I don't think that would be a conflict.
There are a bunch of "just as good" options that won't cause any breaking changes, and also might be less confusing.
The fact that they are limited to Actors is an implementation detail on the client side. I have already considered to make it possible to apply them to items. Haven’t implemented it yet but I have thought it through and it’s not even difficult.
I had plans to do that eventually in my system