Ruby namespace?
-
@jim said:
Is there a way to get a reference to this anonymous namespace created by load?
I'd like to do something like this:
anon_namespace = load("/path/to/command.rb",true) > menu.add_item(anon_namespace.text) { anon_namespace.command } >
First of all load returns true/false for success or raises exceptions (such as LoadError, or SyntaxError from eval,) so you can't get it in that way.
It can be got... but must be got from INSIDE the object.
Also.. it's a temporary namespace.
It needs to be used ONLY for UnWrapped linear scripts, that don't need any persistant objects (such as the cmd or text attribute vars in your 'wishcode' above.)So the UI::Command class instance object(s) need to be outside the temporary namespace, because they must persist for the session, in order for the menus to work; and their callblocks would be:
{ load('*somefile.rb*',true) }
Here's a couple of test files:
-
@dan rathbun said:
Here's a couple of test files:
If you run the test scripts from the console, ie:
load 'test/UnWrapped.rb',true
and
load 'test/NoName.rb',true
you'll see that the annonymous namespace is assigned an identifier such as:
#<Module:0x55EF32A4>
it's basically a temporary pointer.
Before you could do anything with it, the loaded script would be done parsing or loading or running and ruby will have set the pointer to nil.
You can try it after running either script (they will report what the identifier is,) type the name of the identifier at the console with .inspect if you wish. Example:
` #Module:0x55EF32A4.inspectnil`
_ -
@jim said:
@jessejames said:
I noticed when i do obj.methods, and obj.private_methods, there is a lot of pollution in there. Names that obviously don't belong to that obj.
I wouldn't call it pollution. You're seeing all the methods that the object has. Some of them are inherited, some are class methods, others are instance methods. I suspect what you expected to see were only the instance methods of a class.
This is VERY important! For understanding Ruby,... but more so.. how the Sketchup Ruby API (and standard Pure-Ruby extensions (such as 'sketchup.rb' [as the best example,]) can be incorrectly implemented.
Ruby scripts... (if you don't tell them specifically what namespace to run in,) will run in (the global constant namespace,) TOPLEVEL_BINDING. (A Binding class object, is 'sort-of' an execution directive to a namespace.)
(1) TOPLEVEL_BINDING is preset to Object (uppercase.)
(2) Everything in Ruby is an object (lowercase,) meaning it is either an Object class, or a subclass (at some level of descendancy,) of Object class. [You may think of the lowercase word 'class' as 'type' if you come from a language where 'type' is more understandable. But recent revisions to Ruby are attempting to remove the word 'type' in favor of 'class.' Keep in mind that there is in Ruby a class Class, which is subclass of class Module, which is subclass of class Object.]
(3) The Sketchup Ruby Console operates in TOPLEVEL_BINDING. (Open the console and type: self <ENTERkey> , what do you see?
(4) Any script loaded runs in TOPLEVEL_BINDING.
(5) This means that most objects defined in TOPLEVEL_BINDING are in fact then defined WITHIN class Object. So if you define a method (without wrapping it with a Class or Module namespace, it is defined as a method of class Object. (Take a look at the 'sketchup.rb' script in the Tools folder and note the names of the methods defined there.)
(6) As every object in Ruby is a subclass of Object, every object will inherit (in some way,) those objects defined within class Object, including methods, constants, etc. IF you have incorrectly defined them there.
(7) The methods within the 'sketchup.rb' script were intended (for the most part,) to extend developer's use of the classes Module and Class. BUT they were not wrapped within the proper namespace(s) in the script, and so get defined as methods of Object, and so become inherited by ALL classes, such as the Numeric classes, the Sketchup::Entity subclasses, the Geom::subclasses, etc. where they will NEVER be used.
Open a new model, draw something simple a cube, select a Sketchup::Face object. Switch to the console, type (all 1 command):
**Sketchup.active_model.selection[0].private_methods(true).sort.join("\n")**
Look at the list, you will see that the Face object has inherited as private methods (which you will never call from outside the object,) all those methods defined in 'sketchup.rb'!
What kind of drain of memory will this have when you get to a model that has tens of thousands of Entities it it?"How do you fix this?"
(a) ANSWER: 'sketchup.rb' MUST DIE! The methods within NEED to be moved (defined as an extension,) to the proper %(#BF0000)Module where they should have been added in the first place:to module Sketchup:
-
file_loaded* file_loaded?* require_allto module UI:
-
add_separator_to_menu* inputbox (really a patch that needs to be made part of UI.inputbox.)
(b) Edit any script you use, that does not wrap it's code in a module namespace, especially method definitions with a module wrapper. Don't use any rbs (scrambled ruby) that you cannot wrap. (Contact the author and ask they wrap their code.)
(c) If you can.. use the load method with the wrap parameter set true, for menu command scripts, etc. when editing many many plugins is not an option. (The wrap argument for load will wrap the script in an anonymous namespace, and protect the global namespace from corruption.)
(d) Ask the Google Sketchup Team to fix the Sketchup::load method to expose the wrap argument, so we can specify wrap=true for rbs scripts. For some unknown reason, they defeated, or just didn't pass the 2nd argument (wrap) on to the aliased standard load, when they overrode it to handle rbs decrypting.
_ -
-
@dan rathbun said:
(7) The methods within the 'sketchup.rb' script were intended (for the most part,) to extend developer's use of the classes Module and Class. BUT they were not wrapped within the proper namespace(s) in the script, and so get defined as methods of Object, and so become inherited by ALL classes, such as the Numeric classes, the Sketchup::Entity subclasses, the Geom::subclasses, etc. where they will NEVER be used.
Very informative post Dan! But i disagree that the problem boils down to a lowly script. This problem is inherent in the design of the Ruby language itself!
@dan rathbun said:
Open a new model, draw something simple a cube, select a Sketchup::Face object. Switch to the console, type (all 1 command):
**Sketchup.active_model.selection[0].private_methods(true).sort.join("\n")**
Look at the list, you will see that the Face object has inherited as private methods (which you will never call from outside the object,) all those methods defined in 'sketchup.rb'!
What kind of drain of memory will this have when you get to a model that has tens of thousands of Entities it it?Exactly! this is what most of the folks in this thread have missed! Ruby just blindly sucks in every method it can find! In Python we have a system of using single and double leading underscores to indicate private and public methods. It's not a forced convention but we all understand and use it like a forced convention.
But even more importantly Python objects have a dir "special method" that will return the appropriate list of methods when using the introspection function dir(obj). Python also (as i have mentioned time and time again) uses every file as "module" automatically so none of the name clashing occurs unless you import the module in a certain manner.
Advertisement