Module Wrapper for Your Ruby
-
It's a good idea, I was told, to wrap your Ruby code inside a module. I did this, placing "module Vismap" at the top, and "end" at the bottom. This would, I anticipated, avoid name collisions with other loaded Rubies. In fact, it broke nearly everything. I undid the change and went on with the non-module version.
Today I had a minute to catch my breath, so I gave it another try, with help from Fredo, Chris Fullmer and Jim Foltz. This is a list of changes that I made. Hopefully, others will add to this list so that, over time, we can develop a comprehensive checklist.
I assume your Ruby has a name, "Rubyname", and that you did a thorough bit of googling to ensure that "Rubyname" was unique.
- Just after you require 'sketchup', add "module Rubyname" and after the last line, add "end # module Rubyname".
# rubyname.rb - purpose, author, etc. require 'sketchup' module Rubyname ... Ruby code end # module Rubyname # end rubyname.rb
- Functions defined in your code, at the outer level: Replace "def someFunc" with "def self.someFunc" (or "def Rubyname.someFunc").
def self.thisFunc( args ) ... end def Rubyname.thatFunc( args ) ... end
-
Classes - are fine as is, except as follows.
-
Calls to outer module functions
From other module-level code, they are fine as is.
Called from class methods: change "someFunc" to "Rubyname.someFunc".
class Rubyname_myclass ... @instVar = Rubyname.computeFunc this, that
- Nested functions in functions. Move the nested function out to the module level. Add "self." or "Rubyname." to the name. Within the formerly enclosing function, change calls to "someFunc" with calls to "Rubyname.someFunc".
You have now protected your functions from inadvertent collisions. Your class names are not protected, even though your classes are defined in the module. You can achieve a good level of protection by changing the name from "Myclass" to "Rubyname_myclass". (See MSP_Greg's contribution, below, for a better, if not quite so simple, way to do this.)
By the way, your "xxx.rb" file provides a namespace wrapper for its variables. Next time you see Matz, ask him why its just for variables.
-
Great initiative Martin. "wrapping in a module" is a concept that took me a while to understand. And I do it, and I understand why its important with Ruby. But it took a bit of struggle to get my scripts to work. So thanks for starting this write-up.
On #1 - the last line with
end # rubyname.rb
is not necessary. You can't end a "ruby file" with an end statement. It will throw an error. The last "end" should be ending the first defined class/method/module, whichever was first. So in the example given, it should look like this:# rubyname.rb - purpose, author, etc. require 'sketchup' module Rubyname def Rubyname.my_method ... Ruby code end # Rubyname.my_method end # module Rubyname
#2 - I'm not sure about the syntax, perhaps Ruby accepts it. It looks odd, maybe it JS or C++ style, but normally you don't define methods like you have there. Try this:
def self.thisFunc( args ) ... end def Rubyname.thatFunc( args ) ... end
I suppose its not that different, but I have read that the standard ruby syntax is that brackets don't get used over multiple lines of code (I know it works - just int he book I read it said its not standard to do it that way in ruby). Also, I am wondering about the "do" statement in your method definition line. I've never seent hat before. It works?
Hope my comments make sense? This is an area I've wanted to do a tutorial on, I'm still waiting for the SCF to get their tutorial section fixed...
Chris
-
@chris fullmer said:
end # rubyname.rb
It will throw an error.
...def self.thisFunc( args ) > ... > end > def Rubyname.thatFunc( args ) > ... > end
Thanks for the corrections. Original post now fixed. Remind me to never post code that hasn't actually been loaded.
-
Using "self" has the advantage that if you ever decide to change the module name, you only need to change it in one place. Of course your editor should be able to handle this easily with search-and-replace.
-
Speaking of classes, one can sort of hide them by declaring them private and adding a module method to return an instance.
I haven't written any Ruby code for while, so forgive the naming; I code in too many languages. Brief example of private both in a module and a class...
module Is_Visible # method to return an instance of Test def self.get_Test Test.new end private class Test def show ; puts "Test.show" ; end def self.self_show ; puts "Test.self_show" ; end def pub_show ; pri_show ; end private def pri_show ; puts "Test.pri_show" ; end end end # Can't create object since private in module begin t1 = Test.new t1.show rescue puts "Can't create Test" end # Create object and call instance method Is_Visible.get_Test.show # Call class method Is_Visible.get_Test.class.self_show # Can't call pri_show since private in class begin Is_Visible.get_Test.pri_show rescue puts "Can't call pri_show" end # Can call via public method Is_Visible.get_Test.pub_show
FWIW,
Greg
-
@msp_greg said:
Speaking of classes, one can sort of hide them by declaring them private and adding a module method to return an instance.
Very nice! Pointer to this edited into original.
-
@martinrinehart said:
Your class names are not protected, even though your classes are defined in the module.
Absolutely and totally WRONG ! A module namespace protects evrything inside it. It can have it's OWN constant of the same name as another constant in a higher namespace. The same for variables and method names. YOUR module can have a class Matrix, and MINE can have a class Matrix, and they are different.
@martinrinehart said:
By the way, your "xxx.rb" file provides a namespace wrapper for its variables. Next time you see Matz, ask him why its just for variables.
Clarification: Only local_variables NOT instance_variables (@var), class_varaibles (@@avr) or Constants ( MSG, Value etc.)
Running unwrapped code corrupts the ObjectSpace, with:
Constants will be left behind in Object and become globally available.
@vars will be left behind in Object and even when set nil, GC does not clean them up.
@@vars will be left behind in Object and even when set nil, GC does not clean them up, AND worse... every class inherits them.
Advertisement