Thursday 4 December 2008

Ruby Methods Part 1 - The Basics

A Ruby method definition starts with the keyword def followed by the method name and the parameter names, and ends with the keyword end.
def test
puts "Hello World"
end

test # Causes "Hello World" to be printed

Note that unlike Java and C#, Ruby does not require the return type or the parameter types to be stated. Every Ruby method returns a value. In the above example, puts returns nil, and as this is the last statement in the method test, test will also return nil. The return keyword is entirely optional (though necessary if you want to jump out of the middle of a method, of course).

Visibility
By default, all methods are public; they can be accessed from anywhere by anything.

Private methods can only be accessed from with the class and (unlike Java et al) its subclasses. That said, you can access private methods from anywhere using the send method, or modify their visibility on-the-fly, so it would seem that this is more advisory than anything else; the API writer is telling you that you probably should not use those methods.

The private keyword indicates that all subsequent methods (until public or protected is encounted) will be private.
class MyClass
def public_method
puts "In public_method"
end

# You can access private methods within the class
def private_via_public_method
puts "private_via_public_method"
private_method
end

private # Flags all subsequent methods
# as private

def private_method
puts "In private_methodstance_method"
end
end

class MySubClass < MyClass
# You can also access private methods
# within any subclass
def private_via_subclass_public_method
puts "private_via_subclass_public_method"
private_method
end
end

mc = MyClass.new
mc.private_via_public_method
mc.public_method
#mc.protected_method # Protected method
# not visible
#mc.private_method # Private method
# not visible

mc.send :private_method # Private method
# accessed with send

# Visibility can be changed dynamically
class MyClass
public :private_method
end

mc.private_method # Private method is
# now visible

The third visibility type is protected, which is a little more esoteric. The protected visibility is very similar to private, except that protected allows you call method from a different object, as long as that object is of the same class or subclass. To put it another way, private is restricted to the specific object, protected is restricted to the class.
class MyClass
# You can access private methods on the
# object within the class
def private_via_public_method
private_method
end

# You can access protected methods on another
# object within the class
def protected_via_other_public_method
mc = MyClass.new
mc.protected_method
end

# This will fail
# You cannot access private methods on another
# object, even within the class
def private_via_other_public_method
mc = MyClass.new
mc.private_method
end

protected
def protected_method
puts "In protected_method"
end

private
def private_method
puts "In private_methodstance_method"
end
end

The important point here is that both private and protected methods can only be used with the class (or subclass), but that private methods are further restricted to calls that do not require the dot operator.

As an aside, in Ruby all constants are public, and all variables are private

Class methods
Class methods are methods that are applied to the class rather than to a specific instance of the class. The most important example of a class method is new; in Rails the find method is also used very frequently.
s = String.new "My string"  # Equivalent to
# s = "My string"
post = Post.find :first # gets the first record
# from the posts table

As can be seen, class methods are invoked by using the class name, rather than the object, and as in the examples above they are often used to create or retrieve instances of the class.

Class methods are written much like instance methods, but with the keyword self before the method name. You cannot call a class method from an instance of the class, or call an instance method from the class itself. This does mean you can have a class method with the same name as an instance method.

See the next section for an example of a class method.

The initialize method
I have already mentioned the new class method. What this does is create a new instance of the object, and then it invokes the initialize method, if it exists. The initialize method is always private; there seems no way to change that. However, you can invoke it from within your class.

The following code illustrates the use of initialize and class methods.
class MyClass
def initialize
puts "In initialize"
end

def self.class_method
puts "In class_method"
end

def instance_method
puts "In instance_method"
end
end

MyClass.class_method # Prints "In class_method"
mc = MyClass.new # Would cause error; cannot call class methods on an object
#mc.class_method # Prints "In initialize"
mc.instance_method # Prints "In instance_method"


Class initialize method?
Sometimes you might want to do something to set up your class. You want it to run before any instances and created. I believe Rails does this for ActiveRecords, inspecting the database to see what fields are present, so it will know how to handle requests, rather than inspecting the database each time a request is made.

There is no method that supports this, you just put your code straight into the class definition.
class MyClass
p 'This will be printed when the class is loaded'

def initialize
p 'This will be printed when an object is instantiated'
end
end


Multiple return results
Pretty much every modern language allows you to send multiple arguments to a function/procedure/method. How many allow you to return multiple arguments? Certainly not the C derivatives, and really I cannot see why that should be so. At the machine code level, I would assume there is no difference. I would guess the reason is that C is based on a mathematical concept of functions, F = f(x,y,z), and Java and C# had followed suit.

Not so Ruby, which allows you to return as many values as you like. Here is a trivial example to illustrate that:
def test
return return 1, 'two', 3
end

a, b = test
p a # => 1
p b # => "two"

a, b, c, d = test
p a # => 1
p b # => "two"
p c # => 3
p d # => 4

Note that in this case the return keyword is required. What is actually returned is an array containing each each return value. This can lead to confusion...
def tester
return 3, 6
end

def output x, y = nil
p "x=#{x} y=#{y}"
end

x, y = tester
output x, y
# => "x=3 y=6"

output tester
# => "x=36 y="

The first call to tester correctly gets the two values assigned to the two variables. However, the second fails; the array is passed directly to the output method, which has no clue about the context, and just sees an array. What is really happening is a bit of trickery. Ruby does not pass back multiple values at all; it just passes back an array when multiple values are used with return. Meanwhile, at the other end, Ruby allows multiple varaibles to be set from an array.
x, y = [7, 6]
output x, y

Overall we have the illusion of muliple returns.

blog on dynamically creating methods:
http://ola-bini.blogspot.com/2008/05/dynamically-created-methods-in-ruby.html

Struggling with Ruby: Contents Page

No comments: