About Modules
-
Ruby Modules are something that you would normally not have to think about when first learning Ruby without SketchUp.
For the most part, the purpose of using modules is to minimize method name collision. Modules are used so that my "draw_rectangle" method does not over-write yours (for example.)
Method over-writing may be a point worth some study. In Ruby, it is perfectly legal to re-define a method. Let's say I have a plugin that defines:
def one return 1 end
and you come along and release a new plugin with this:
def one return 2 end
Now, the method "one" has been redefined for both plugins, and my plugin will likely fail. So we use modules to give the our methods a namespace (or scope):
module JF def one return 1 end end
and yours:
module CLF def one return 2 end end
Then, to call the methods you need to specify which "one" you want:
value = JF.one => 1 value = CLF.one => 2
I hope that helps.
-
Yes indeed, that totally helps. I'm sort of struggling to comprehend the difference between a class and a module at the moment. Are classes just a way of organizing groups of similar or related methods?
Chris
-
@chris fullmer said:
Are classes just a way of organizing groups of similar or related methods?
Classes and Modules are similar. Grouping methods is one use of both Modules and Classes, and you can include both data and methods in there. (That is the meaning of the @variables - they have a class-level scope.)
Classes are what you use when you want to create "things".
class Door def open; puts "opening.."; end end door = Door.new # create an INSTANCE of a Door door.open ==> "opening.."
Modules, on the other hand are not meant to be instantiated -- you can not call "new" on a module.
Use a Module to group together data and methods for re-use. One hint I read somewhere is to think of Module names as adjectives. For example, what do Doors and Jars have in common? They are both "Openable".
module Openable def open puts "opening.." end end class Door include Openable end class Jar include Openable end d = Door.new d.open ==> "opening.." j = Jar.new j.open ==> "opening.."
I don't think I've ever used Modules in this way for a SketchUp plugin. In fact, I can't remember needing to create any classes either. Arrays and Hashes are natural choices for manipulating SketchUp's entities. So my use of modules in SketchUp has been purely for namespace management.
-
Same here. I use modules to group my code.
I think I only used classes to make new Tools. Then you have to define a class.
And I made a class which based on the Hash module to make a JSON class, because I needed to convert a ruby Hash to a JSON string so I could send it to a Webdialog. I didn't want to modify the built-in classes to Ruby. -
Hi Jim, all,
thanks for bringing up this topic.
I might be wrong, because I'm just trying to learn Ruby and the SU Api, but I believe, it is always a good idea to encapsulate plugins into a class or a module. Else everyone potentionally stumps onto the feet of others.
Because, if you just use 'def some_method' you actually add methods to the class 'Object' beeing the parent of all other classes and thus it is easy to accidentially redefine/overwrite methods already in place.
Try the following in your ruby console, directly after starting SU (and maybe with all plugins removed, to be sure). Your input in black, output from Ruby in red:
%(black)[self.class] Object %(black)[def XXXXX; return "5 X'es"; end] nil %(black)[XXXXX] 5 X'es %(black)[self.XXXXX] 5 X'es
So, fine, we have our new method in place and it works. But now try this:
%(black)[Array.new.class] Array %(black)[Array.new.XXXXX] 5 X'es %(black)[Sketchup.active_model.XXXXX] 5 X'es %(black)[Sketchup.active_model.selection.XXXXX] 5 X'es
Huh! Were you aware of that? And was that actually your intention? Depending on the implementation of XXXXX it might even do strange things, when called on an instance of WhatEver (not in our sample, as that simply returns a string object). And that method name is unusual enough to not stump onto anybody's feet too easily.
But assume you have a need for, say, a method returning some identifier and you naturally name it 'id' ... first let's check a little bit, then define our method and recheck (your numbers will be different):
%(black)[id] 108448104 %(black)[self.id] 108448104 %(black)[Sketchup.active_model.id] 115396684 %(black)[def id; return 4711; end] nil %(black)[id] 4711 %(black)[self.id] 4711 %(black)[Sketchup.active_model.id] 4711
Nice, our method works ... but we stumped heavily onto everbodys feet. Try some other object ids you can come up with, all will give 4711. Most probably some loaded Ruby scripts are now broken too, e.g. when they test for equality of object instances with the method 'equal?'. So you better now terminate and restart SketchUp before continuing with serious work!!!
How stupid can one be to redefine/overwrite the 'id' method? OK, nobody, but if everybody just uses 'def' within the scope of the default Object, especially with several plugins already loaded, name me that man knowing all and every method name already in use in some plugin, so that I can ask him for permission to use my personal method name.
A better(?) approach would be to pre/postfix every method (still in Object) with your signature ... good, when you enjoy typing your name often, e.g.
def deerwood_id ...
.But, if you just have one to five unrelated methods to offer, then maybe a module is appropriate, e.g.
module Whatever # a module method (note the prefix) def Whatever.id return 4711 end end
and call it then as
Whatever.id
returning the desired 4711 without affecting others.But if your plugin is just a little bit more sophisticated and uses different helper methods to implement some complicated feature, then use a class. Instead of writing one several pages long method with many nested loops and if/else statements split the whole behaviour into several instance methods, that call each other.
To be continued ...
best regards, deerwood -
This thread is full of great information. Thanks to everyone who is contributing!
Chris
-
Great thread! Thank you!
Perhaps you could show some examples with variables (@, @@ or $). I'm not able to make these examples clearly -
An excellent and insightful post, deerwood. I'm looking for forward to part 2.
-
@deerwood said:
Hi Jim, all,
But, if you just have one to five unrelated methods to offer, then maybe a module is appropriate, e.g.> module Whatever > # a module method (note the prefix) > def Whatever.id > return 4711 > end > end >
and call it then as
Whatever.id
returning the desired 4711 without affecting others.But if your plugin is just a little bit more sophisticated and uses different helper methods to implement some complicated feature, then use a class. Instead of writing one several pages long method with many nested loops and if/else statements split the whole behaviour into several instance methods, that call each other.
Why create a class if you don't make any instances? They way I see it, classes are objects, so unless you use them as such, why not just use modules?
-
In case of Ruby you are right: classes are objects:
Class.is_a? Class #=> true Class.ancestors #=> [Class, Module, Object, Kernel] Class.object_id #=> ... class A; end A.is_a? Class #=> true A.ancestors #=> [A, Object, Kernel] A.object_id #=> ...
But for programming, classes are blueprints for building objects. You have to ask yourself, why you do not use objects and so do not make use of classes. It's a question that points on the programming paradigm you use.
azuby
-
It was just that what deerwood said sounded to me that, modules where to be used for a few methods, but classes if you had many. Maybejust me that read it odd.
I've seen often in scripts that classes has been used as purely encapsulation and never used as objects.
-
Sorry man - it was just the term "using classes as objects", this is meta-programming. In most of the scripts the programmers use classes to build objects.
Modules are kind of light classes, because you can not make objects of them on the direct way.
azuby
-
Dear thomthom, all,
@thomthom said:
It was just that what deerwood said sounded to me that, modules where to be used for a few methods, but classes if you had many. Maybejust me that read it odd.
I've seen often in scripts that classes has been used as purely encapsulation and never used as objects.
sorry for my late answer, I just had prepared a continuation of my earlier post ... and lost it completely in an editor (notepad++) crash !
Yes, you are right: if one invents a class with only class methods, it makes no real sense at all to instantiate that class, because the instantiated object(s) don't carry any state. The Java Math class is an example. It has a full load of useful class methods, e.g. Math::sin(), Math::cos() etc, all getting in the values to work on by providing them as parameters. But it has no state at all (and no instance methods). In Ruby one would normally use a module for that (independent of the number of modul/class methods, compare the Ruby Math module). But then, it doesn't harm to use a class. Just do not instantiate any objects from it and use it the same way you would use a module, just to have your own namespace.
Class/module methods do their work by accessing some commonly available information (e.g. Sketchup.active_model.selection) or some parameters or a combination of both.
Though there is more to tell about Ruby modules (later).
But the Ruby way of dealing with things is different. In Ruby almost everything is an object. Even simple things like integers are full blown objects. Try this in the Ruby console:
65 >> 65 65.next >> 66 65.chr >> A 65.next.next.chr >> C
You just invoked two easy to use methods on a simple integer number object. What is an object, then? Something having a state (a value or some values, individual to each object) and a behaviour (common to all objects of that type). Every integer object in Ruby has it's own state (that integer value) and a common behaviour (next always gives you a new integer object with it's value incremented by one). And chr spits out a 1 character long string object with the ASCII representation of the value.
Note, that both methods take no parameter at all ... they use the internal state of the integer object at hand.
It would be very inconvenient to have to define the common behaviour of something in all and every object of that sort. Thus OO languages (including Ruby) have classes to define the common code/behaviour for all things of that sort.
If the class is in place, you can later instantiate as many objects of them as you need. Every instance/object will have it's own state (using up memory) but share the same behaviour (not using up memory).
So, for now (remeber my loss of ready made explanations): if your plugin does not carry around some state (or you save them somehow externally in attributes) you might be fine with just using module/class methods.
Still to be continued ...
best regards, deerwood
Advertisement