These free mini-courses will give you a strong foundation in web development. Track your progress and access advanced courses on HTML/CSS, Ruby and JavaScript for free inside our student portal.
Scroll down...
You've been using methods since square one and writing your own as well so we'll focus on the slightly more advanced stuff in this lesson. We'll cover what methods should do and some stylistic issues that come up frequently.
Each method should only do ONE thing. If it's doing two, it's time for another method. If it's doing a dozen, you probably need to start thinking about having a separate class.
What should you put into methods? Everything! All your program logic should be encapsulated in methods.
Methods help organize your code by keeping you from repeating yourself so anything that you find yourself doing repetitively should also go in its own method.
Methods make your code much easier to read and understand. You'll appreciate this the first time you have to debug a bit of horrendously complicated spaghetti code. It can take you longer to figure out what the code is actually doing in the first place than to fix the error.
Let's say you're building a game that uses an until
loop to keep taking turns until the game is over when someone wins. But figuring out if someone has won takes several lines of code. You really can't pack 20 lines of logic into the condition for your until
loop. How do you fix this?
It's better to write a method called game_over?
that does all the work. Then you can simply and legibly write until game_over?
and it's obvious what your code does. Even if the game_over?
logic is a single line, breaking it into an appropriately named method makes your code more legible.
For instance, the game_over?
method probably needs to check if the human player has won or has lost, so maybe it just contains calls to two other methods, victorious?
and defeated?
. That's okay!
If you were debugging, it'd be very easy for you to go and find where the problem is if the player just can't seem to lose:
def game_over?
victorious? || defeated?
end
def victorious?
# check for victory, return true/false
end
def defeated?
# check for loss, return true/false
end
The brevity in the method above is a good thing!
If your methods are >10 lines, you're probably doing too much. When you look at the open-source projects on Github, their methods are often incredibly short (the comments take up all the space). Good code doesn't look long, it looks brief but descriptive.
A method should be self-contained (encapsulated) and only use those variables that have been passed in. Don't modify global variables or otherwise have side effects for your methods unless absolutely necessary.
By the same token, don't destructively modify the arguments or the object your method was called on (unless it's explicitly a bang! method).
When naming methods the goal is to be descriptive but short. Name based on what it will return or what the major intended side effect will be. The name, while short, should still be fully descriptive because the method should only do one thing anyway.
Avoid names like get_xyz
because just naming the method xyz
is sufficient.
If you can't tell what the method will return based on the name, you probably need a better name. If your method name seems insanely long, your method may be trying to do more than one thing. End with a question mark ?
if it will return true/false.
Always use snake_case.
# BAD: avoid get_
def get_health(player)
player.health
end
# GOOD
def health(player)
player.health
end
# BAD: needs ?
def is_healthy(player) # Need a ?
player.health > 100
end
# GOOD
def healthy?(player)
player.health > 100
end
# BAD: Vague name, does too many things
def get_attacked(player, damage)
player.health -= damage
if player.health < 0
player.dead = true
end
end
# GOOD
def take_damage(player, damage)
player.health -= damage
end
self
?self
is a word that you see a whole lot in Ruby and it's actually pretty simple... it refers to whatever object is running the current method (the "caller"). So if I called current_user.jump
, current_user
is the caller of that method. Inside the definition of the jump
method, self
would refer to the current_user.
For another example:
class String
def who_called
puts self
end
end
"I did!".who_called
"I did!"
#=> nil
That is incredibly useful because we create methods that could be called by any number of different objects so we need a way inside of that method to dynamically refer to whatever object called it.
When you start working in Rails, you might want to return the full name of a user and self
will help you out:
def full_name
"#{self.first_name} #{self.last_name}"
end
# Console
> User.first.full_name
#=> "Foo Bar"
Sometimes you want to dynamically insert a method that should be called so you don't know its name ahead of time. That's one of the cool parts of Ruby!
To do this, use send
. It's simple to use -- just call it on the original object and pass in any additional parameters as normal. Adapted from the docs:
class Klass
def hello(string)
"Hello #{string}"
end
end
> k = Klass.new
#=> #<Klass:0x007ff25b9132c8>
> k.send :hello, "gentle readers"
#=> "Hello gentle readers"
This is not often seen in beginning Ruby but it comes up a fair bit when you get more advanced.