[info] Using Ruby Modules
-
[info] Using Ruby Modules
This is an informational thread.
Please do not use this thread to discuss issues relating to Using Ruby Modules,
instead please use the talk thread: [talk] Using Ruby Modules
@unknownuser said:
(http://forums.sketchucation.com/viewtopic.php?f)":21b4de32] I am having lots of trouble with modules though, if I ask the ruby console for
Voyeur::FaceMaker::instance_methods
it gives me back a list of the methods as I'd expect, but if I sayVoyeur::FaceMaker.makeAnalysisFaces
it throws a#<NoMethodError....
any idea how to fix that?OK...
(1) instance methods are really meant for Classes, where an instance (a child copy,) of a class inherits the method, that is always evaluated within the context of the instance copy, and not the Class itself. This is the opposite of a Class method.
(2) "But why.." you ask "then are instance methods allowed to be declared within modules?"
The answer is that modules can be used in a special purpose library way, called a "Mixin Module". Where you can encapsulate a set of instance methods in the "Mixin", and then mix it into a normal module, a class defintion, and even a specific instance object; and they will then have those methods.
However what "kind" of method that the resultant method becomes in the "mixee" object, depends on:- what kind of method it was in the Mixin Library module1. whether the "mixee" is a Module or Class or Instance1. and whether the "mixing" is done by include or extend.It IS confusing and I have trouble keeping track, myself. I started to make up a table showing the various combinations, as a quick reference, and using a test mixin module, but have not yet completed it.
(3) Your not doing a mixin module, so you don't want to worry about that right now. What you want, when you wish to call methods from outside the module, is called a "module method" or a "module function". Where the name of the specfic module (with or without namespace qualification,) is used as the "receiver" in the call, as your example shows:
Voyeur::FaceMaker.makeAnalysisFacesThere are 3 ways, within the module definition to explicitly, define a method as a module method:
- def Facemaker.makeAnalysisFaces* def self.makeAnalysisFaces* use module_function(:makeAnalysisFaces) after the method definition. The arg list can list more than one methodname. Or you can use it at the top (without an argument,) before multiple method def blocks and all of them are effected until you declare public or private again. However, that feature actually creates two copies of each method within the module, which doubles it's memory cost.
But the easiest way, (the implicit form,) if you have many methods that need to be module methods, is to wrap ALL of them within a class << self block.
Example:
module Voyeur module FaceMaker if( not file_loaded?("makeTheFaces.rb") ) # menu setup file_loaded("makeTheFaces.rb") # <<-- NEED end class << self private def methOne return "This is from methOne " end def methTwo return "and from methTwo." end public def methExternal return methOne << methTwo end # etc. end # self end # Facemaker end # Voyeur
EDIT
@unknownuser said:
As a sidenote, it can be done as is (I think,) in a convoluted way, via:
Voyeur::FaceMaker.method('makeAnalysisFaces').call
but of course that's silly, and defeats the purpose of modular programmimg and elegance.
Nope.. not right. Cannot be done that way. Instance methods are just only meant for Mixin Modules.So my advice is:
(a) In normal modules: wrap ALL methods in a class << self block.
(b) use the methods public and private to mark which ones are externally called and only internally called (respectively.)
(c) Ruby likes to have methods defined before they get called, so probably those that are private will be at the top of the block.
[Example above shows this...]NOW.. most important to coders migrating to using modules: intenal private methods can now be called from other internal methods without qualificaton (ie: methodname instead of self.methodname )
Please do not use this thread to discuss issues relating to Using Ruby Modules,
instead please use the talk thread: [talk] Using Ruby Modules -
@chris fullmer said:
@Dan - the double wrapping thing sounds excessive,...
It's not really excessive... didn't Google wrap (most) all their classes inside the Sketchup namespace ?? (and a few others inside the Geom and UI namespaces.)
Think of these analogies...
(1) When you receive your paycheck.. do you go to the bank and have the teller randomly distribute the funds into other people's accounts? Of course not. Because the whole purpose of accounts (ie, accounting,) is to identify and separate whose funds belong to whom. Imagine the chaos if everyone in your town had to share the same bank account.
(2) You own valuable personal posessions (papers, jewelery, antiques etc.) Do you store these personal items in the homes of strangers or your own home? Of course you store them in your own home. Your "homespace" is a separation between those things that belong to you, and the world of things that belong to others or the public.Restating what I've said earlier, and in other threads, YOUR toplevel namespace:
(a) Prevents you from trashing the ObjectSpace (which will trash the rest of the world's classes and modules.)
(b) Protects their code from your code.
(c) Protects your code from other people's code (if they don't trash the ObjectSpace.)
(d) Allows you to not worry about whether someone else has used the same Identifier for a plugin or tool. Example: a "Cylinder" plugin (there are several of these all named 'cylinder.rb' floating around.) But yours would be (examplizing,) FullmerCode::Cylinder and say TIG's would be TIG::Tool::Cylinder they would not clash. They are different modules or classes. It's simply jurisdictional addressing. (Like how [in a perfect world,] the mail addressed to you gets delivered to your house instead of TIG's.)@chris fullmer said:
...unless it is true that you say it makes it so SU can unload tools or modules from memmory when they are not being used? If that is true, then I think I'll jump on your double wrapping bandwagon.
Well.. I prefer not to "talk out my rear-end" so I wouldn't purposely mislead you or anyone else. Butt (pun intended,) your question made made double check, and I sort of mis-read or mis-assumed more into what the book said. I'll explain more below, however it does not negate the good reasons listed above for using a personal toplevel namespace, which are the reasons that they were invented to begin with. (read on..)
@chris fullmer said:
I have only done a limited amount of reading on Ruby and never seen that mentioned, but I have not read very much in depth info like that.
Let me endeavour to provide some citations.. here:
From the "Pick-Axe book" (aka "Programming Ruby: The Pragmatic Programmer's Guide" by Dave Thomas)http://phrogz.net/ProgrammingRuby/ref_c_module.html#Module.remove_const@unknownuser said:
class Module
private methods
*%(#000000)[remove_const( aSymbol ) -> anObject]*
"Removes the definition of the given constant, returning that constant's value. Predefined classes and singleton objects (such as true) cannot be removed."
Remember that module and class identifiers are actually Constants that reference Ruby objects. A custom written class or module is actually an instance of class Class and class Module, respectively. The call to the new method that creates these instances is really done by the interpreter (transparently,) when it parses a class or module definition.
When a Ruby object becomes unreferenced, it can then be garbage collected via GC.start, and this includes instances of class Class and class Module. However it would be a smart thing to first check that all instances of a class are unreferenced (GC'd) before removing their class definition.Drawing your attention to the 2nd sentence, this is where I made the wrong assuption.
I assumed the reason was that remove_const is a private method of class Module, and that Object, being the superclass of all classes, would not have that method. But it does. It gets it from module Kernel, which is a module, and is 'mixed-in' to class Object. So.. Object inherits a private remove_const method.
Base classes are not supposed to be removable, because the Ruby C source for the remove_const method is "supposedly" hardcoded not to. But there is (IMHO,) a bug in 1.8.6 that still removes the Constant pointer at the Toplevel. If I run:
Object.instance_eval("remove_const(:Numeric)")
I can no longer refer to the class by 'Numeric', but it's not gone. ie:
Float.superclass.name
>> NumericNow.. this is a Ruby bug. And it may be fixed in the 1.9.x branch, but I'll need to do some checking. Just because it does not yet work the way they intended, doesn't mean it won't when they fix it. (Oh that was brillant!)
Think of this... if you wish to manage the memory use of your plugins, where are you going to do it? Wouldn't the best place for it be within your Toplevel module (namespace) AND therefore, in the outer scope of the plugin submodule that you will remove? Since remove_const is a private method, it's best called from within that Toplevel module, although as I showed above, it might be done from outside using the instance_eval method. However I personally remove that method (as well as eval and module_eval,) from any of my modules that I don't want "outsiders" messing with when I can't freeze it for other reasons.
In another thread post, I mentioned a Toilet module with a flush method.
module Toilet def flush(arg) if arg.class==(Symbol) || arg.class.kind_of?(String) arg=arg.to_s if Object.eval("defined?(;;Toilet;;#{arg})=='module'") remove_const(arg) else $stderr.write("Warning; ;;Toilet;;#{arg} was undefined or not a module.") end else raise(TypeError,"String or Symbol argument required.") end end end
So any submodule name could be passed to the Toplevel module's flush method, not just submodule :Crap. Your choice is to either have your Toplevel module be the manager of your plugins, or create a manager submodule that does it.
Please do not use this thread to discuss issues relating to Using Ruby Modules,
instead please use the talk thread: [url=http://forums.sketchucation.com/viewtopic.php?f=180&t=29730:1re2ivef][talk] Using Ruby Modules[/url:1re2ivef] -
Thanks Dam for this interesting topic. I was rewriting all my plugin's code when I saw this thread.
So the only thing I need to do to follow you is adding a "TOP_LEVEL" namespace to my modules.But I have a question about adding methods to Sketchup classes :
I want to add a method to "Geom::Point3d", how can I use a special name ??Here my current code :
class Geom;;Point3d def myFirstMethod return(self.z*self.z) end end
-
@morgan74 said:
But I have a question about adding methods to Sketchup classes :
I want to add a method to "Geom::Point3d", how can I use a special name ??Here my current code :
> class Geom;;Point3d > def myFirstMethod > return(self.z*self.z) > end > end >
It's impossible to be 100% sure no one else implements a method of the same name as your when you extend a shared module/class. But if you for instance prefix with your initials you decrease the likely hood that your method is overwritten.
Personally I don't extend the base Ruby and SketchUp classes because of the potential of name conflict. (Doesn't look as good - but it's safe.)
-
Oops I have used the wrong thread
Shame on me
-
Condensed, Simplified and Reposted from another topic thread.
Modules are important and very necessary.
If you (speaking generally to all readers and especially newbies,) do not understand the importance of modules, then you do not understand how Ruby operates and how code is (and should be,) loaded.
Modules separate your code from other people's code, and prevent your code from "crapping" on the ObjectSpace (which is the same as "crapping" on everyone else's modules and classes, including Google's and Ruby's.) ANY script that defines methods, instance variables, class variables or constants in the ObjectSpace IS "crap." It does not matter if Google did it in their examples, it is still "crap."
Not only should you be using A module around your script... you should be using nested modules. The outermost is your TOP_LEVEL namespace.
Any tool or plugin you create should be in a nested module (submodule,) of your TOP_LEVEL namespace. This also allows individual tools or plugins to be removed from memory when they are no longer being used.
(It also means Google incorrectly put their modules at the toplevel, when they should have been within module Google. What happens in the future when Layout gets an API, and/or Picasa gets a Ruby API that integrates to Sketchup and Layout? But that could be a whole other discussion topic.)Concerning indenting... anticpating a chorus of whiney voices, thus:
"Oh man, nested modules mean I have to waste so much space on the left of my code, and push the "meat" of my rubies way to the right! And then I have to hit that TAB key all the time on every line... boohoo!"
No you don't! Ruby allows multiple scope defintions !
If I wish to define a class within a submodule of my toplevel module, I can do so and only have 1 indent:
# The Outer namespace modules must exist before referencing them. require('myfolder/MyTopLevel.rb') require('myfolder/myplugin/MyPlugin.rb') class MyTopLevel;;MyPlugin;;ThisClass def initialize(*args) # INIT CODE GOES HERE end # def end # class MyTopLevel;;MyPlugin;;ThisClass
I can also save an indent when defining a plugin module by doing:
# The Outer namespace module must exist before referencing it. require('myfolder/MyTopLevel.rb') module MyTopLevel;;MyPluginTwo class<<self private def method_one # .. code .. end public def method_two # .. code .. end # .. etc .. end # self end # module MyTopLevel;;MyPluginTwo
EDIT: Ruby v1.8.0 does not allow the TopLevel scope operator prefix, ie:
class **::**MyTopLevel::MyPlugin::ThisClass
%(#4000BF)[v1.8.6 does allow it. It basically tells Ruby to start looking at the TopLevel for an indentifier, instead of backing up thru each nesting level from the current level.
(I've removed the use of it in these examples, as many users will be running v1.8.0 on Win32, and not know how to update their Sketchup Ruby.)]instead of doing this:
module MyTopLevel module MyPluginTwo class<<self private def method_one # .. code .. end public def method_two # .. code .. end # .. etc .. end # self end # module MyPluginTwo end # module MyTopLevel
So choose a TopLevel namespace (module name,) that is unique. Use part of your surname. Or if your plugins will be distributed under a brand or company name, then that would be a good toplevel module name. Or you could use your SCF screen name, as long as it's unique.
Just don't use a trademarked brandname(s) if your not the owner of the trademark!!
Please do not use this thread to discuss issues relating to Using Ruby Modules,
instead please use the talk thread: [talk] Using Ruby Modules
Advertisement