3 hours ago(This post was last modified: 3 hours ago by kyonides.)
Ruby's Peculiarities
by Kyonides
This thread is all about some strange or unusual or certain features specific to the Ruby programming language. Some are object-oriented or functional characteristics while other are just internal specifications you should know about. This is NOT intended to go too deep into any of the topics discussed here. There's another thread that focuses on analysis of Ruby and RGSS here on the board.
NOTE: Parent class and Super class are synonyms here.
Index
It has 5 Types of Variables
CONSTANTS are NOT as Static as their Counterparts in other Languages
Classes and Modules are ALWAYS OPEN
You only declare a class's parent class ONCE.
The Very Existence of the Eigenclass or Metaclass in Ruby
Modules have other ways to define NEW methods without explicitly specifying they belong to that module
Ruby's Boolean Values
1. It has 5 Types of Variables
Content Hidden
Languages like C and C++ only offer 2, namely the local and the global variables.
Ruby has 3 additional types: class, instance and block variables.
From Ruby you get: $global, @@class, @instance, local and block.
Yes, the last 2 variables have no special symbol attached to them. The context determines which type of variable it is. Block variables can only live inside loops and iterators.
2. CONSTANTS are NOT as Static as their Counterparts in other Languages
Content Hidden
In most languages, constants are always written in UPPERCASE. Sometimes their names might include some _underscore_ symbols as well.
In C or C++, you are NEVER supposed to alter a constant during execution.
On the contrary, Ruby does allow you do change them. This is specially true for Arrays [] and Hashes {}. Yet, you might want to use @instance variables instead. This would force you to create specific getter and setter methods in order to let you access or modify their values.
Then why would anybody let you alter a CONSTANT in the first place?
Well, the answer to this question might be found in our next point.
3. Classes and Modules are ALWAYS OPEN
Content Hidden
Both classes and modules need a name, and it's a Constant by nature.
Did you notice that I didn't write it in ALL CAPS?
That's because you're NOT supposed to. Only normal CONSTANTS need to follow that nomenclature. Class or Module names follow the same convention used in C++ for classes and namespaces, respectively.
Contrary to what happens to the other CONSTANTS' behavior, these Constants are not altered directly. You can only alter its contents, including its eigenclass or metaclass.
Just like it happens in other languages, you need to declare a class or module first.
Code:
class MyClass
end
module MyModule
def self.my_method
print "My method"
end
end
Call MyModule.my_method and a popup window will display this message: "My method"
Once they've been declared, they'll keep existing until the program finishes its execution.
Classes in Ruby can ONLY inherit methods declared in 1 parent class at a time. This limitation doesn't prevent the parent class to have its own superclass (a synonym) and this dynamic can repeat itself indefinitely. At the end, a class can inherit tons of methods from multiple classes as long as they all belong to the same family of classes.
This means a given class would only follow 1 single path up to the most basic class Object in Ruby 1.8 and 1.9 or BasicObject in Ruby 2.x and 3.x and that's it.
In C++ you can declare many friend classes simultaneously while Ruby cannot.
How might this affect the way Constants work in Ruby as I've mentioned in the previous point?
Well, these Constants cannot be closed. Not even once. You might finish defining a list of methods or singleton methods by placing the very last end keyword at the end of that script, but the class isn't closed internally. You can only close the definition stage, not the class itself.
You can easily confirm this. Just reopen that class or module in a separate script section and it will still work as long as you have not committed any errors.
That means I can now extend MyClass this way:
Code:
class MyClass
def first_method
print "First"
end
end
I can either paste it right below my previous declaration or on another script section, and nothing weird will ever happen.
What this proves is that Ruby classes and modules can be extended INDEFINITELY. (Or until you get out of RAM...)
And here comes another revelation that many old scripters loved to ignore:
4. You only declare a class's parent class ONCE
Content Hidden
It doesn't mean that you can't declare it 1000 times if that's what you really want to do. The problem is you have NO NEED to do that, so why bother?
Code:
class MyChildClass < MyClass
def second_method
print "Second"
end
end
So far I have only declared 1 method MyChildClass, but don't forget you can also call MyClass#first_method as well. Why? Because it has been inherited from MyClass.
[ MyClass#first_method is not proper syntax, it's just a way Ruby programmers indicate where you can actually find that method. You're supposed to replace MyClass# with nothing if called from a class's internal method or with an actual object like @my_class.first_method ]
Still, what happens if later on I wanna add a third method? Especially on a different script...
Well, I can do this without facing any consequences:
Code:
class MyChildClass
def third_method
print "Third"
end
end
So why was I allowed to keep defining new methods if I NEVER redeclared the parent class?
The answer is very simple: the class is still open and it DOES REMEMBER its superclass!
In Ruby 1.8 and 1.9 you're even allowed to change an existing class's parent class by redeclaring that superclass. Nonetheless, stop doing that! In later releases of the Ruby language it's considered a BUG and you're NOT ALLOWED to redeclare a parent class at all. Your session would crash immediately.
Of course, I know some people wouldn't care about changes applied to Ruby in later releases of the language, and I get it. The problem is that your code is supposed to follow a logic, an order. Changing a class's parent class definitely breaks that. Plus, it makes it way harder for other scripters to foresee such a change because they're used to the way the default scripts have been defined by the RPG Maker developers. If a scripter expects to access methods defined in the original parent class, the game will crash during test play.
Instead of changing the declaration of a class's superclass, just go create a new class with its own parent class, and case closed.
5. The Very Existence of the Eigenclass or Metaclass in Ruby
Content Hidden
Because of the ability to declare singleton methods, methods that can only be accessed via the class or module itself instead of an instance (a copy) of a class, Ruby has the need to store all those class- or module-specific methods somewhere else. That other place is the eigenclass or metaclass.
By the way, eigen means "own" or "proper" in German, but in this particular case proper is the term we're looking for here. The eigenclass is the real class behind the actual class, if that makes any sense to you. :aswt:
How do you access such an internal class?
Actually you might have already seen it in 2 different contexts: some scripter was altering a module or the Bitmap class.
You can rarely watch someone else altering some other class this way:
Code:
class << Bitmap
end
module << Graphics
def width
640
end
end
What has changed after accessing the eigenclass this way?
The changes are reduced to 1 simple concept: you can now define any methods or even Module class's methods like attr_accessor the same way you'd do it in any normal class. Yes, you can easily alias methods as well.
Except for the use of this << operator, everything else would look and behave as business as usual.
6. Modules have other ways to define NEW methods without explicitly specifying they belong to that module
Content Hidden
This alternate method defining processes might not work with previously defined methods that included the self keyword.
Let's assume the default scripts included this module (or just add it to your script list):
Code:
module OldModule
def self.method
print "method"
end
end
And later on we try to redefine it or alias it this way:
Code:
module OldModule
extend self
def method
print "method revisited"
end
end
Normally, we would use extend to extend a class or another module, but Ruby also allows us to extend the same module, too. Classes are not supposed to extend themselves.
Or let's use any of these 2 other ways:
Code:
module OldModule
module_function
def method
print "method revisited"
end
end
Code:
module OldModule
def method
print "method revisited"
end
module_function :method
end
There's a big chance that those new definitions might not work as expected. :S They can even be ignored by the language interpreter.
If you want to redefine them, you better use the self.method definition at all costs. :asad: Or go straight to the eigenclass and redefine it there or simply go alias that method.
7. Ruby's Boolean Values
Content Hidden
If you really ever wanted to find something terribly weird in a programming language, then the way Ruby implemented the boolean values might fit the bill.
In Ruby there are not 2 but 3 boolean values. The typical values are true or false. Yet, Ruby added nil to this list. It treats it as a second false value. Most of the time at least.
Nil or nihil means nothing in Latin. Ruby only calls it nil, and it has the same basic usage as a null in C or C++ except for a little detail I'll discuss later on.
When is nil treated as an actual nil or non existing value?
That happens ONLY when you test a value against nil itself!
Code:
@variable == nil
If it returns true, it means it was really equal to nil in the first place. If you have never defined it before, then its current value is equal to nil. (It simply creates it then, and quickly sets its value to nil.)
nil is quite weird
If you define a variable the following way using the || or-operator:
[code@var = nil || false[/code]
It won't return a nil but a false value!
Defining a variable with an ||= if-not-yet-defined-set-value operator:
Code:
@var ||= nil
It becomes a redundant piece of code because that's exactly what Ruby would have done in the first place.
In languages like C and C++, you can use numeric values like 1 and 0 as replacements for true and false respectively. That's not possible in Ruby.
Ruby treats ALL NUMBERS (and objects except for false and nil) as true if you test them directly or via calling a variable.
Code:
if 0
print "Zero is treated as a truthy value."
else
print "You'll never read this line ever."
end
Nevertheless, that won't be case if you explicitly test it against true:
Code:
if 0 == true
print "Zero is treated as a truthy value... but not here!"
else
print "You'll read this line in no time. Guaranteed!"
end
So numbers are treated and NOT treated as truthy values depending on the current context.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.