Skip to content

Instantly share code, notes, and snippets.

@zduymz
Last active October 21, 2016 21:49
Show Gist options
  • Save zduymz/2e1614e39c93371b935fd9d68bbe101a to your computer and use it in GitHub Desktop.
Save zduymz/2e1614e39c93371b935fd9d68bbe101a to your computer and use it in GitHub Desktop.

Q: What’s the difference between an accessor method and an instance method?

A: “Accessor method” is just a way of describing one particular kind of instance method, one whose primary purpose is to get or set the value of an instance variable. In all other respects, accessor methods are ordinary instance methods.

Q: I set up an instance variable outside an instance method, but it’s not there when I try to access it. Why? class Widget @size = 'large' def show_size puts "Size: #{@size}" end end A: When you use instance variables outside of an instance method, you’re actually creating an instance variable on the class object. (That’s right, even classes are themselves objects in Ruby.)

While there are potential uses for this, they’re beyond the scope of this book. For now, this is almost certainly not what you want. Instead, set up the instance variable within an instance method:

class Widget
  def set_size
    @size = 'large'
  end
  ...
end

Q: Can you have more than one level of inheritance? That is, can a subclass have its own subclasses?

A: Yes! If you need to override methods on some of your subclass’s instances, but not others, you might consider making a subclass of the subclass.

class Car < Vehicle end

class DragRacer < Car def accelerate puts "Inject nitrous!" end end Don’t overdo it, though! This kind of design can rapidly become very complex. Ruby doesn’t place a limit on the number of levels of inheritance, but most Ruby developers don’t go more than one or two levels deep.

Q: You said that if a method is called on an instance of a class and Ruby doesn’t find the method, it will look on the superclass, then the superclass’s superclass... What happens if it runs out of superclasses without finding the method?

A: After searching the last superclass, Ruby gives up the search. That’s when you get one of those undefined method errors we’ve been seeing.

image with no caption Q: When designing an inheritance hierarchy, which should I design first, the subclass or the superclass?

A: Either! You might not even realize you need to use inheritance until after you’ve started coding your application.

When you discover that two related classes need similar or identical methods, though, just make those classes into subclasses of a new superclass. Then move those shared methods into the superclass. There: you’ve designed the subclasses first.

Likewise, when you discover that only some instances of a class are using a method, create a new subclass of the existing class and move the method there. You’ve just designed the superclass first!

Q: I tried this code in irb instead of using the ruby command. After I override to_s, if I type lucy = Dog.new into irb, I still see something like # < Dog:0x007fb2b50c4468>. Why don’t I see the dog’s name and age?

A: The values that irb shows you are the result of calling inspect on an object, not to_s. You won’t see the results of to_s until you set the name and age, and pass the object to puts.

Q: What’s the difference between initialize methods in Ruby and constructors from other object-oriented languages?

A: They both serve the same basic purpose: to let the class prepare new instances for use. Whereas constructors are a special structure in most other languages, though, Ruby’s initialize is just an ordinary instance method.

Q: Why do I have to call MyClass.new? Can’t I just call initialize directly?

A: The new method is needed to actually create the object; initialize just sets up the new object’s instance variables. Without new, there would be no object to initialize! For this reason, Ruby doesn’t allow you to call the initialize method directly from outside an instance. (So we oversimplified a little bit; initialize does differ from an ordinary instance method in one respect.)

Q: Does MyClass.new always call initialize on the new object?

A: Yes, always.

Q: Then how have we been calling new on the classes we’ve made so far? They didn’t have initialize methods!

A: Actually, they did have one... All Ruby classes inherit an initialize method from the Object superclass.

Q: But if Employee inherited an initialize method, why did we have to write our own?

A: The initialize from Object takes no arguments, and basically does nothing. It won’t set up any instance variables for you; we had to override it with our own version in order to do that.

Q: Can I return a value from an initialize method?

A: You can, but Ruby will ignore it. The initialize method is intended solely for setting up new instances of your class, so if you need a return value, you should do that elsewhere in your code.

Q: If I override initialize in a subclass, does the superclass’s initialize method run when the overriding initialize method runs?

A: Not unless you explicitly call it with the super keyword, no. Remember, in Ruby, initialize is just an ordinary method, like any other. If you call the move method on a Dog instance, does move from the Animal class get run as well? No, not unless you use super. It’s no different with the initialize method.

Ruby is not the same as many other object-oriented languages, which automatically call the superclass’s constructor before calling the subclass constructor.

Q: If I use super to call the superclass’s initialize method explicitly, does it have to be the first thing I do in the subclass’s initialize method?

A: If your subclass depends on instance variables that are set up by the superclass’s initialize method, then you may want to invoke super before doing anything else. But Ruby doesn’t require it. As with other methods, you can invoke super anywhere you want within initialize.

Q: You say the superclass’s initialize method doesn’t get run unless you call super... If that’s true, then how does @last_name get set in this sample?

class Parent attr_accessor :last_name def initialize(last_name) @last_name = last_name end end

class Child < Parent end

child = Child.new("Smith") puts child.last_name

A: Because initialize is inherited from the Parent class. With Ruby instance methods, you only need to call super to invoke the parent class’s method if you want it to run, and you’ve overridden it in the subclass. If you haven’t overridden it, then the inherited method is run directly. This works the same for initialize as it does for any other method.

Q: Why can blocks access variables that were declared outside their bodies, when methods can’t? Isn’t that unsafe?

A: A method can be accessed from other places in your program, far from where it was declared (maybe even in a different source file). A block, by contrast, is normally accessible only during the method call it’s associated with. A block, and the variables it has access to, is kept in the same place in your code. That means you can easily see all the variables a block is interacting with, meaning that accessing them is less prone to nasty surprises.

Q: How can File.open work both with a block and without one? A:

A: Within a Ruby method, you can call the block_given? method to check whether the method caller used a block, and change the method behavior accordingly.

If we were coding our own (simplified) version of File.open, it might look like this:

def File.open(name, mode)
  file = File.new(name, mode)
  if block_given?
     yield(file)
  else
     return file
  end
end

If a block is given, the file is passed to it for use within the block. If it’s not, the file is returned.

Q: Do all blocks return a value?

A: Yes! They return the result of the last expression in the block body.

Q: If that’s true, then why didn’t we learn about this sooner?

A: We haven’t needed to. A block may return a value, but the associated method doesn’t have to use it. The each method, for example, ignores the values returned from its block.

Q: Can I pass parameters to a block and use its return value?

A: Yes! You can pass parameters, use the return value, do both, or do neither; it’s up to you.

def one_two
  result = yield(1, 2)
  puts result
end

one_two do |param1, param2|
  param1 + param2
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment