it trivial to split the menu items from the logic…
in your extension.rb you point to a menu.rb
# use 'lazy-loading' by only adding the ruby file containg menu and toolbar items
PLUGIN = SketchupExtension.new(extension_name, "your_extension_menu")
in your_extension_menu file you load needed images, translations and a ‘logic loader’ command
# add a cmd for menu, toolbar or shortcut item...
cmd = UI::Command.new(title) do
# load the logic containing any require code, resources, etc...
Sketchup.require File.join(path, "your_extension_logic")
# and if it's defined under the same namespace...
your_main_method
end
you add the command to where required and on activation it first loads all the resources it needs and then runs the items action…
additional commands can load it’s additional requirements and if none are activated during a session the the cost to SU performance is a few menu items…
there are many extensions that load observers, add attributes, materials and even components on SU startup and on every modification…
you only need to track the DC tool or vary to see all the activity that can be generated…
Except that (in your example) calling require() is slow, because it needs to check a full absolute pathstring against the HUGE number of strings kept in the $LOADED_FEATURES array.
I’d recommend defining “lazy loaded” module variables to keep track of what needs loading:
# at top of module
@@cmd_support_loaded ||= {}
@@cmd_support_loaded[:title]= false
# ... etc. for other commands this plugin defines
Then down in the command definition …
# add a cmd for menu, toolbar or shortcut item...
cmd = UI::Command.new(title) do
unless @@cmd_support_loaded[:title]
# load the logic containing any require code, resources, etc...
Sketchup.require File.join(__dir__, "title_command_method_logic.rb")
@@cmd_support_loaded[:title]= true
end
# and if it's defined under the same namespace...
title_command_method()
end
If you’re a real true Ruby geek, you could even get fancy and override method_missing() in your module, and have it do the loading when it gets called for a missing command method.
… oh no, I can’t help myself …
module Author
module SomePlugin
extend self
# Constants, module variable declarations,
# other method definitions, etc., etc., ...
def method_missing(symbol, *args, &block)
lazy_loaded_file = File::join(__dir__,"#{symbol}_logic.rb")
if exist?(lazy_loaded_file)
Sketchup::require(lazy_loaded_file) # load it
if block_given?
method(symbol).call(*args, &block)
else
method(symbol).call(*args)
end
else
super # otherwise pass it on up the chain
end
end
# Run once conditional block that loads commands, menus and toolbars
end
end
If you wait to load the translations until the user interacts with the plugin, the menu itself can’t be translated. Unless you split up the translation into two chunks, one that loads directly and one that leads laters, but that would just make things unnecessary complicated.
Some plugins need observers before the user even interacts with the menu because the observer is the primary way to interact with the extension.
Loading components, materials or setting attributes without user interaction is however not acceptable. I don’t think such a filthy extension would even pass the extension warehouse review. Such behavior would break the modified state of the model, causing SketchUp to ask whether the user wants to save the changes, even if the user hasn’t changed anything.
I’ve always had the translations for the SketchupExtension object separate from the plugin’s translations proper. (There is need to load the core translations until the user decides to switch the extension on.)
But this does not mean I had 2 sets of files. For extension registration, only a few strings need translating so I usually just use a literal Ruby hash or two, pick the strings needed, then clear the hash and set it to nil.
Two set of files or not, the translation is still split up into two places. I would not do that.
I think someone made a plugin that stops faces created in the X Y plane from being drawn back side up. My railroad plugin updates the tracks after they are moved with native move tool. My auto weld plugin also optionally welds edges created by native Follow Me, whether the plugin’s menu has been interacted with in that session or not.
The Extension Manager only needs two (2) strings from the SketchupExtension object (name and description,) in order to display the extension information in it’s list.
Why load an entireLanguageHandler object for the entire extension, when you do not know yet if the user is going to have the extension switched ON ?
To do so, wastes the user’s memory.
Sometimes I just use a case statement to set these rather than even have them in a separate file …
case Sketchup.get_locale
when 'en-US'
# load English
ext.name = "My Plugin"
ext.description = "A plugin that does nifty things ..."
when 'fr'
# set name and desciption
when 'it'
# set name and desciption
when # ... etc.
# ... etc.
else
# load English
ext.name = "My Plugin"
ext.description = "A plugin that does nifty things ..."
end
Once read by SketchUp these cannot be changed for the session, so there is no reason to have them in memory anymore.
A good starting point for lazy loading might be to implement the singleton design pattern instead of using modules.
require 'singleton'
module Author
module MyAwesomePlugin
class Logger
include Singleton
def initialize
@log = File.open("log.txt", "a")
end
def log(msg)
@log.puts(msg)
end
end
end
end
Author::MyAwesomePlugin::Logger.instance.log('message 2')
This makes sure that there will only be one instance of that class, which is only constructed the first time you call the instance. Resources are only assigned in the constructor (in the initialize method) and not during load time, which is the case when working with modules.
We are required to use Author/Company namespacing modules in SketchUp’s shared objectspace.
And then we do like to separate each of our extensions from one another within that namespace.
There is no way to avoid Author::SomePlugin namespace nested modules. They are required to publish on the Trimble Extension Warehouse.
Improperly namespaced SketchUp extensions have been and will be shunned by the community.