1. When puts needs to convert an object to a string, it calls that object’s to_s method.
2. I nheritance allows you to create a class that is a refinement or specialization of another class. This class is called a subclass of the original, and the original is a superclass of the subclass. The child inherits all of the capabilities of its parent class—all the parent’s instance methods are available in instances of the child:
class Child < Parent end
3. The superclass method returns the parent of a particular class.
4. I f you don’t define an explicit superclass when defining a class, Ruby automatically makes the built-in class Object that class’s parent.
5. class BasicObject was introduced in Ruby 1.9 as the parent of Object . It is used in certain kinds of metaprogramming acting as a blank canvas. BasicObject is the root class of the hierarchy of classes.
6. to_s is actually defined in class Object .
7. R uby comes with a library called GServer that implements basic TCP server functionality. The GServer class handles all the mechanics of interfacing to TCP sockets. When you create a GServer object, you tell it the port to listen on. When a client connects, the GServer object calls its serve method to handle that connection.
8. When you invoke super , Ruby sends a message to the parent of the current object, asking it to invoke a method of the same name as the method invoking super . It passes this method the parameters that were passed to super .
9. A Ruby class has only one direct parent. However, Ruby classes can include the functionality of any number of mixins (a mixin is like a partial class definition).
10. Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits:
a) Modules provide a namespace and prevent name clashes.
b) Modules support the mixin facility.
11. Modules define a namespace, a sandbox in which your methods and constants can play without having to worry about being stepped on by other methods and constants.
12. Module constants are named just like class constants, with an initial uppercase letter. module methods are defined just like class methods. You call a module method by preceding its name with the module’s name and a period, and you reference a constant using the module name and two colons:
#trig.rb module Trig PI = 3.141592654 def Trig.sin(x) # .. end def Trig.cos(x) # .. end end #moral.rb module Moral VERY_BAD = 0 BAD = 1 def Moral.sin(badness) # ... end end #pinhead.rb require_relative 'trig' require_relative 'moral' y = Trig.sin(Trig::PI/4) wrongdoing = Moral.sin(Moral::VERY_BAD)
13. A module can’t have instances, because a module isn’t a class. However, you can include a module within a class definition. When this happens, all the module’s instance methods are suddenly available as methods in the class as well. They get mixed in. In fact, mixed-in modules effectively behave as superclasses:
module Debug def who_am_i? "#{self.class.name} (id: #{self.object_id}): #{self.name}" end end class Phonograph include Debug attr_reader :name def initialize(name) @name = name end # ... end class EightTrack include Debug attr_reader :name def initialize(name) @name = name end # ... end ph = Phonograph.new("West End Blues") et = EightTrack.new("Surrealistic Pillow") ph.who_am_i? # => "Phonograph (id: 2151894340): West End Blues" et.who_am_i? # => "EightTrack (id: 2151894300): Surrealistic Pillow"
14. A Ruby include does not simply copy the module’s instance methods into the class. Instead, it makes a reference from the class to the included module. If multiple classes include that module, they’ll all point to the same thing. If you change the definition of a method within a module, even while your program is running, all classes that include that module will exhibit the new behavior. Instance variables are always per object.
15. The Comparable mixin adds the comparison operators (< , <= , == , >= , and > ), as well as the method between? , to a class. For this to work, Comparable assumes that any class that uses it defines the operator <=> . So, as a class writer, you define one method, <=> ; include Comparable ; and get six comparison functions for free.
16. If you write an iterator called each , which returns the elements of your collection in turn.Mix in Enumerable , and suddenly your class supports things such as map , include? , and find_all? . If the objects in your collection implement meaningful ordering semantics using the <=> method, you’ll also get methods such as min, max, and sort.
17. Because inject is made available by Enumerable , we can use it in any class that includes the Enumerable module and defines the method each :
class VowelFinder include Enumerable def initialize(string) @string = string end def each @string.scan(/[aeiou]/) do |vowel| yield vowel end end end vf = VowelFinder.new("the quick brown fox jumped") vf.inject(:+) # => "euiooue"
18. The module you mix into your client class (the mixee? ) may create instance variables in the client object and may use attr_reader and friends to define accessors for these instance variables. For instance, the Observable module in the following example adds an instance variable @observer_list to any class that includes it:
module Observable def observers @observer_list = [] end def add_observer(obj) observers << obj end def notify_observers observers.each {|o| o.update } end end
19. A mixin’s instance variables can clash with those of the host class or with those of other mixins. For the most part, mixin modules don’t use instance variables directly—they use accessors to retrieve data from the client object. But if you need to create a mixin that has to have its own state, ensure that the instance variables have unique names to distinguish them from any other mixins in the system (perhaps by using the module’s name as part of the variable name). Alternatively, the module could use a module-level hash, indexed by the current object ID, to store instance-specific data without using Ruby instance variables:
module Test State = {} def state=(value) State[object_id] = value end def state State[object_id] end end class Client include Test end c1 = Client.new c2 = Client.new c1.state = 'cat' c2.state = 'dog' c1.state # => "cat" c2.state # => "dog"
A downside of this approach is that the data associated with a particular object will not get automatically deleted if the object is deleted. In general, a mixin that requires its own state is not a mixin—it should be written as a class.
20. Ruby looks first in the immediate class of an object, then in the mixins included into that class, and then in superclasses and their mixins. If a class has multiple modules mixed in, the last one included is searched first.
When you’re looking for subclassing relationships while designing your application, be on the lookout for the is-a relationships. We need to be using composition wherever we see a case of A uses a B, or A has a B. In this case mixin can help.