Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Ruby—A Diamond of a Programming Language? : Page 6

Ruby is an object-oriented, meta-programming language that has many developers wondering if there are actually better alternatives to languages like Java and C#.

Method Arguments
Up to this point, it is assumed that the number of arguments passed to a method is known. While unthinkable in many other languages, Ruby allows you to pass in a variable number of arguments and capture them with a single parameter. To create an argument of a variable length, simply place an asterisk (*) in front of the last argument. In this way, you could write the definition of a polygon in Ruby.

class Polygon def initialize (s1, s2, s3, *others) @sideOne = s1 @sideTwo = s2 @sideThree = s3 @otherSides = others end end

As shown below, you can use this definition to create a triangle or a hexagon. In Part 2 of this series, you will learn more about arrays and how to work with the other "sides," or arguments passed in, of the Polygon instances.

irb(main):009:0> poly1=Polygon.new(2,4,5) => #<Polygon:0x594db10 @otherSides=[], @sideThree=5, @sideTwo=4, @sideOne=2> irb(main):010:0> poly2=Polygon.new(2,18,4,5,7,9) => #<Polygon:0x5948d58 @otherSides=[5, 7, 9], @sideThree=4, @sideTwo=18, @sideOne=2>

On the flipside of accepting variable length arguments, Ruby also lets you define a default value for a method argument when the caller does not provide one. For example, here's a better initializer for the Rectangle class.

def initialize (hgt = 1, wdth = 1) @height = hgt @width = wdth end

As seen here, the assignment operator next to the parameters listed in the definition serves as a default assigner if parameters are left off. Now, when creating a new Rectangle, if the width is left off in the call, an appropriate width is provided by default:

irb(main):090:0> rect=Rectangle.new(2) => #<Rectangle:0x5873f68 @width=1, @height=2>

Class Variables and Class Methods
Like most object-oriented languages, Ruby classes also allow for class variables and methods. A class variable allows a single variable to be shared amongst all instances of a class. In Ruby, a double at sign (@@) is used to signify class variables. For example, if you wanted all instances of a BankAccount class to share the same interest rate, then the class might be defined as follows:

class BankAccount @@interestRate = 6.5 def BankAccount.getInterestRate() @@interestRate end attr_accessor :balance def initialize (bal) @balance = bal end end

As you can see, class variables must be initialized before they are used, and like instance variables, you need to write accessor methods if you want to make class variables accessible. In this case, I have defined a class method to return the interest rate. Note the class name and period in front of getInterestRate denoting a class method. A class method works the same regardless of any instance—in this case, returning the common interest rate to all BankAccount instances. To call on class methods, you need to use the class name as it is also used in the class method definition:

irb(main):045:0> BankAccount.getInterestRate => 6.5

In fact, the "new" method used to create instances of classes is actually a class method. Thus, when you type Rectangle.new in your program, you are actually calling on the new class method which Ruby provides by default.

One of the tenets of object-oriented programming is support for a class hierarchy. As in the classification of things in nature, classes are allowed to inherit characteristics from more generic classes. Characteristics in object-oriented programming are embodied in methods and variables. For example, a Square class inherits from the Rectangle class some characteristics or methods and variables. A Square is a more specific type of Rectangle (an instance of Rectangle with equal sized height and width) but it still has a height and width as well as area (also computed in the same way as a Rectangle's by multiplying one side by the other side). In Ruby, the Square class could be created with the following definition:

class Square < Rectangle end

The < Rectangle signifies that Square is a subclass of Rectangle or, conversely, that Rectangle is the superclass of Square. By default, an instance of Square automatically has all the same attributes and methods that a Rectangle has to include height, width, and the area method. In order to insure the sides of Square instances are of equal length, you may want to override the existing initialize method of Square:

class Square < Rectangle def initialize (size) @height = size @width = size end end

Again, everything in Ruby is an object. Quite literally, this means that everything descends from the Object class. While it is not explicit in all class definitions (you won't see < Object in the definition), all classes descend from the Ruby base class Object. Knowing this fact makes this next point a little easier to understand.

When writing your application, you can define methods outside of a class definition. At the start of this article, you were shown a Celsius to Fahrenheit converter method that was not part of any class. As an additional example, here's a method outside of any class:

def feel? return "I feel fine." end

To execute this method, just enter the method name—no class or instance is needed:

irb(main):042:0> feel? => "I feel fine."

These methods look like functions or procedures in another language like C. In fact, while the methods look like they do not belong to any class, these methods are actually methods you have added to the Object class, which (because Object is "the" superclass of all classes) in turn also adds the method to your classes. Therefore, you can now call this method on any object (like instances of Squares and Rectangles) or even a class (like the Rectangle class).

irb(main):043:0> sq1=Square.new(4) => #<Square:0x5a18b50 @width=4, @height=4> irb(main):044:0> rect1=Rectangle.new(5,7) => #<Rectangle:0x5a139a8 @width=7, @height=5> irb(main):045:0> sq1.feel? => "I feel fine." irb(main):046:0> rect1.feel? => "I feel fine." irb(main):047:0> Rectangle.feel? => "I feel fine."

Method Access Control
There is a small catch to the top-level methods. They are considered "private" methods. In many cases, as you design your applications, you want methods to be used internally by an object, but not by other objects. Ruby provides three keywords to limit access to methods.

  • Private: Methods that can only be accessed by the object.
  • Protected: Methods that can be accessed by the object and by instances of the class and direct inheritance descendants.
  • Public: Methods can be accessed by any object (Public is the default setting for all methods).
The keywords are inserted in the code between methods. All methods defined from the keyword private are private until another access control keyword is put in the code. For example, in the code below, the accessors and area method are all public by default while the grow method is private. The doubleSize method is explicitly made public. The initialize method of a class is automatically private.

class Rectangle attr_accessor :height, :width def initialize (hgt, wdth) @height = hgt @width = wdth end def area () @height*@width end private #start making private methods def grow (heightMultiple, widthMultiple) @height = @height * heightMultiple @width = @width * widthMultiple return "New area:" + area().to_s end public #back to public methods again def doubleSize () grow(2,2) end end

As shown below, doubleSize can be executed on the object, but any calls to grow directly are rebuffed and return an error.

irb(main):075:0> rect2=Rectangle.new(3,4) => #<Rectangle:0x59a3088 @width=4, @height=3> irb(main):076:0> rect2.doubleSize() => "New area: 48" irb(main):077:0> rect2.grow() NoMethodError: private method 'grow' called for #<Rectangle:0x59a3088 @width=8, @height=6> from (irb):77 from :0

Instance and class variables are private by default in Ruby unless attribute accessors and mutators are provided.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.