|
|||
Previous < |
Contents ^
|
Next >
|
Class
, which contains all the object things plus a
list of methods and a reference to a superclass (which is itself
another class). All method calls in Ruby nominate a receiver (which is
by default self
, the current object).
Ruby finds the method to
invoke by looking at the list of methods in the receiver's class. If
it doesn't find the method there, it looks in the superclass, and then
in the superclass's
superclass, and so on. If the method cannot be found in the receiver's
class or any of its ancestors, Ruby invokes the method
method_missing
on the original receiver.
And that's it---the entire explanation. On to the next chapter.
``But wait,'' you cry, ``I spent good money on this chapter. What
about all this other stuff---singleton classes, class methods, and so
on. How do they work?''
Good question.
lucille
, the object's class,
Guitar
, and that class's superclass, Object
. Notice how the
object's class reference (called klass
for historical reasons
that really bug Andy) points to the class object, and how the
super
pointer from that class references the parent class.
Figure not available... |
Guitar.strings()
, it follows the same process
as before: it goes to the receiver, class Guitar
, follows the
klass
reference to class Guitar$'$
, and finds the method.
Finally, note that an ``S'' has crept into the flags in class
Guitar$'$
. The classes that Ruby creates automatically are
marked internally as singleton classes.
Singleton classes are
treated slightly differently within Ruby. The most obvious difference
from the outside is that they are effectively invisible: they will
never appear in a list of objects returned from methods such as
Module#ancestors
or
ObjectSpace::each_object
.
String
objects. We then associate
an anonymous class with one of them,
overriding one of the methods in
the object's base class and adding a new method.
a = "hello"
|
||
b = a.dup
|
||
|
||
class <<a
|
||
def to_s
|
||
"The value is '#{self}'"
|
||
end
|
||
def twoTimes
|
||
self + self
|
||
end
|
||
end
|
||
|
||
a.to_s
|
» |
"The value is 'hello'"
|
a.twoTimes
|
» |
"hellohello"
|
b.to_s
|
» |
"hello"
|
class <<
obj'' notation, which
basically says ``build me a new class just for object obj.'' We
could also have written it as:
a = "hello"
|
||
b = a.dup
|
||
def a.to_s
|
||
"The value is '#{self}'"
|
||
end
|
||
def a.twoTimes
|
||
self + self
|
||
end
|
||
|
||
a.to_s
|
» |
"The value is 'hello'"
|
a.twoTimes
|
» |
"hellohello"
|
b.to_s
|
» |
"hello"
|
a
''. This gives us a strong hint about the Ruby implementation:
a singleton class is created and inserted as a
's direct
class. a
's original class, String
, is made this singleton's
superclass. The before and after pictures are shown in Figure
19.3 on page 242.
Ruby performs a slight optimization with these singleton classes. If
an object's klass
reference already points to a singleton class,
a new one will not be created. This means that the first of the two
method definitions in the previous example will create a singleton
class, but the second will simply add a method to it.
Figure not available... |
module SillyModule
|
||
def hello
|
||
"Hello."
|
||
end
|
||
end
|
||
class SillyClass
|
||
include SillyModule
|
||
end
|
||
s = SillyClass.new
|
||
s.hello
|
» |
"Hello."
|
module SillyModule
|
||
def hello
|
||
"Hi, there!"
|
||
end
|
||
end
|
||
s.hello
|
» |
"Hi, there!"
|
Figure not available... |
class <<obj
'', you can mix a module into an
object using
Object#extend
. For example:
module Humor
|
||
def tickle
|
||
"hee, hee!"
|
||
end
|
||
end
|
||
|
||
a = "Grouchy"
|
||
a.extend Humor
|
||
a.tickle
|
» |
"hee, hee!"
|
extend
.
If you use it
within a class definition, the module's methods become class
methods.
module Humor
|
||
def tickle
|
||
"hee, hee!"
|
||
end
|
||
end
|
||
|
||
class Grouchy
|
||
include Humor
|
||
extend Humor
|
||
end
|
||
|
||
Grouchy.tickle
|
» |
"hee, hee!"
|
a = Grouchy.new
|
||
a.tickle
|
» |
"hee, hee!"
|
extend
is equivalent to
self.extend
, so the methods are added to self
, which in a
class definition is the class itself.
class MediaPlayer include Tracing if $DEBUGGING if ::EXPORT_VERSION def decrypt(stream) raise "Decryption not available" end else def decrypt(stream) # ... end end end |
self
must reference
something. Let's find out what it is.
class Test puts "Type of self = #{self.type}" puts "Name of self = #{self.name}" end |
Type of self = Class Name of self = Test |
class Test def Test.sayHello puts "Hello from #{name}" end sayHello end |
Hello from Test |
Test.sayHello
, and
then call it in the body of the class definition. Within
sayHello
, we call name
, an instance method of class
Module
. Because Module
is an ancestor of Class
, its instance
methods can be called without an explicit receiver within a class definition.
In fact, many of the directives that you use when defining a class
or module, things such as alias_method
, attr
, and
public
, are simply methods in class Module
. This opens up
some interesting possibilities---you can extend the functionality of
class and module definitions by writing Ruby code. Let's look at a
couple of examples.
As a first example, let's look at adding a basic documentation
facility to modules and classes.
This would allow us to associate a
string with modules and classes that we write, a string that is
accessible as the program is running. We'll choose a simple syntax.
class Example doc "This is a sample documentation string" # .. rest of class end |
doc
available to any module or class, so we
need to make it an instance method of class Module
.
class Module @@docs = Hash.new(nil) def doc(str) @@docs[self.name] = str end def Module::doc(aClass) # If we're passed a class or module, convert to string # ('<=' for classes checks for same class or subtype) aClass = aClass.name if aClass.type <= Module @@docs[aClass] || "No documentation for #{aClass}" end end class Example doc "This is a sample documentation string" # .. rest of class end module Another doc <<-edoc And this is a documentation string in a module edoc # rest of module end puts Module::doc(Example) puts Module::doc("Another") |
This is a sample documentation string And this is a documentation string in a module |
date
module
(described beginning on page 439). Say we
have a class that represents some underlying quantity (in this case, a
date). The class may have many attributes that present the same
underlying date in different ways: as a Julian day number, as a
string, as a [year, month, day] triple, and so on. Each value
represents the same date and may involve a fairly complex calculation
to derive. We therefore would like to calculate each attribute only
once, when it is first accessed.
The manual way would be to add a test to each accessor:
class ExampleDate def initialize(dayNumber) @dayNumber = dayNumber end def asDayNumber @dayNumber end def asString unless @string # complex calculation @string = result end @string end def asYMD unless @ymd # another calculation @ymd = [ y, m, d ] end @ymd end # ... end |
once
modifier for routines.
We'd like to be able to write something like:
class ExampleDate def asDayNumber @dayNumber end def asString # complex calculation end def asYMD # another calculation [ y, m, d ] end once :asString, :asYMD end |
once
as a directive by writing it as a class method of
ExampleDate
,
but what should it look like internally? The trick
is to have it rewrite the methods whose names it is passed. For each
method, it creates an alias for the original code, then creates a new
method with the same name. This new method does two things. First, it
invokes the original method (using the alias) and stores the resulting
value in an instance variable. Second, it redefines itself, so that on
subsequent calls it simply returns the value of the instance variable
directly. Here's Tadayoshi Funaba's code, slightly reformatted.
def ExampleDate.once(*ids) for id in ids module_eval <<-"end_eval" alias_method :__#{id.to_i}__, #{id.inspect} def #{id.id2name}(*args, &block) def self.#{id.id2name}(*args, &block) @__#{id.to_i}__ end @__#{id.to_i}__ = __#{id.to_i}__(*args, &block) end end_eval end end |
module_eval
to execute a block of code in the context of
the calling module (or, in this case, the calling class). The original
method is renamed __nnn__, where the nnn part is
the integer representation of the method name's symbol id. The code
uses the same name for the caching instance variable. The bulk
of the code is a method that dynamically redefines itself. Note that
this redefinition uses the fact that methods may contain nested
singleton method definitions, a clever trick.
Understand this code, and you'll be well on the way to true Ruby mastery.
However, we can take it further. Look in the date
module, and you'll
see method once
written slightly differently.
class Date class << self def once(*ids) # ... end end # ... end |
class << self
''. This defines a class based on the object
self
, and self
happens to be the class object for
Date
. The result? Every method within the inner class definition
is automatically a class method of Date
.
The once
feature is generally
applicable---it should work for any class. If you took once
and made it a private instance method of class Module
, it would be
available for use in any Ruby class.
Class
object itself. When you say
something such as String.new("gumby")
, you're sending the message
new
to the object that is class String
. But how does Ruby
know to do this? After all, the receiver of a message should be an
object reference, which implies that there must be a
constant called ``String'' somewhere containing a reference to the
String
object.[It will be a constant, not a variable,
because ``String'' starts with an uppercase letter.]
And in fact, that's exactly what happens. All the built-in classes,
along with the classes you define, have a corresponding global
constant with the same name as the class.
This is both straightforward and subtle. The subtlety comes from the
fact that there are actually two things named (for example) String
in the
system. There's a constant that references an object of class
String
, and there's the object itself.
The fact that class names are just constants means that you can treat
classes just like any other Ruby object: you can copy them, pass them
to methods, and use them in expressions.
def factory(klass, *args)
|
||
klass.new(*args)
|
||
end
|
||
|
||
factory(String, "Hello")
|
» |
"Hello"
|
factory(Dir, ".")
|
» |
#<Dir:0x401b51bc>
|
|
||
flag = true
|
||
(flag ? Array : Hash)[1, 2, 3, 4]
|
» |
[1, 2, 3, 4]
|
flag = false
|
||
(flag ? Array : Hash)[1, 2, 3, 4]
|
» |
{1=>2, 3=>4}
|
puts "Hello, World" |
"Hello, World"
generates a Ruby
String
, so there's one object. We also know that the bare method
call to puts
is effectively the same as self.puts
. But
what is ``self''?
self.type
|
» |
Object
|
Object
, we can
use all of Object
's methods (including those mixed-in from Kernel
)
in function form. This explains why we can call Kernel
methods
such as puts
at the top level (and indeed throughout Ruby):
these methods are part of every object.
class Base def aMethod puts "Got here" end private :aMethod end class Derived1 < Base public :aMethod end class Derived2 < Base end |
aMethod
in
instances of class Derived1
, but not via instances of Base
or
Derived2
.
So how does Ruby pull off this feat of having one method
with two different visibilities? Simply put, it cheats.
If a subclass changes the visibility of a method in a parent, Ruby
effectively inserts a hidden proxy method in the subclass that invokes
the original method using super
. It then sets the visibility
of that proxy to whatever you requested. This means that the code:
class Derived1 < Base public :aMethod end |
class Derived1 < Base def aMethod(*args) super end public :aMethod end |
super
can access the parent's method regardless of
its visibility, so the rewrite allows the subclass to override its
parent's visibility rules. Pretty scary, eh?
Object#freeze
. A frozen object may
not be modified: you can't change its instance variables (directly or
indirectly), you can't associate singleton methods with it, and, if it
is a class or module, you can't add, delete, or modify its
methods. Once frozen, an object stays frozen: there is no
Object#thaw
. You can test to see if an object is frozen using
Object#frozen?
.
What happens when you copy a frozen object? That depends on the method
you use. If you call an object's clone
method, the entire
object state (including whether it is frozen) is copied to the new
object. On the other hand, dup
typically copies only the
object's contents---the new copy will not inherit the frozen status.
str1 = "hello"
|
||
str1.freeze
|
» |
"hello"
|
str1.frozen?
|
» |
true
|
str2 = str1.clone
|
||
str2.frozen?
|
» |
true
|
str3 = str1.dup
|
||
str3.frozen?
|
» |
false
|
Previous < |
Contents ^
|
Next >
|