[code] SketchupExtension and rbs rubies

[code] SketchupExtension and rbs rubies

Postby Dan Rathbun » Sun Jul 31, 2011 5:05 pm


[ code ] SketchupExtension and rbs rubies

What is SketchupExtension ?

    It's not AN extension (or plugin,) it's a Ruby class named SketchupExtension that is basically a custom hash. It registers plugins with the Sketchup application, so it can display them in a list. The list is on the "Extensions" panel, of the "Preferences" dialog (accessed from the "Window" menu.). The list has check boxes so the user can decide whether the extension / plugin will (or will not be,) loaded when Sketchup starts.




This example concentrates on the quirks regarding scrambled rbs scripts with the SketchupExtension class. (Although the issues here can also be applied to non-scrambled Ruby scripts, as well.)

WARNING: This is an OLD topic thread. The SketchupExtension class has been revised (at least) twice since this topic was written.



1) Manage your "filespace"

Why mix your files up with everyone else's ??

If everyone puts all their files into the Plugins folder, we will have filename clashes, and the Plugins folder will become unmanagable. We will not know what files belong to which plugin.

I encourage rubyists, to use an "author" subfolder, of the Plugins folder (which would be their top level "filespace",) and put ALL their plugins, in sub-folders of their "author" subfolder. (The actual name of the "author" folder can be a company name, or your SCF screen name, or whatever you like. Just do not use some other entity's trademark name like "CocaCola".)

WARNING: Since the ExtensionWarehouse was implemented, ALL plugins sub-dirirectories must be directly in one of the "plugin" directories, AND be prefixed with the author's namespace, followed by an underscore character. (See the ExtensionWarehouse Developer's Documentation.)

A specific plugin subdir, can have it's own subdirs, if you wish, such as "images".

Keep naming conventions simple, by sync'ing directory names and module (plugin) names:
  • Use the same name for your toplevel author namespace, AND your toplevel folder (directory.)
  • Use the same names for your submodule names, AND your subfolder names. This makes it easier to remember what the pathstrings and namespace stings will be, when you code require(), load() and include() arguments.



2) Manage your Ruby namespaces

You should be using a toplevel "Author" or "Company" module to wrap ALL of your plugins, to protect your code from other author's code, and their code from yours.

Each one of your separate plugins should be defined within a separate submodule of your toplevel module, so the code for each plugin runs within it's own namespace, and does not clash with your other plugins.



3) Using SketchupExtension class and scrambled rbs plugins.

In the examples below... the identifer Author would be replaced by your own toplevel module identifier, and the folder name "author" would be replaced by the name of YOUR author folder.



There are a few quirks you need to be aware of with scrambled rubies.

1) If your using the SketchupExtension class (see file Tools/extensions.rb,) the file refered to in the path attribute must be an unscrambled script. Convention is that it is named with a "_loader.rb" suffix. So if the name of your plugin is "widget" then the registration script in the Plugins folder would be "author_widget_ext.rb", which sets the path to "author/widget/widget_loader.rb" (in your plugin subfolder, of your author subfolder.)
The script for "author/widget/widget_loader.rb" would be a one-liner, that simply says:
Sketchup.require("author/widget/widget")
which will (if it isn't loaded,) will call Sketchup.load("author/widget/widget.rbs")
  • SketchupExtension registration scripts:
    • IF they will be in the "Plugins" folder, prefix their filename, with "Author_" (where Author is YOUR toplevel module name, aka YOUR namespace.) This helps prevent name clashes, and we can tell at a glance in a directory listing, who authored the file.)
    • Put the PluginName into the filename, after any prefix, and before the suffix. (Where PluginName is the name of the plugin, or it's submodule name. This helps prevent filename clashes, and we can tell at a glance in a directory listing, what plugin it registers.)
    • Suffix them with "_ext.rb" so we can tell at a glance, that they are SketchupExtension registration scripts.

2) Special Ruby __vars__The special Ruby keywords (actually built-in functions,) __FILE__ and __LINE__ do NOT work inside scrambled rbs scripts (as of v8.0M2.)
I've reported it in the last beta round, and we've complained about it all thru the ver 7 lifecycle as well. Hoping it will be fixed in the next release.
[Fixed with SketchUp 2014M0, using Ruby 2.0]

Workaround:

Keep a reference to your SketchupExtension instance object, by doing this for your extension registration script (this file goes in the main Plugins folder.)

author_widget_ext.rb
Code: Select all
require('extensions.rb')

