Error occur when require file in Plugin

I have some trouble when I required file inside folder in my Plugin. I definitely have file on path but I can’t figure out where i miss.

Here’s an error.

Error Loading File C:/Users/AppData/Roaming/SketchUp/SketchUp 2022/SketchUp/Plugins/MetaWood/metaWood.rb
Error: #<LoadError: cannot load such file -- metaWood/modules/rubyTools/editTool.rb>
C:/Program Files/SketchUp/SketchUp 2022/Tools/RubyStdLib/rubygems/core_ext/kernel_require.rb:92:in `require'
C:/Program Files/SketchUp/SketchUp 2022/Tools/RubyStdLib/rubygems/core_ext/kernel_require.rb:92:in `require'
C:/Users/AppData/Roaming/SketchUp/SketchUp 2022/SketchUp/Plugins/MetaWood/metaWood.rb:5:in `<top (required)>'
C:/Program Files/SketchUp/SketchUp 2022/Tools/extensions.rb:197:in `require'
C:/Program Files/SketchUp/SketchUp 2022/Tools/extensions.rb:197:in `load'
C:/Users/AppData/Roaming/SketchUp/SketchUp 2022/SketchUp/Plugins/meta_activate.rb:8:in `register_extension'
C:/Users/AppData/Roaming/SketchUp/SketchUp 2022/SketchUp/Plugins/meta_activate.rb:8:in `<module:MetaWood>'
C:/Users/AppData/Roaming/SketchUp/SketchUp 2022/SketchUp/Plugins/meta_activate.rb:4:in `<top (required)>'

Here’s my code for calling file.

We ask generally that you post code not images of code. See:
[How to] Post correctly formatted and colorized code on the forum?


FYI:

(1) Ruby uses 2 space indents. (Also reduces horizontal scrolling when posted to forums.)

There are several other problems:

(2) All of your code and each one of your extensions need to be within a toplevel namespace module whose name is unique to you or your company. Each one of your extension must be within a submodule to isolate them from one another and within your namespace module to isolate them from extensions by other authors/companies.

(3) The extensions registrar script defining the SketchupExtension data object needs to be named as a combination of your top level namespace and the extension name, separated using underscores. Usually it is just the namespace module name and the extension submodule name, with the :: scope operator replaced with a “_” character.
For example, if you chose your unique top level namespace to be MetaGame and this extension submodule to be MetaWood, then the name of the registrar file should be "MetaGame_MetaWood.rb".

  • This is necessary because the "Plugins" folder is a shared file space, and unique namespace prefixes prevent clashes where the RBZ installer would otherwise overwrite extension files from another author.

(4) All of each extension’s code files need to be in a subfolder of the “Plugins” folder, whose name is exactly the same as the registrar script (minus the .rb filetype.)
Following the example above, … the extension submodule folder would be named "MetaGame_MetaWood".

(5) The 2nd argument the SketchupExtension constructor call (in the registrar file) is a relative path not an absolute path. (Although the code snippet in the docs is a bit incorrect and way behind current suggested use.)
Secondly, the .rb filetype needs to be omitted from the loader file name, as the SketchupExtension class uses Sketchup::require. (Note, that you can read the contents of "Tools/extension.rb" that defines this class as it’s plain text. It’s located beneath the SketchUp program folder.)

(6) The use of unless file_loaded?(__FILE__) and file_loaded(__FILE__) are not necessary in a registrar script as SketchUp only processes the “Plugins” folders once at startup.
Secondly, even though the API documentation promotes the use of these method, they push all the long pathstrings into a global $loaded_files array (see the "Tools/sketchup.rb" file.) This array is global and shared by any and all extensions that happen to use these methods. Another reason why unique prefix filename are a must.
But in most interpretive languages, text string comparison is relatively slow, and as more and more extensions load, this global array gets bigger with more of these long pathstrings, which means searching this entire array for a non-match, ie, unless file_loaded?(__FILE__), takes more and more time.
It makes more sense to just define a local boolean module variable to prevent multiple evaluation of run once code.
Ie:

# Within an extension submodule:
unless defined?(@loaded)
  #
  # run once code such as define GUI objects
  #
  @loaded = true
end

You have as many as these local module variables as you need with certain names for whatever various “setup” tasks your extension needs to do. Ie @gui_loaded, @menu_defined, @tools_loaded, etc. (Rather than file based, it’s task based.)

You can also stuff them all into a single Hash object. Just use at the top of the first file:

    @loaded ||= {}

… then you can use unless @loaded[:some_task] and within the block, after doing the task, set the item @loaded[:some_task]= true.

(7) The actual error and it’s backtrace you posted above, come from what I call the “loader” file. (Ie, the 2nd argument to the SketchupExtension constructor call.

In this file you are not using Sketchup::require but instead using the global require (as defined in Kernel and mixed into the global ObjectSpace.)

If you read the doc for Kernel#require it will explain that a relative path argument will be appended to each root path in the global $LOAD_PATH array and checked for existence until a file is actually found to load.
SketchUp pushes the root paths to it’s various “Plugins” folders into the $LOAD_PATH array before it begins loading extensions.

You might have been thinking that the current working directory is automatically set to your extension subfolder, but this is untrue. After SketchUp loads Ruby and just before procesing extensions, it sets the current working directory to the user’s “Documents” folder (ie, File.join(ENV["USERPROFILE"], "Documents") for English systems on MS Windows. This has been this way since circa v2014 I think.

So basically, you are missing the parent directory in the path for these require calls … or you should perhaps be using require_relative* instead. (* Note that you cannot use this if you are planning to later RBE encrypt your extension files as encrypted files must be loaded with Sketchup::require.)

Conclusion. You used a absolute path when a relative path is required (5), and incorrect relative paths where they are allowed (7).

REF:


Inconsequential Items

(a) You do not need to use calls to require “sketchup.rb” or “extensions.rb” for every file. In fact these files are loaded automatically by SketchUp since v2014 before any extensions begin loading. One of those then loads “langhandler.rb”.

(b) You can use an iterative block to load files from a literal array of filenames (again without the .rb type) …

[
  'editTool', 'modelCreateTool','metaWoddLogin', ... etc., ...
].each do |file|
  Sketchup.require(File.join(__dir__,file))
end

The global __dir__ method is defined in the Kernel module. It returns the absolute path to the directory where the file being evaluated resides. Basically the same as we used to do with File.dirname(__FILE__).