You are on page 1of 6

# 6.1 Being Methodical What are methods, really?

========================= We've already talked about how objects in Ruby are analogous eal world. We've also briefly covered how they interact with a construct called a "method." Since everything useful in a ough objects collaborating using methods, understanding them ant. to objects in the r one another through program happens thr well is very import

When one object calls a method on another, it is simply telling it what to do. A method, then, is simply programming jargon for something one object can do for another. In the example below, we ask the object that represents the integer 1 to give us the next integer in the sequence. Do keep in mind that in the context of the pr ogram, "we" simply means the current object. Example Code: puts 1.next So, to summarize, the data an object contains is what it is and its methods are what it can do. Implicit in this definition is the fact that the abilities of an object are limited to the methods it exposes. Objectifying methods Methods aren't exempt from Ruby's "everything is an object" rule. This means tha t the methods exposed by any object are themselves objects, and yes, you can use them as such. All objects in Ruby expose the eponymous method method that can be used to get h old of any of its methods as an object. Example Code: puts 1.method("next") Here, we ask the object that is the integer 1 to give us the instance of the met hod next. The method object still maintains a relationship with the object to which it bel ongs so you can still call it and it responds like a normal invocation of that m ethod. See for yourself. Example Code: next_method_object = 1.method("next") puts next_method_object.call It's worth noting that in the normal course of things it is unlikely that you wi ll need to - or should - look up a method object and use it as such. Make it so ========== Let's write a method called reverse_sign that takes one object - an Integer - an d changes a positive value to negative one, and vice-versa. We'll then dissect i t, then move on to you practicing writing your own method. Example Code: def reverse_sign(an_integer) return 0 - an_integer end

puts reverse_sign(100) puts reverse_sign(-5) There, that works perfectly, converting 100 to -100 and -5 to 5 by simply subtra cting the number given from 0. Let's dissect this example a little, and you can then practice by writing your o wn. First, note that we use the def keyword to create a method called reverse_sign o n the current object. Since Ruby doesn't allow us to use spaces in method names, we replace them with underscores instead. It's also recommended that, as a conv ention, method names be in lower case. The reverse_sign method accepts one parameter or argument. This is simply jargon for the objects a method needs from the caller in order for it to do its job. I n this case, it's an integer. A method can accept any number of parameters (or n one). The return keyword specifies the object to be returned to the caller when the me thod has done its work. If no return keyword is specified, the object created by the last line in the method is automatically treated as the return value. A met hod must always return exactly one object. Finally, the method is closed using the end keyword. There - simple, right? Example Code: def do_nothing end puts do_nothing.class As you can see, even a method that does nothing at all and has no return produce s an object - the nil. I'm printing out the class name because printing a nil re turns an empty string, so you wouldn't see anything. Be cautious when using return - calling return also exits the method at that poi nt. No code in the method after the return statement is executed. Example Code: def demonstrate_early_return return puts "You will never see this, because we never get here." end puts demonstrate_early_return.class This last example demonstrates two things: The return exits the method; the puts statement that comes right after is ne ver run. Calling return without specifying an object to return results in a nil, whic h is returned by default. An excellent practice is to either avoid using return entirely or always use ret urn in the last line of the method (which is effectively the same thing). Having a method exit in the middle is always surprising, so being consistent in this m anner makes for code that's easier to understand. Beam me up, Scotty Your turn. Write a method called add_two that adds 2 to any number passed to it and returns the result. Yes, please feel free to experiment using next in additi

on to the more obvious route of simply adding the integer 2 to the incoming numb er. def add_two(input) input.next.next end A more subtle lesson we learn from this exercise - especially if you got this to work using both addition as well as next - is that: As someone using a method, you don't usually care about how it works, just t hat it does. As the author of the method, you're free to change its internal implementati on so long as it continues producing the same result. Switch from addition to ne xt and back again - the master is happy either way. # 6.2 Calling a method Cooperative objects =================== Thus far, we've only dealt with methods that only accept a single object as an a rgument or parameter. We'll expand this and talk about the ways in which methods can accept more than one parameter. Let's take this slow and begin with two parameters. Example Code: def add(a_number, another_number) a_number + another_number end puts add(1, 2) So, adding a second parameter is really simple - just add the new parameter sepa rated from the original by a comma. Example Code: def add(a_number, another_number, yet_another_number) a_number + another_number + yet_another_number end puts add(1, 2, 3) Parameters can have default values too. Let's say we usually add three numbers, but occasionally just add two. We can default the last parameter in the previous example to 0 if nothing is passed to it. Example Code: def add(a_number, another_number, yet_another_number = 0) a_number + another_number + yet_another_number end puts add(1, 2) Older versions of Ruby - 1.8.x and older - required you to set default values fo r parameters starting with the last parameter in list and moving backward toward the first. The current version of Ruby (1.9.x) no longer has this limitation, b ut it's worth mentioning since Ruby 1.8.7 is still in use. Ok, your turn. I shall simply ask that you make the tests pass for the exercise below.