module Author  # Proprietary TopLevel Namespace: No Tresspassing!

  module Widget  # Namespace for THIS plugin

    # Create an entry in the Extension list that loads
    #  a script called: "author/widget/widget_loader.rb"

    @@plugin = SketchupExtension.new( "Author's Widget", "author/widget/widget_loader.rb" )
   
    @@plugin.creator   = 'Author'
    @@plugin.copyright = "(c)2011 by Author"
   
    @@plugin.version   = '1.0.0'

    @@plugin.description = "A Widget that does fancy things."
   
    unless @@plugin.class.method_defined?(:path)
      #
      # Define a singleton method for @@plugin to access the @path attribute.
      # (... because the API did not do it in extensions.rb !!)
      #
      def @@plugin.path()
        instance_variable_get(:@path)
      end
    end # unless
   
    # Create local path and filename constants:
    #
    PATH = @@plugin.path()
    LOADER_SUFFIX = '_loader'
    LOADER = File.basename(PATH)
    RBSFILE = LOADER.split("_")[0] << '.rbs'

    # RELDIR is relative to Plugins dir, OR the dir from
    # the $LOAD_PATH array that require used to find it.
    RELDIR = File.dirname(PATH)
    # search the load paths for a valid RELDIR
    ROOT = $LOAD_PATH.find do |abspath|
      Kernel.test(?d, File.join(abspath,RELDIR) )
    end
    if ROOT
      ABSDIR = File.join( ROOT, RELDIR )
    else
      # assume the target dir is directly below, the dir that THIS file is in.
      ABSDIR = File.join( File.dirname(__FILE__), RELDIR )
    end
   
    # Register this extension with the Sketchup::ExtensionManager
    Sketchup.register_extension( @@plugin, true )

  end # module Author::Widget
 
end # module Author


"author/widget/widget_loader.rb"
Code: Select all
Sketchup.require("author/widget/widget")


Now all of the code in the "author/widget/widget.rbs" file should also be namespace wrapped within module Author::Widget like so:
Code: Select all
# widget.rbs
require('sketchup.rb')

# We can use a namespace qualification, saving an indent,
# because the outer module already has been defined.
#
# Since the inner Widget namespace also already exists,
# the following module definition block just adds to the
# module, or redefines things that are already defined.
# (Such as overriding methods, constants or variables.)
#
module Author::Widget

  # The constants PATH, ABSDIR, RELDIR, RBSFILE, etc., can be
  # accessed here because they are local to this namespace.
  # The same is true for the module var @@plugin, which is the
  # reference to your SketchupExtension plugin instance.

  def self.plugin()
    @@plugin
  end

  class WIDGETmaker
    # your Tool class definition
  end #class

  # The rest of your scrambled code goes here.
  # More methods, etc.
  # Can have nested submodules and class definitions, etc.


  imgdir = ABSDIR # or ABSDIR + "/images" if in a subdir


  ### Do only once block
  #
  unless file_loaded?( RBSFILE )

    # create toolbar
    @@toolbar = UI::Toolbar.new("Author Widget Toolbar")

    def self.toolbar()
      @@toolbar
    end

    # Button 1
    cmd = UI::Command.new("Widget") { Sketchup.active_model.select_tool WIDGETmaker.new }
    cmd.large_icon = File.join(imgdir, "WIDGET_LG.png")
    cmd.small_icon = File.join(imgdir, "WIDGET_SM.png")
    cmd.status_bar_text = "Use Author's nifty widget."
    cmd.tooltip = "Widget"
    @@toolbar.add_item cmd

    file_loaded( RBSFILE )

  end # unless

end #mod Author::Widget


3) Paths for toolbar icons:

The UI::Command instance methods large_icon() and small_icon() can (in unscrambled rb files,) take either an absolute path, or a relative path (that is relative to the dir that the ruby script is in, NOT relative to the Plugins folder.)

A scrambled rbs file CAN take an absolute path. But I don't know if it can take a relative path.



