Issues regarding extension publishing

Hi all,

I’m not sure who to reach out to but I submitted my my first extension for review on March 22nd and it is still in review. How long does this process usually take?

Thanks.

Bump?

I will try to get one of the EW team to come and answer your question.

Hello!
Taking advantage of your post, I would like to report my problem also related to extensions.
I’m having trouble signing and encrypting my extension at this link: Sign Extension | SketchUp Extension Warehouse
It’s just a first test, then I’ll deliver my real extension. I believe I’m following the recommended standards and still can’t encrypt.
I already sent an email asking for support more than three days ago and they haven’t returned me.
It’s frustrating, it seems that Timble doesn’t want new plugin developers anymore.

The requirements to sign/encrypt your RBZ are relatively simple.

Make a new ZIP file containing your xxx.RB file which sets up the details of your extension and indicates the main code-file to use - e.g. ‘xxx_my_code’…
This ZIP must also contain a folder named ‘xxx’ matching the RB file.
It should not contain anything else.
The ‘xxx.RB’ file should refer explicitly to the sub-folder’s main code-file by name, but it should not use the .RB suffix you will have been using during testing. So use ‘xxx/xxx_my_code’ NOT ‘xxx/xxx_my_code.RB’
SketchUp loads the named file by assuming an RBE suffix [added after encryption] or if not found an RB suffix [before any encryption] - whichever it finds in the subfolder…

Before submitting the file for signing/encryption you must rename the xxx.ZIP as xxx.RBZ

Ensure you keep copies of any RB files you hope to encrypt, because after the encryption they will be changed irretrievably…

Yes, I believe I did just that.
I really don’t know what’s wrong.
It’s my first extension.
See the attachment…
mobnamico.rbz (714 Bytes)

There’s something weirdly wrong with your RBZ - it thinks it’s ‘read-only’ and I can’t get it to change no matter what I try and what it says it’s set to.
BUT if I make an RBZ from scratch using your RB & subfolder it encrypts and signs OK [NB: with tweaks***]…

***A couple of points to fix in your code…
You shouldn’t include the extension’s set up code in a separate class, just leave it in the main module…
And of course it does nothing as it stands as inside the encrypted RBE there’s also has a class that’s never runs…
Use modules rather than classes and explicitly tell things to do things rather than leaving a method call ‘loose’ within a class etc…

1 Like

Humm… Upon mentioning that something would be wrong with my rbz, I decided to investigate and really… I was creating a .rar instead of a .zip. :sweat_smile:
Okay, now it’s encrypting as expected, but displays an error when loading.

