BUT … my persistent UID is stored in material attributes. And then it changes the model enough to force the user to save it. And this persistent UID is created on the first time OCL need to read material attributes. Then without clear user changes.
This silent changes is not good in user workflow. He won’t understand why the model has change.
And that’s why my extension submission to EW was rejected.
Do you have any idea to have the same feature without this behavior ?
Persistent means that the data persists from one session to the next.
We save persistent data in attribute dictionaries which changes the model, yes.
There is no way to write persistent data (ie save it,) without also saving the file.
Your code has a comment that mentions the UUID is for the session. Is this correct?
If so, then Entity#entityID should be used during a session if the id does not need to be persistent.
Since you cannot save it to a attribute dictionary (which would change the model,) you’ll have to save it to a collection as extension data. Ie, a Hash or Array of data records.
The records could be subhashes, Structs or OpenStructs.
See the Ruby Core docs or Ruby books on these classes.
Also, you are free to save extra model data as files.
Yes, those ID need to be unique in session. But also persistent. Entity#EntityID, couldn’t be an enough solution, because it’s restricted to session.
The code you see to force ID to be unique in session is here because if you import a previously used material from an other model, it can have an already generated ID that could exists in current model. And then we must regenerate a new one.
The goal of those IDs are to identify a material during multiple sessions on multiple computer with the same single model file. In this context, external storage will be difficult.
From those IDs are created other IDs that represent a group of part. Linked to those group some user parameters will be stored. (For exemple cutting diagram options).
Sorry it could be difficult to understand these features without understanding how OCL works.
OCL detect that those 2 parts have the same thickness. Then it groups them under the group “MyMaterial / 20”. But this group doesn’t correspond to a real group in model. It’s a sort of “virtual” group.
To identify this group, OCL generate an ID from Material UUID and thickness (watch the code)
This group could have several options to store for Cutting Diagram configuration, for exemple. Those informations are stored in model attributes with a key that contains the “virtual” group ID.
Those options must be accessible even if the material name or color change. And must be accessible everywhere the model is opened.
And to achieve this material’s UID saved in Material attributes is a good solution.
But it involves to force the user to save the model on first time the material UID are generated …
One possibility could be to use Material#persitent_id if SU > 2020.1. But how to be able to open the same model on a previous version of SU …
Rather than writing your ID attributes when the window is first opened, you can try to write them once the user changes some property in your UI that needs to be written as an attribute. I’m not familiar with this extension, but generally there is conceptually nothing to save until the user enters some data or perform some action.
Alternatively you could wrap it in an undoable operation ‘Store Cutlist Material info’. That way if the user is wondering what changed he could see it on the undo stack and even undo it. This would probably satisfy the EW.
There are issue with older SketchUp versions and attribute dictionaries attached to Material objects.
Fixed a bug in .skm serialisation where arrays in material attributes were not written out to file
SketchUp sometimes crashes after applying materials. When using some Ruby plugins,
such as some of the render engine plugins that use entity observers, SketchUp previously
crashed when applying materials with attributes. This issue has been fixed.
I also thought there was a general bug that did not either write any attributes out to the .skm files or import any, (but I cannot find the reference after 15 mins of searching old release notes.)
If the user isn’t explicitly entering any data to the extension I would prefer not to do this either. For users not familiar with the Undo stack it can be difficult to figure out what extension performs a change to the model.
And if it’s not enough for EW validators needs we have to speak about my bad User Experience around Extension Warehouse due to lack of feedback, bugs in submission process and long wait for nothing… Everybody have something to review on his copy
The solution :
Now, I generate UUID only if the code need to access it.
I do not directly store new generated UUIDs in material’s attributes. Those UUIDS are temporarily stored in a static variable of MaterialAttributes class.
I add a ModelObserver to be able to save those cached UUIDs to Material’s attributes when the user save the model through onPreSaveModel method.
The issue with writing to the model before the user performs performs any changes has been resolved, but the issue with changes not being done in a single undo operation still remains.
Regarding extension review UX, it’s frustrating for us too. For now this forum category can hopefully help for discussing extension issues, but long term I hope the EW can get more resources to fix its bug and issues.
Regarding the implementation I would personally avoid anything as complex as a ModelObserver. Once the user performs any change to the material through the extension, the relevant attributes can be written in the same operation. Until the user enters any data/performs any actions, there shouldn’t on a higher level be anything to save. All information the extension has up to this point has been fetched from the model. I’m not sure how ModelObservers work with auto saves and backups, and what risk there is of getting data and the user action out of sync here.
Personnaly I would prefer too. Several time observers crashes. But material’s metadata are used later outside the material tab for multiples things where it’s could be more complicated to keep the corresponding material instance to store it when the user do an “important” action.
My first strategy was clearly more simple. Save things as soon as possible to be sure to stay synchronized.
Else now that I can see that the new validation process is extremly more stronger, it would be good to write a full list of what is permit and what is not, no ?
Maybe the UUID could be generated and the attributes written once the user performs these actions?
Currently the extension requirements is hosted at the help center. We want to at some point move it and have it include in the Ruby API documentation where it’s easier to find and easier for us to maintain.
First, sorry to bypass the EW normal process. But OCL dev has progress during the last days and I can’t imagine publishing a previous release en EW. But current release was not enough tested to be published today. And it would be confortable to know is those issues are past.
Then, exceptionally, could you tell me if the current DEV build could satisfy EW rules ?