# Classes and objects in Ruby are rather straigthforward class Person # Class variables (also called static attributes) are prefixed by @@ @@person_counter=0 # object constructor def initialize(age, name, alive = true) # Default arg like in C++ @age, @name, @alive = age, name, alive # Object attributes are prefixed by '@' @@person_counter += 1 # There is no '++' operator in Ruby. The '++'/'--' operators are in fact # hidden assignments which affect variables, not objects. You cannot accomplish # assignment via method. Since everything in Ruby is object, '++' and '--' # contradict Ruby OO ideology. Instead '-=' and '+=' are used. end attr_accessor :name, :age # This creates setter and getter methods for @name # and @age. See 13.3 for detailes. # methods modifying the receiver object usually have the '!' suffix def die! @alive = false puts "#{@name} has died at the age of #{@age}." @alive end def kill(anotherPerson) print @name, ' is killing ', anotherPerson.name, ".\n" anotherPerson.die! end # methods used as queries # usually have the '?' suffix def alive? @alive && true end def year_of_birth Time.now.year - @age end # Class method (also called static method) def Person.number_of_people @@person_counter end end # Using the class: # Create objects of class Person lecter = Person.new(47, 'Hannibal') starling = Person.new(29, 'Clarice', true) pazzi = Person.new(40, 'Rinaldo', true) # Calling a class method print "There are ", Person.number_of_people, " Person objects\n" print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n" lecter.kill(pazzi) print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n" print starling.name , ' was born in ', starling.year_of_birth, "\n" |
# If you don't need any initialisation in the constructor, # you don't need to write a constructor. class MyClass end class MyClass def initialize @start = Time.new @age = 0 end end class MyClass def initialize(inithash) @start = Time.new @age = 0 for key, value in inithash instance_variable_set("@#{key}", value) end end end |
# Objects are destroyed by the garbage collector. # The time of destroying is not predictable. # The ruby garbage collector can handle circular references, # so there is no need to write destructor for that. # There is no direct support for destructor. # You can call a custom function, or more specific a proc object, when the # garbage collector is about to destruct the object, but it is unpredictable # when this occurs. # Also if such a finalizer object has a reference to the orignal object, # this may prevent the original object to get garbage collected. # Because of this problem the finalize method below is # a class method and not a instance method. # So if you need to free resources for an object, like # closing a socket or kill a spawned subprocess, # you should do it explicitly. class MyClass def initialize ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) end def MyClass.finalize(id) puts "Object #{id} dying at #{Time.new}" end end # test code 3.times { MyClass.new } ObjectSpace.garbage_collect |
# You can write getter and setter methods in a natural way: class Person def name @name end def name=(name) @name = name end end # But there is a better and shorter way class Person attr_reader :age attr_writer :name # attr_reader and attr_writer are actually methods in class Class # which set getter and setter methods for you. end # There is also attr_accessor to create both setters and getters class Person attr_accessor :age, :name end |
class Person # Class variables (also called static attributes) are prefixed by @@ @@person_counter = 0 def Person.population @@person_counter end def initialize @@person_counter += 1 ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) end def Person.finalize(id) @@person_counter -= 1 end end people = [] 10.times { people.push(Person.new) } printf("There are %d people alive", Person.population) FixedArray.class_max_bounds = 100 alpha = FixedArray.new puts "Bound on alpha is #{alpha.max_bounds}" beta = FixedArray.new beta.max_bounds = 50 # calls the instance method beta.class.class_max_bounds = 50 # alternative, calls the class method puts "Bound on alpha is #{alpha.max_bounds}" class FixedArray @@bounds = 7 def max_bounds @@max_bounds end # instance method, which sets the class variable def max_bounds=(value) @@max_bounds = value end # class method. This can only be called on a class, # but not on the instances def FixedArray.class_max_bounds=(value) @@max_bounds = value end end |
PersonStruct = Struct.new("Person", :name, :age, :peers) # creates a class "Person::Struct", which is accessiable with the # constant "PersonStruct" p = PersonStruct.new p = Struct::Person.new # alternative using the classname p.name = "Jason Smythe" p.age = 13 p.peers = ["Wilbur", "Ralph", "Fred"] p[:peers] = ["Wilbur", "Ralph", "Fred"] # alternative access using symbol p["peers"] = ["Wilbur", "Ralph", "Fred"] # alternative access using name of field p[2] = ["Wilbur", "Ralph", "Fred"] # alternative access using index of field puts "At age #{p.age}, #{p.name}'s first friend is #{p.peers[0]}" # The fields of a struct have no special type, like other ruby variables # you can put any objects in. Therefore the discussions how to specify # the types of the fields do not apply to ruby. FamilyStruct = Struct.new("Family", :head, :address, :members) folks = FamilyStruct.new folks.head = PersonStruct.new dad = folks.head dad.name = "John" dad.age = 34 # supply of own accessor method for the struct for error checking class PersonStruct def age=(value) if !value.kind_of?(Integer) raise(ArgumentError, "Age #{value} isn't an Integer") elsif value > 150 raise(ArgumentError, "Age #{value} is unreasonable") end @age = value end end |
# The ruby Object class defines a dup and a clone method. # The dup method is recommended for prototype object creation. # The default implementation makes a shallow copy, # but each class can override it, for example to make a deep copy. # If you want to call 'new' directly on the instances, # you can create a instance method "new", which returns a new duplicate. # This method is distinct from the class method new. # class A def new dup end end ob1 = A.new # later on ob2 = ob1.new |
methname = 'flicker' obj.send(methname, 10) # calls obj.flicker(10) # call three methods on the object, by name ['start', 'run', 'stop'].each do |method_string| obj.send(method_string) end # Another way is to create a Method object method_obj = obj.method('flicker') # And then call it method_obj.call(10) |
# All classes in Ruby inherit from class Object # and thus all objects share methods defined in this class # the class of the object puts any_object.type # Ruby classes are actually objects of class Class and they # respond to methods defined in Object class as well # the superclass of this class puts any_object.class.superclass # ask an object whether it is an instance of particular class n = 4.7 puts n.instance_of?(Float) # true puts n.instance_of?(Numeric) # false # ask an object whether it is an instance of class, one of the # superclasses of the object, or modules included in it puts n.kind_of?(Float) # true (the class) puts n.kind_of?(Numeric) # true (an ancestor class) puts n.kind_of?(Comparable) # true (a mixin module) puts n.kind_of?(String) # false # ask an object whether it can respond to a particular method puts n.respond_to?('+') # true puts n.respond_to?('length') # false # all methods an object can respond to 'just a string'.methods.each { |m| puts m } |
# Actually any class in Ruby is inheritable class Person attr_accessor :age, :name def initialize @name @age end end #----------------------------- dude = Person.new dude.name = 'Jason' dude.age = 23 printf "%s is age %d.\n", dude.name, dude.age #----------------------------- # Inheriting from Person class Employee < Person attr_accessor :salary end #----------------------------- empl = Employee.new empl.name = 'Jason' empl.age = 23 empl.salary = 200 printf "%s is age %d, the salary is %d.\n", empl.name, empl.age, empl.salary #----------------------------- # Any built-in class can be inherited the same way class WeirdString < String def initialize(obj) super obj end def +(anotherObj) # + method in this class is overridden # to return the sum of string lengths self.length + anotherObj.length # 'self' can be omitted end end #----------------------------- a = WeirdString.new('hello') b = WeirdString.new('bye') puts a + b # the overridden + #=> 8 puts a.length # method from the superclass, String #=> 5 |
# In ruby you can override the method_missing method # to have a solution similar to perls AUTOLOAD. class Person def initialize @ok_fields = %w(name age peers parent) end def valid_attribute?(name) @ok_fields.include?(name) end def method_missing(namesymbol, *params) name = namesymbol.to_s return if name =~ /^A-Z/ if name.to_s[-1] == ('='[0]) # we have a setter isSetter = true name.sub!(/=$/, '') end if valid_attribute?(name) if isSetter instance_variable_set("@#{name}", *params) else instance_variable_get("@#{name}", *params) end else # if no annestor is responsible, # the Object class will throw a NoMethodError exception super(namesymbol, *params) end end def new kid = Person.new kid.parent = self kid end end dad = Person.new dad.name = "Jason" dad.age = 23 kid = dad.new kid.name = "Rachel" kid.age = 2 puts "Kid's parent is #{kid.parent.name}" puts dad puts kid class Employee < Person def initialize super @ok_fields.push("salary", "boss") end def ok_fields @ok_fields end end |
# The ruby garbage collector pretends to cope with circular structures. # You can test it with this code: class RingNode attr_accessor :next attr_accessor :prev attr_reader :name def initialize(aName) @name = aName ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) end def RingNode.finalize(id) puts "Node #{id} dying" end def RingNode.show_all_objects ObjectSpace.each_object {|id| puts id.name if id.class == RingNode } end end def create_test a = RingNode.new("Node A") b = RingNode.new("Node B") c = RingNode.new("Node C") a.next = b b.next = c c.next = a a.prev = c c.prev = b b.prev = a a = nil b = nil c = nil end create_test RingNode.show_all_objects ObjectSpace.garbage_collect puts "After garbage collection" RingNode.show_all_objects |
class String def <=>(other) self.casecmp other end end # There is no way to directly overload the '""' (stringify) # operator in Ruby. However, by convention, classes which # can reasonably be converted to a String will define a # 'to_s' method as in the TimeNumber class defined below. # The 'puts' method will automatcally call an object's # 'to_s' method as is demonstrated below. # Furthermore, if a class defines a to_str method, an object of that # class can be used most any place where the interpreter is looking # for a String value. #--------------------------------------- # NOTE: Ruby has a builtin Time class which would usually be used # to manipulate time objects, the following is supplied for # educational purposes to demonstrate operator overloading. # class TimeNumber attr_accessor :hours,:minutes,:seconds def initialize( hours, minutes, seconds) @hours = hours @minutes = minutes @seconds = seconds end def to_s return sprintf( "%d:%02d:%02d", @hours, @minutes, @seconds) end def to_str to_s end def +( other) seconds = @seconds + other.seconds minutes = @minutes + other.minutes hours = @hours + other.hours if seconds >= 60 seconds %= 60 minutes += 1 end if minutes >= 60 minutes %= 60 hours += 1 end return TimeNumber.new(hours, minutes, seconds) end def -(other) raise NotImplementedError end def *(other) raise NotImplementedError end def /( other) raise NotImplementedError end end t1 = TimeNumber.new(0, 58, 59) sec = TimeNumber.new(0, 0, 1) min = TimeNumber.new(0, 1, 0) puts t1 + sec + min + min #----------------------------- # StrNum class example: Ruby's builtin String class already has the # capabilities outlined in StrNum Perl example, however the '*' operator # on Ruby's String class acts differently: It creates a string which # is the original string repeated N times. # # Using Ruby's String class as is in this example: x = "Red"; y = "Black" z = x+y r = z*3 # r is "RedBlackRedBlackRedBlack" puts "values are #{x}, #{y}, #{z}, and #{r}" print "#{x} is ", x < y ? "LT" : "GE", " #{y}\n" # prints: # values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack # Red is GE Black #----------------------------- class FixNum REGEX = /(\.\d*)/ DEFAULT_PLACES = 0 attr_accessor :value, :places def initialize(value, places = nil) @value = value if places @places = places else m = REGEX.match(value.to_s) if m @places = m[0].length - 1 else @places = DEFAULT_PLACES end end end def +(other) FixNum.new(@value + other.value, max(@places, other.places)) end def *(other) FixNum.new(@value * other.value, max(@places, other.places)) end def /(other) puts "Divide: #{@value.to_f/other.value.to_f}" result = FixNum.new(@value.to_f/other.value.to_f) result.places = max(result.places,other.places) result end def to_s sprintf("STR%s: %.*f", self.class.to_s , @places, @value) #. end def to_str to_s end def to_i #convert to int @value.to_i end def to_f #convert to float` @value.to_f end private def max(a,b) a > b ? a : b end end def demo() x = FixNum.new(40) y = FixNum.new(12, 0) puts "sum of #{x} and #{y} is #{x+y}" puts "product of #{x} and #{y} is #{x*y}" z = x/y puts "#{z} has #{z.places} places" unless z.places z.places = 2 end puts "div of #{x} by #{y} is #{z}" puts "square of that is #{z*z}" end if __FILE__ == $0 demo() end |