I wonder if it is possible to perform operations in a plugin when its is being uninstalled? In my case, during the installation, the plugin creates some components in the Components\ folder of Sketchup
I’m pretty sure the API doesn’t provide any way to do this. So far as I know there is no way to be notified when an extension is uninstalled. It might be a useful feature request though.
I agree with Steve.
__
( The “closest thing” you can monitor is when the user is disabling the extension in Extension Manager, but unfortunately this doesn’t fire if you uninstall any extension so - still far away from your asked for…
The #onUnloadExtension method is called when the user turns off a Ruby extension… )
Note that custom components should always be saved or copied into a subfolder of the Components folder as the subfolder name will be used as the collection name in the interface.
The doc entry for that method doesn’t make clear what “when a user turns off an extension” means. I suspect it is when the extension is disabled via the extension manager, as otherwise it would be redundant with other methods. It’s also not the same as uninstalling.
Reading the definition file for the SketchupExtension class, we see that when a user unchecks an extension in the Extension Manager, OR some code calls #uncheck upon the extension object, SketchUp calls Sketchup::register_extension again with a false 2nd argument, which will call an internal #unload callback in the SketchupExtension object which sets the @load_on_start variable (which is eventually saved into the JSON preferences file for this extension.)
At some point after the “unchecking” the AppObserver#onUnloadExtension callback will get called in ALL app observers with this extension as the argument. When in the “unchecking” process it is called is not published. Ie, do the AppObservers get called before or after the extension object “housekeeping” ?
Yes, actually Ruby itself provides a way. It is called subclassing, where you use a modified “child” class of the SketchupExtension class.
I have just tested it and it does work. However, in the realm of best practices, it might be considered fragile, because the API SketchupExtension class’ behavior could change in the future and you would then need to issue an update to work around these possible changes. (To be fair however, the class has not changed at all in the last 11 years, since 2013.)
In the following example, I have a test extension with the DC Training components copied into the extension’s “Components” subfolder. When the extension is registered with SketchUp’s ExtensionsManager the subclass will copy the components files over into the user’s %AppData% Components folder within a destination collection folder.
When the extension is uninstalled this %AppData% Components collection subfolder is removed.
@slbaumgartner (or anyone else on a Mac) I only tested on Win 11.
If anyone has the time, please also test on Mac platform.
The SKP files in the extension’s “Components” subfolder should be copied (I think) over to: ~/Library/Application Support/SketchUp 2024/SketchUp/Components/Roadrunner Weapons
Upon uninstall this user resource subfolder and its contents should be deleted.
On Windows in SU2024 the Components panel is refreshed automatically, adding the extension specific components collection to the menu when it is registered, and removed from the collections menu when the extension is uninstalled. (I seem to remember in past versions that we had to restart SketchUp to get user resource collections to be properly enumerated. Apparently this has been fixed.)
Wow, thank you very much for your detailed response DanRathbun!
First of all, yes I am placing my components in a Subfolder of the Components\ folder. I will try to use the code in your example to delete my folder instead of the Roadrunner Weapons, shouldn’t be very difficult. I wonder if this resource will work for all SU versions after SU8, for that one I believe fileutils is not available.
NO! SU8 used Ruby 1.8.x and I did not write it for that old Ruby version. (I do not code for dead versions of Ruby. Supported versions of SketchUp are at Ruby 2.7 and higher. The latest version is at Ruby 3.2.)
Yes, SketchUp did not come with the Standard Library until SketchUp 2014 (which used Ruby 2.0.0,) so the FileUtils library module will not be available unless the user installs the Standard Library. Therefore no guarantees.
I wrapped up the Ruby 1.8.6 Standard Library into a SketchUp RBZ file for Windows only for SU8 and SU2013:
Also, there were some changes made to the SketchupExtension class with the 2013 release. So the mark_as_uninstalled() callback was not available in SU8.
There was also a major change with the 2017 release where the user custom resources were saved into the user’s %AppData% path. Prior to this on Windows the resources were in the %ProgramFiles% path which caused permissions issues.
Therefore, this solution would most likely only work (as is) for SketchUp 2017 and higher.
FYI: It is meant that you should rename the top-level namespace module to your namespace, and rename the extension submodule to your submodule. Of course the extension registrar file and extension subfolder must also be renamed to match according to Extension Requirements.
Of course, the extension properties must also be changed to match the extension under developement.
Ie, this code is not a library for use as is. It is an example extension registrar file (with a dummy "main.rb" and "Components" subfolder with some of the DC teaching components.)
Yes, I got this also when testing and moved the super call to the end of the callback override to hopefully have the correct type returned to SketchUp’s Extension Manager “engine” (at some point in the future.)
However, I believe this is a bug in the calling C core code as it happens even if there is no subclass in use. Meaning that the callback simply makes a Boolean assignment to the @has_been_uninstalled instance variable, but the core expects some other type as the return type. (I suspect integer as in C there really is no Boolean type, it uses 0 and 1.)
There is no way to trap this exception when it is the user’s manual uninstalling an extension, because it is being raised in the C++ EM core that internally calls the mark_as_uninstalled() callback.
Likewise the API does not expose an uninstall method for either the SketchupExtension or Sketchup::ExtensionsManager classes.
I believe that I had reported this to @ChrisFullmer during the last release cycle.
ADD: Also, this originally came up in the public forum here:
After testing a few return values, I have determined that the Extension Manager core wants a NULL return when calling the callback. So, I’ve updated the example to do this in the internal calback overrides in the subclass. viz:
# Callback called by SketchUp when the extension is uninstalled via the UI.
def mark_as_uninstalled
# Call a method to delete extension specific component files:
delete_components()
# Call the superclass' method which does some housekeeping:
super
return nil # The EM core wants a NULL result
end
for some reason the mark_as_uninstalled function is not being triggered when the plugin is uninstalled using Windows_manager/manage/Uninstall in SU24. However, I can see the warning message “Extension returned wrong type for method mark_as_uninstalled” popping up in the ruby API when the plugin is uninstalled.
The plugin installs with no error messages, at the moment I uninstall using Extensions_manager/manage/Uninstall in SU24 I can see the he warning message “Extension returned wrong type for method mark_as_uninstalled” popping up in the ruby console. I suspected my mark_as_uninstalled function was not triggered because my UI.messagebox is not executed neither I can see the MyComponents subfolder of the components folder being deleted after the uninstall is completed.