Module class method defining confusion
-
OK, I'm confused here. What is the right way to define modules and classes? Here is what I have:
module MyModule def MyModule;;mod_method1 end def MyModule;;mod_method2 end class MyModule;;MyClass def class_method1 end def class_method2 end end #MyClass end #MyModule
Is that correct? classes inside a module need to have the module:: in front of it to make sure it gets defined actually as part of the module? That seems to be what I'm seeing. Then methods defined loosely in the module need the same module:: in front of them to force them to be defined as inside the module. But methods inside a class do not need module::class:: or anything in front of them, they get defined inside the class automatically? That seems to be what I'm seeing, but the logic there is a bit fuzzy for my brain. Also, I have a few classes inside the module. Is there a way to share variables between those classes? I've got a @@data variable for example the gets defined in my
MyModule::Import
class, but I'd like to access it from myMyModule::Processing
. Is there a way to access it across classes? Or probably a better way to store the data so I can access it across classes? Thanks for anyone's time who patient enough to read through all that!Chris
-
Your example is incorrect, in several ways.
First, defining nested modules and nested classes.
There are two ways:module MyModule # constant, var and methods module NestedModule # constant, var and methods end # NestedModule class NestedClass # constant, var and methods end # NestedClass end # MyModule
.. or ..
module MyModule # constant, var and methods end # the following can be in THIS file, # or in separate file(s). # If in separate file(s) a require statement is # needed to be sure the first file is loaded. module MyModule;;NestedModule # constant, var and methods end module MyModule;;OtherNestedModule # constant, var and methods end class MyModule;;NestedClass # constant, var and methods end class MyModule;;OtherNestedClass # constant, var and methods end
-
METHODS
@chris fullmer said:
But methods inside a class do not need module::class:: or anything in front of them, they get defined inside the class automatically?
NO
To define a class method, the defintion is preceeded with the namespace qualification. You can retype the actual name, ie:
def MyClass.method_name_1
... but it is better practice to use the keyword self, as in:
def self.method_name_1
... that way you can easily change the class name at the top of the file, without having to edit every method definition.Within a class definition, AND within all class methods, the keyword self refers to the class itself, (except within any instance method.)
Class methods must be called using namespace qualification. Every class' constructor method (usually named new,) is a public class method, and must be called qualified with the class name, and if called from outside that class' defining namespace, it must be additional qualified with the module name that wraps it, such as:
Geom::Point3d.new(args)- If called from within the Geom module, then it can be called simply as Point3d.new() because Point3d is a local constant in that scope. (Remember that module and class identifiers are themselves constants.)
Instance methods are NOT defined with any qualification:
def instance_method_nameWithin any instance method (especially initialize,) the keyword self refers to the instance object that will be created at runtime by the constructor method.
@chris fullmer said:
Then methods defined loosely in the module need the same module:: in front of them to force them to be defined as inside the module.
NO
To define a module method, the defintion is preceeded with the namespace qualification. You can retype the actual name, ie:
def MyModule.method_name_1
... but it is better practice to use the keyword self, as in:
def self.method_name_1
... that way you can easily change the module name at the top of the file, without having to edit every method definition.Within a module definition, and all module methods, the keyword self refers to the module itself, (except within any instance method.)
Module methods must be called using namespace qualification. These are usual what you would consider to be library methods, for use by other modules and classes. (All the methods in the Sketchup module are like this. You must call them qualified with the name of the module.)
Because modules can be written as special "Mixin-Modules", you can also define instance methods within modules, so that when the "mixin" is added to a class using include(), those instance methods become available within the "mixee" class.
Just as in classes, the keyword self within an instance method in a mixin module, will (eventually,) refer to the instance object, created at runtime, by the constructor method, of the class that the mixin module in "mixed into" using include().An example is the standard Ruby module Math, as it is a combo Library module, and Mixin module. (There are 2 copies of each of it's methods, an instance copy, and a module copy.)
You can call it's module methods as library methods:
Math.cos(arg)
... or you can mix it into your class(es) using include(Math) and all it's instance method copies are added to your class as private instance methods, and all it's constants are added to your class as local constants. -
TBD (making things easier with modules using an anonymous class block.)
FYI: I explained this previously in my topic [info] Using Ruby Modules
-
Accessing shared data / vars between nested modules and/or nested classes.
If the data will not change, you can use a constant (just DON'T use the name DATA as it's a Ruby keyword.)
module MyModule VALUE = 1.234 class MyClass attr_accessor(;value) def initialize @value = MyModule;;VALUE end end # MyClass begin @@my_instance = MyClass.new() end end # MyModule
If the data will change use a module variable. Ruby 1.8.x does not contain a methods that autobuild class/module var getter and setter methods like it does for attributes (aka instance variables.)
But you CAN define them manually:
module MyModule @@value = 1.234 def self.value @@value end def self.value=(arg) @@value = arg end class MyClass attr_accessor(;value) def initialize @value = MyModule.value end end # MyClass begin @@my_instance = MyClass.new() end end # MyModule
A quirk is that, in the instance of MyClass, the object refereneced initially by @value will be the same object referenced by @@value in MyModule. But if the reference @@value is reassigned to point at a different object (via the setter method MyModule.value(object) or MyModule.class_variable_set(:@@value,object),) then the two references will afterward be pointing at different objects.
You would need to be sure the references are "in sync" if you do not wish this behaviour, usually by always calling the module's getter method for the @@var.
One way around this, is to wrap any shared data within an object reference that will not change, such as a Hash or Array object, that still allows it's submembers to change freely.
module MyModule @@data = {'value' => 1.234 } def self.value @@data['value'] end def self.value=(arg) @@data['value']= arg end class MyClass attr_accessor(;value) def initialize @value = MyModule.value end # override the standard getter # to be sure it's in sync; def value @value = MyModule.value end # override the standard setter # to be sure both are in sync; def value=( arg ) @value = MyModule.value= arg end end # MyClass begin @@my_instance = MyClass.new() end end # MyModule
Be very careful with some of the methods for Hash and Array objects that return a new Hash or Array object, for your shared data wrappers. (Try and use the "inplace" methods that have an exclamation point in the name, such as sort! instead of sort.)
-
Considering the following code, we have MyClass defined in MyModule, which can be accessed with MyModule::MyClass. You do not require the prefix, and note that class methods are differentiated by the 'self.' prefix.
module MyModule class MyClass def self.class_method return "I am a class method" end def instance_method return "I am an instance method" end end end
Accessing another classes variables is simply a matter of creating getter methods for them. Since you need access to a class variable, you'll want a class method like the following. Note that I've changed it to @@value instead of @@data for the reasons Dan gave.
class MyClass def self.value return @@value end end
With that being said, It begs the question as to why you require access to a variable across multiple classes because global variables are really bad mojo. One of the great things about namespaces is localization, as it allows you to isolate bugs to a specific region of your code or to reuse your code without having to untangle it from other portions of your application. It's interesting to note that you can actually write every conceivable program without modifying any variables at all. This style of programming is generally called functional programming and it avoids the side effects that can crop up when you're modifying a variable.
If you absolutely much have a global, then there are a few ways you can implement this. You can simply have a getter like the example above, or you can have a superclass which handles interaction with that global then have the classes which require access to that variable inherit from it. Note that if you want a class level variable which is isolated to a class and not accessible by it's subclasses/superclasses then you'd want a class instance variable. But once again I'd encourage you to avoid doing this at all costs, as you can likely avoid the global entirely and avoid a lot of headaches in the process.
-
Aha, I was confused about what a class variable vs. an instance variable was. I was really meaning to say instance variable. Now I see that there are two things here.
EDIT: Grr, I meant to say class methods vs instance methods. Sometimes I just can't get anything right
-
Your guys's code is very helpful here, thanks for posting it. I am examining it closely.
Also, that variable that I wanted access to across classes and modules is really an array of objects. I've got it working just having a method in the module that returns the array. That is working perfect for what I really need at this point.
-
@chris fullmer said:
Aha, I was confused about what a class variable vs. an instance variable was. I was really meaning to say instance variable. Now I see that there are two things here.
Yes.. and there are three (3) basic kinds of methods, a class method, a module method (aka singleton method,) and an instance method.
Calling methods, assume ChrisCode is your module identifier.
Class methods:
ChrisCode::MyClass.class_method( args )
Module (singleton) method:
ChrisCode.singleton_method( args )
- note that because Module is a class, it can (and does,) have class methods of it's own, such as:
Module.nesting()
.. but these are class methods, not module methods. You cannot call, for example:
ChrisCode.nesting()
# because ChrisCode is an instance of class Module, not actually THE class Module.
.. but you can call:
ChrisCode.class.nesting()
.. becauseChrisCode.class
would return the constant Module which points at the class defintion object, within which the class method nesting() is defined.
Instance method:
inst = ChrisCode::MyClass.new( args ) inst.instance_method( args )
- note that because Module is a class, it can (and does,) have class methods of it's own, such as:
-
That is great Dan, thanks so much. That is very helpful, and is finally all starting to soak in and make sense. I don't entirely understand where I would use some of this, but I'm sure at some point I'll come across good reasons for things like module methods and mix-ins. I'm just not there yet in my programming needs.
But a big big thank you for taking the time to write this out. Its very helpful,
Chris
Advertisement