Edited: 08-AUG-2014
Removed false argument from find method call (line 40.)
1
Last edited by Dan Rathbun on Fri Aug 08, 2014 1:35 pm, edited 8 times in total.
    I'm not here much anymore. But a PM will fire email notifications.
    User avatar
    Dan Rathbun 
    PluginStore Author
    PluginStore Author
     

    Re: [code] SketchupExtension and rbs rubies

    Postby thomthom » Tue Aug 16, 2011 1:06 pm

    Dan Rathbun wrote:A scrambled rbs file CAN take an absolute path. But I don't know if it can take a relative path.

    When I hacked UI::Command to intercept the data plugins sent I found that Sandbox Tools used a relative path. Though I don't remember what it was relative to.
    0
    Thomas Thomassen — SketchUp Monkey & Coding addict
    List of my plugins and link to the CookieWare fund
    User avatar
    thomthom 
    PluginStore Author
    PluginStore Author
     

    Re: [code] SketchupExtension and rbs rubies

    Postby Dan Rathbun » Tue Aug 16, 2011 1:57 pm

    thomthom wrote:
    Dan Rathbun wrote:A scrambled rbs file CAN take an absolute path. But I don't know if it can take a relative path.

    When I hacked UI::Command to intercept the data plugins sent I found that Sandbox Tools used a relative path. Though I don't remember what it was relative to.

    The problem is... that example does not equate, because the command builder script for Sandbox is unscrambled. It's named Sandbox/sandboxmenus.rb
    0
      I'm not here much anymore. But a PM will fire email notifications.
      User avatar
      Dan Rathbun 
      PluginStore Author
      PluginStore Author
       

      Re: [code] SketchupExtension and rbs rubies

      Postby Dan Rathbun » Fri Aug 19, 2011 3:29 am


        Fixed: example above for "author_widget_ext.rb"

        ABSDIR was using File.expand_path incorrectly.

        Added in the search of $LOAD_PATH array so that the ext registration script can be in the Plugins folder (or any of the $LOAD_PATH folders,) and the target plugin dir can be below ANY of the $LOAD_PATH folders, (possibly even a different one than the "author_widget_ext.rb" script.)
        0
          I'm not here much anymore. But a PM will fire email notifications.
          User avatar
          Dan Rathbun 
          PluginStore Author
          PluginStore Author
           

          Re: [code] SketchupExtension and rbs rubies

          Postby Dan Rathbun » Sat Mar 10, 2012 9:13 pm


            Fixed: example above for "author_widget_ext.rb"

            Line 19: Sketchup.register_extension( @@plugin, true )
            Moved down to line 51, so ALL constants are defined BEFORE extension loading.



            Thanks to Keld Sørensen (ksor) for finding this bug.
            :thumb:
            0
              I'm not here much anymore. But a PM will fire email notifications.
              User avatar
              Dan Rathbun 
              PluginStore Author
              PluginStore Author
               

              Re: [code] SketchupExtension and rbs rubies

              Postby blruuska » Fri Jun 08, 2012 1:45 pm

              Dan, I tried to implement your extension script, but around line 25:
              Code: Select all
              def @@plugin.path()
                 @path
              end

              The @path variable seems to be undefined. Do you recall where that should be set? (I'm probably missing something?!)
              0
              User avatar
              blruuska 
               

              Re: [code] SketchupExtension and rbs rubies

              Postby Dan Rathbun » Fri Jun 08, 2012 2:46 pm

              No you're not missing something.. I am. Doh! It's a booboo.

              The @path variable is out of scope.

              But it's not THAT important. From inside you plugin, you could always just:
              ext_path = @@plugin.instance_eval("@path")

              Thanks for keeping me honest !! I'll fix it now.

                Fixed: example above for "author_widget_ext.rb", line 25


                blruuska wrote:Do you recall where that should be set?

                It is set by the SketchupExtension class constructor new() method's second argument, which is required. (Ie they will not let you create the object with empty args, and set them afterward. If @path does not get set, then it's load method will raise an exception. So they wanted to force you to set it in the constructor call. And, BTW there is no setter method defined either.)
                0
                  I'm not here much anymore. But a PM will fire email notifications.
                  User avatar
                  Dan Rathbun 
                  PluginStore Author
                  PluginStore Author
                   

                  Re: [code] SketchupExtension and rbs rubies

                  Postby greenskp » Thu Aug 07, 2014 2:10 am

                  Dan,

                  How to escape of the "false" at line 40?

                  Code: Select all
                  ROOT = $LOAD_PATH.find(false) do |abspath|


                  SketchUp 8 show undefined method `call' for false:FalseClass
                  0
                  User avatar
                  greenskp 
                  Premium Member
                  Premium Member
                   

                  Re: [code] SketchupExtension and rbs rubies

                  Postby Dan Rathbun » Fri Aug 08, 2014 1:24 pm

                  greenskp wrote:Dan,

                  How to escape of the "false" at line 40?

                  Code: Select all
                  ROOT = $LOAD_PATH.find(false) do |abspath|


                  SketchUp 8 show undefined method `call' for false:FalseClass


                  Just eliminate the argument for find. If no match is found, the method will return nil, which evaluates as false.

                  So use:
                  Code: Select all
                  ROOT = $LOAD_PATH.find do |abspath|


                  Sorry this is a very old topic thread. It may have errors running under newer Ruby versions.
                  0
                    I'm not here much anymore. But a PM will fire email notifications.
                    User avatar
                    Dan Rathbun 
                    PluginStore Author
                    PluginStore Author
                     

                    Re: [code] SketchupExtension and rbs rubies

                    Postby Dan Rathbun » Fri Aug 08, 2014 1:37 pm

                    WARNING: This is an OLD topic thread. The SketchupExtension class has been revised (at least) twice since this topic was written.
                    0
                      I'm not here much anymore. But a PM will fire email notifications.
                      User avatar
                      Dan Rathbun 
                      PluginStore Author
                      PluginStore Author
                       

                      SketchUcation One-Liner Adverts

                      by Ad Machine » 5 minutes ago



                      Ad Machine 
                      Robot
                       



                       

                      Return to Developers' Forum

                      Who is online

                      Users browsing this forum: No registered users and 12 guests