def say_hello(name="Qui-Gon Jinn") "Hello, #{name}." end Arraying your arguments ======================= The list of parameters passed to an object is, in fact, available as a list. To do this, we use what is called the splat operator - which is just an asterisk (* ). The splat operator is used to handle methods which have a variable parameter lis t. Let's use it to create an add method that can handle any number of parameters . Example Code: def add(*numbers) numbers.inject(0) { |sum, number| sum + number } end puts puts puts puts add(1) add(1, 2) add(1, 2, 3) add(1, 2, 3, 4)

The splat operator works both ways - you can use it to convert arrays to paramet er lists as easily as we just converted a parameter list to an array. I'll show you how we can splat an array of three numbers into a parameter list s o that it works with one of the examples from earlier in this lesson that accept s exactly three parameters. Example Code: def add(a_number, another_number, yet_another_number) a_number + another_number + yet_another_number end numbers_to_add = [1, 2, 3] # Without a splat, this is just one parameter puts add(*numbers_to_add) # Try removing the splat just to see what happens If you know some of the parameters to your method, you can even mix parameter li sts and splatting. Again, older versions of Ruby (1.8.x or older) required you t o place splatted parameters at the end of the parameter list, but this is no lon ger necessary. In the example below, I'll expand on an earlier example to allow for the sum to be printed as a part of a message. We know what the message is - but we don't kn ow how many numbers we'll need to add. Example Code: def add(*numbers) numbers.inject(0) { |sum, number| sum + number } end def add_with_message(message, *numbers) "#{message} : #{add(*numbers)}" end puts add_with_message("The Sum is", 1, 2, 3) Why don't you try it on for size? Create a method called introduction that accep ts a person's age, gender and any number of names, then returns a String that in

troduces that person by combining all of these values to create a message accept able to the tests. As always, your objective is to make all the tests pass. Pay careful attention t o the feedback from the tests, because even a simple, misplaced comma in the mes sage could cause them to fail. def introduction(age, gender, *names) "Meet #{names.join(' ')}, who's #{age} and #{gender}" end Naming parameters ================= This last section on method invocation is easiest demonstrated, then explained. Pay careful attention to the invocation of the add method in the example below. Look at how neatly we are able to pass configuration options to the method; the user of the add method gets to decide if the absolute value should be returned a nd if rounding should happen. Example Code: def add(a_number, another_number, options = {}) sum = a_number + another_number sum = sum.abs if options[:absolute] sum = sum.round(options[:precision]) if options[:round] sum end puts add(1.0134, -5.568) puts add(1.0134, -5.568, absolute: true) puts add(1.0134, -5.568, absolute: true, round: true, precision: 2) Ruby makes this possible by allowing the last parameter in the parameter list to skip using curly braces if it's a hash, making for a much prettier method invoc ation. That's why we default the options to {} - because if it isn't passed, it should be an empty Hash. As a consequence, the first invocation in the example has two parameters, the se cond, three and the last, seemingly five. In reality, the second and third invoc ations both have three parameters - two numbers and a hash. A not-so-gentle workout You are used to this by now. Write for me three methods - calculate, add and sub tract. The tests should all pass. Take a look at the hint if you have trouble! A nd as a little extra hint: remember that you can use something.is_a?(Hash) or an other_thing.is_a?(String) to check an object's type. def add(*numbers) numbers.inject(0) { |sum, number| sum + number } end def subtract(*numbers) sum = numbers.shift numbers.inject(sum) { |sum, number| sum - number } end def calculate(*arguments) # if the last argument is a Hash, extract it # otherwise create an empty Hash options = arguments[-1].is_a?(Hash) ? arguments.pop : {} options[:add] = true if options.empty?

return add(*arguments) if options[:add] return subtract(*arguments) if options[:subtract] end

You might also like