Error: #<LoadError: cannot load such file -- c:/users/robson/appdata/roaming/sketchup/sketchup 2020/sketchup/plugins/mobnamico/lista_pecas.rb>
c:/users/robson/appdata/roaming/sketchup/sketchup 2020/sketchup/plugins/mobnamico/mobnamico_load.rbe:3:in `require_relative'
c:/users/robson/appdata/roaming/sketchup/sketchup 2020/sketchup/plugins/mobnamico/mobnamico_load.rbe:3:in `<main>'
C:/Program Files/SketchUp/SketchUp 2020/Tools/extensions.rb:197:in `eval'
C:/Program Files/SketchUp/SketchUp 2020/Tools/extensions.rb:197:in `require'
C:/Program Files/SketchUp/SketchUp 2020/Tools/extensions.rb:197:in `load'
C:/Users/Robson/AppData/Roaming/SketchUp/SketchUp 2020/SketchUp/Plugins/mobnamico.rb:19:in `register_extension'
C:/Users/Robson/AppData/Roaming/SketchUp/SketchUp 2020/SketchUp/Plugins/mobnamico.rb:19:in `<module:MobNamicoHome>'
C:/Users/Robson/AppData/Roaming/SketchUp/SketchUp 2020/SketchUp/Plugins/mobnamico.rb:7:in `<module:MobNamico>'
C:/Users/Robson/AppData/Roaming/SketchUp/SketchUp 2020/SketchUp/Plugins/mobnamico.rb:6:in `<module:Plugins>'
C:/Users/Robson/AppData/Roaming/SketchUp/SketchUp 2020/SketchUp/Plugins/mobnamico.rb:5:in `<module:JRobson>'
C:/Users/Robson/AppData/Roaming/SketchUp/SketchUp 2020/SketchUp/Plugins/mobnamico.rb:4:in `<top (required)>'

I have already removed the .rb extensions in require, because I know that later it will be .rbe, I also changed from require_relative to require and nothing works, but without encryption it works. Why?

If you’re going to end up with an RBE file you need to use an alternative way of ‘requiring’ a file…
The native ‘require’ methods expect an RB file etc - so once encrypted to RBE it’ll fail…
This alternative way will load either the original RB and any subsequent RBE you make from it…

In the loader file’s code set up a path variable to the loader’s subfolder […/mobnamico] and reuse that…

SUB = FILE.dirname(__FILE__)

then

Sketchup::require(FILE.join(SUB, "lista_pecas"))

Note the missing RB suffix to allow it to work with RB or RBE…
See this…
https://ruby.sketchup.com/Sketchup.html#require-class_method

Ok, I tried to do it that way and until I got success except when I try to load files inside other subfolders.
I decided to undo all the changes and go back to the version that worked before.
But not even this one works anymore.
It looks like it created some blockage to just work the new way.

load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/mobnamico-sketchup/src/mobnamico/lista_pecas.rb"
load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/mobnamico-sketchup/src/mobnamico/troca_componente.rb"
load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/mobnamico-sketchup/src/mobnamico/troca_material.rb"
load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/mobnamico-sketchup/src/mobnamico/mobnamico_load.rb"
load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/mobnamico-sketchup/src/mobnamico.rb"
File load error (mobnamico/mobnamico_load): Could not find included file 'mobnamico/mobnamico_load'
true

This seems to happen with other extensions as well.

load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/vscode-template-sketchup-projects/src/ex_hello_cube.rb"
load "C:/Users/Robson/Documents/workspace-vscode/sketchup-ruby/vscode-template-sketchup-projects/src/ex_hello_cube/main.rb"
File load error (ex_hello_cube/main): Could not find included file 'ex_hello_cube/main'
true

Extensions load only this error appears, I don’t know if this is a serious problem.

You are muddling up the requires / load native methods which need a .rb suffix, compared to the Sketchup::require() methods which need a path to a file, but the suffix can be omitted…

Without seeing your whole code getting you into this mess who can tell /
But if you get a ‘load_error’ telling you that it can’t find

mobnamico/mobnamico_load

There’s a good chance it’s the missing suffix that’s tripped you up !

The second error is less obvious since you don’t give enough info…
Although it seems that the

'ex_hello_cube/main'

wasn’t found, so it couldn’t be loaded ! [no .rb ??]

Please try going in ‘baby-steps’ - currently your ‘scatter-gun’ approach is very difficult to understand and debug…

Yes, I still haven’t been able to use your approach with the Sketchup::require(File.join(SUB, “part_list”)) method)
But what I mean has nothing to do with this approach, but with the fact that before, extensions normally loaded with the load method “C:/…/…/test.rb”, now they have the mentioned error above.
If I download this project: https://github.com/SketchUp/sketchup-extension-vscode-project.git

and loading your code into the console it gives the error.
They keep working, only this strange error appears. Anyway…
I’m going to study more and try to make Sketchup load my extension no matter if it’s encrypted or not.
Thanks again for the tips. :grinning:

Just one more thing.
How should I add one module to another using this approach.

Sketchup::require(File.join(‘mobnamico’, ‘constants’))

I’m trying to ‘include MobnamicoConstants’ but to no avail.

I don’t think you understand it.
The Sketchup::require(…)
expects a path to a single file.
Your code…

File.join('mobnamico', 'constants')

does nothing… because Sketchup is probably not expecting its path to start from there, AND your ‘constants’ is unlikely to be a file either ??
BUT you say you are trying to ‘include’ include MobnamicoConstants

You haven’t explained what that is or how it is structured… e.g.
If it’s a Constant [it starts with a capital-letter] ?
Or an additional module - in which case you could simply add a line at the start of the other module

include MobnamicoConstants

Or define your nested modules with an overarching module, so they are all auto-included…

Your partial code snippets are not helpful to those trying to help you - you also need to provide a full explanation of what it is you’re trying to do…
So far some educated guesses have helped move this thread forward… but you can help yourself a lot by communicating more clearly.
I appreciate that English might not be your first language - but GoogleTranslate [and some forethought] can be a big help when you are formulating your questions…

Actually his main issue is that he is loading from a coding repository and has not pushed his repository path into the $LOAD_PATH array …

The following script can be named: "_load_from_vscode.rb"

# Put this rb file into SketchUp's "Plugins" folder.

# define a path to your vscode projects:
vscode = "C:/Users/Robson/Documents/workspace-vscode"

# Now push any code repo path you are working on into $LOAD_PATH:
repo = "sketchup-ruby/mobnamico-sketchup/src/"
$LOAD_PATH << File.join(vscode,repo)
Sketchup.require("mobnamico")

Sorry, I will try to be more clear.
This is my constants module:

module JRobson
  module Plugins
    module MobNamico
      module MobnamicoConstants
        DYDICT ||= 'dynamic_attributes'.freeze
        #...
        #...
      end
    end
  end
end

This is where I try to include a constant.

# require_relative 'component_utils.rb'
# require_relative 'constants/constants.rb'
Sketchup::require(File.join('mobnamico', 'component_utils'))
Sketchup::require(File.join('mobnamico', 'constants', 'constants'))

module JRobson
  module Plugins
    module MobNamico
      module TrocaMaterial
        include MobnamicoConstants

        puts DYDICT
        
      end
    end
  end
end

When I used require_relative ‘constants/constants.rb’ it worked fine, now not anymore.

Forget what I said, the fact is that before the constants file were loaded automatically without calling load in the console. Now I’ve seen that if I call load “C:/…/…/constants.rb” it works.
Forgiveness… :no_mouth:
Thank you all.

A few notes:

JRobson::Plugins subfolder:

Your JRobson::Plugins submodule is frivolous and only serves to waste an indent.
Think about it … you will only use these files within SketchUp and they will always be Extensions.

Also a “plugin” is also not a SketchUp “extension”. There are two “schools of thought”.

  • Trimble uses the term “plugin” to mean compiled C++ importer and exporter libraries that go in SketchUp’s “Importers” and “Exporters” folders.

    Extension Registrar scripts and their extension subfolder of the same name, go in SketchUp’s “Plugins” folder. The name “Plugins” predates the creation of the SketchupExtension class and their file organization. (Nowadays, extensions can also contain compiled C files.)
    Back in those pre-extension days, Ruby scripts were known as “Ruby plugins” and were just dropped into the “Plugins” folder. This eventually led to file and folder havoc and clashing filenames.

  • The SketchUcation Tools (PluginStore manager) utility considers an “extension” to be the set of files loaded from an extension subfolder that is loaded by a proper SketchupExtension object and can be switched on and off via SketchUp’s Extension Manager interface.
    But it also considers any Ruby script automatically run on startup from the “Plugins” folder (including the extension registrar scripts,) to be “plugins”.

There was a friendly debate on whether a “plugin is a subtype of extension” or that an “extension is a subtype of plugin”. It doesn’t matter which a person prefers as both “extend” SketchUp’s Ruby is give it more functionality.

Anyway, my point here is that you are definitely creating a formal SketchUp extension that defines a proper SketchupExtension object. So it is not really a “plugin” in either school of thought.

Extension and RBZ file naming:

But the main reason I think this first submodule should be removed is, that proper SketchupExtension objects must follow rules as to the file naming. And that is that the prefix needs to be your unique namespace module name, then an underscore character (’_’) and then the name of the extension. What has become standard practice for most extension authors is to use the top 2 module names… ie the namespace module and the extension submodule name, so that the following (called from within any of the extension’s subfolder files) and from within the extension submodule is true

Module::nesting[0].name.gsub('::','_') == File.basename(File.dirname(__dir__))

In other words your module nesting would be: JRobson::MobNamico, and your
extension registrar script would be "JRobson_MobNamico.rb", and it’s subfolder
would be the same name without the file extension, ie: "JRobson_MobNamico".
(This naming is expected by the code within the "Tools/extensions.rb" file, which is not encrypted and you can read it to gain better insight into how extension files are loaded.)

When you zip up the extension into an RBZ archive, it retains the extension folder name, but it is convention to add the extension’s version to the end of the name separated by an underscore:
"JRobson_MobNamico_v1.0.0.rbz"

As you published updated versions, you change the version in the RBZ name (as well as in the extension registrar file.)

The "JRobson_MobNamico.rb" extension registrar script:

  1. The requires at the top for “extensions.rb” and “sketchup.rb” are not needed as they have been automatically loaded by SketchUp before any extension begin loading since SketchUp 2014.0.

  2. The MobNamicoHome class is frivolous and unneeded. The SketchupExtension object should be defined in it’s extension submodule, and you might as well define it as a local constant.

  3. Wrapping the SketchupExtension object within an unless file_loaded?(__FILE__) block is unneeded (as these files are loaded once by SketchUp during the startup cycle,) but in addition serves only to push an needed long pathname string into the global $loaded_files array.
    This can result in longer search times when subsequent necessary calls to #file_loaded? are made. (Ie, the method must search a larger array of path strings than should be necessary.)

  • Coding Sages have long been promoting the better paradigm of using a local persistent @var to control “load only once” scenarios. It is must faster, does not bloat the global path array, and does not rely upon a data structure that could be compromised by erroneous code. (Ie, a mistake by someone else’s code could clear out all the path strings in the global $loaded_files array, … or mistakenly delete a path for your extension.)
  1. Freezing string literals will not prevent a constant that points at that string from being reassigned to point at another string object. In order to freeze constants they need to be wrapped in a submodule and then that submodule frozen after setting the constants. Example:
# encoding: UTF-8
#
# Extension Registrar: "JRobson_MobNamico.rb"

module JRobson
  module MobNamico

    module Properties
      NAME      ||= 'MobNamico'
      VERSION   ||= '1.0.0'
      CREATOR   ||= 'Robson'
      COPYRIGHT ||= "#{CREATOR} © #{Time.now.year}"

      EXTENSION ||= SketchupExtension.new(
        NAME, 'JRobson_MobNamico/mobnamico_load'
      )
      EXTENSION.instance_eval {
        self.version     = VERSION
        self.copyright   = COPYRIGHT
        self.creator     = CREATOR
        self.description = 'Plugin SketchUp para marcenaria, '<<
          'utiliza componentes dinamicos como base.'
        Sketchup.register_extension(self, true)
      }
    end
    Properties.freeze

    include Properties
    
  end
end

(scroll to see all code)

  1. continued : This loads and works to an extent in that the Properties submodule is frozen and none of it’s constants can be changed.
    However, a SketchupExtension object should NOT be frozen because the Extension Manager interface can no longer manipulate it. What this means is that any of the display properties can be reassigned at any time via the object’s setter methods. Closing and reopening the Extension Manager interface will show the changes.
    This means that any attempt to freeze an extension’s property values is a waste of time.

You might go thru the rigmarole of using a custom extension subclass and undefining methods after you setup the extension instance, like:

# encoding: UTF-8
#
# Extension Registrar: "JRobson_MobNamico.rb"

module JRobson
  module MobNamico

    NAME      ||= 'MobNamico'
    VERSION   ||= '1.0.0'
    CREATOR   ||= 'Robson'
    COPYRIGHT ||= "#{CREATOR} © #{Time.now.year}"

    # Create a subclass of the SketchupExtension class:
    MobNamicoExtension = Class.new(SketchupExtension)

    EXTENSION ||= MobNamicoExtension.new(
      NAME, 'JRobson_MobNamico/mobnamico_load'
    )
    EXTENSION.instance_eval do

      self.version     = VERSION
      self.copyright   = COPYRIGHT
      self.creator     = CREATOR
      self.description = 'Plugin SketchUp para marcenaria, '<<
        'utiliza componentes dinamicos como base.'

      Sketchup.register_extension(self, true)

      # Prevent changes using the setter methods:
      [:copyright, :creator, :description, :name, :version].each do |meth|
        self.class.undef_method("#{meth}=")
      end
      # Prevent tinkering using Ruby override methods:
      self.class.class_eval {
        [ :alias_method, :class_eval, :class_exec, :define_method,
          :define_singleton_method, :extend, :remove_instance_variable,
          :instance_eval, :instance_method, :instance_variable_set,
          :module_eval, :module_exec, :remove_method, :undef_method
        ].each do |meth|
          begin
            undef_method(meth) rescue singleton_class.undef_method(meth)
          rescue NameError
            puts "Error undefining: #{meth}"
          end
        end
      }

    end # EXTENSION setup

  end
end

(scroll to see all code)

… But that is a bit much. Ruby is a dynamic language and it’s modules classes and instance objects are meant to be manipulated at runtime. Even though it is possible for some one else’s code to change extension properties (say all of the extension names to “Buy Widgets at widget.com”) we’ve never heard of someone doing dirty tricks like this. Any dirty extension would not get through Trimble’s Review.

So rather than deal with that rigmarole (of undefining methods on a custom subclass,) it’s just simpler to forget about trying to protect (freeze) properties and use a simple registrar format like:

# encoding: UTF-8
#
# Extension Registrar: "JRobson_MobNamico.rb"

module JRobson
  module MobNamico

    NAME      ||= 'MobNamico'
    VERSION   ||= '1.0.0'
    CREATOR   ||= 'Robson'
    COPYRIGHT ||= "#{CREATOR} © #{Time.now.year}"

    # Setup the SketchupExtension instance:
    EXTENSION ||= SketchupExtension.new(
      NAME, 'JRobson_MobNamico/mobnamico_load'
    )
    EXTENSION.version     = VERSION
    EXTENSION.copyright   = COPYRIGHT
    EXTENSION.creator     = CREATOR
    EXTENSION.description = 'Plugin SketchUp para marcenaria, '<<
      'utiliza componentes dinamicos como base.'

    # Register the extension with SketchUp's Extension Manager:
    Sketchup.register_extension(EXTENSION, true)

  end
end

(scroll to see all code)

~

2 Likes

As said above, don’t use #load. Use Sketchup::require.

As I also said above, if your code is not in SketchUp’s “Plugins” folder(s), then you must push the path to your code repository into Ruby’s global $LOAD_PATH array, if you intend to use relative paths with either Sketchup::require or Kernel#require.

Your other choice is to use absolute paths. Ruby’s Kernel module defines a global method named __dir__ which returns the absolute path of the directory for the file from which it is called. (This was added in Ruby 2.0, so it can be used for SketchUp 2014 and higher.)

In older SU versions running Ruby version less than 2 ...

In older SU versions running Ruby version less than 2, code can use the __FILE__ interpreter function which returns the full absolute path for the file from which it is used. To strip off the filename and get the directory path in each Ruby file, use

 __dir__ = File.dirname(__FILE__)

But do not do this in Ruby 2.x or you’ll locally override the global __dir__ method.

Example "JRobson_MobNamico/mobnamico_load" file …

# encoding: UTF-8
# File "mobnamico_load"
module JRobson
  module MobNamico

    # Load all files for this extension:
    ["component_utils", "constants/constants"].each do |filename|
      ::Sketchup::require(File.join(__dir__,filename))
    end

  end
end

Add any more filenames to the literal array, in the order that they need to be loaded.

~

1 Like

As always, you’re amazing with your explanations @DanRathbun.
I was really confused on how to organize my modules and followed some examples from that repository: SketchUp · GitHub.
I also read somewhere that in addition to three modules a class would be welcome.
But with your lesson I can refactor my code and follow the example by default.
In relation to the constants, vscode was adding this ‘.freeze’ method automatically every time I indented the code, I didn’t do it intentionally. But thanks for the explanation about constants.

Load my code into the console:
I will try to load it that way.
Thank you all.