Ruby Modules: include vs extend vs prepend
Tags: modules, ruby, beginners
Published: 2021-01-20 17:24:49

Previously, we learned about the differences between class inheritance and modules in Ruby. You can read more about that [here](https://dev.to/abbiecoghlan/class-inheritance-vs-modules-in-ruby-1fha). In that post, we reviewed how to use modules to share functionality between classes that do not have a clear hierarchical arrangement. Specifically, we used the `include` keyword to give our classes access to instance methods. In this post, we will focus on digging deeper into modules in Ruby by reviewing the use of the `include` keyword and introducing two other keywords, `extend` and `prepend`. Understanding the different behaviors of each of these three keywords and knowing when to use them will provide us with the power to utilize modules to extend functionality to our classes in a variety of ways. 

##`include`
`include` is the keyword that we will use within our classes to provide access to the methods defined in a module as *instance* methods in our class. In order to do so, we will first define a module.

```ruby
module IndoorPet
    def can_be_housebroken?
        true
    end
end  
```

Now, all we have to do to give our classes access to the instance method `can_be_housebroken?` as it is defined in our IndoorPet module is add `include IndoorPet` in the body of our class when we define it. 

```ruby
class Animal 
end 

class Dog < Animal 
    include IndoorPet
end 

class Cat < Animal
    include IndoorPet
end 

class Rabbit < Animal 
    include IndoorPet
end


roger = Rabbit.new("Roger")

roger.can_be_housebroken?
=> true 

Rabbit.can_be_housebroken?
=> NoMethodError: undefined method `can_be_housebroken?' for Rabbit:Class


```

With that, we have now *included* the module's instance method `can_be_housebroken?` in our Dog, Cat, and Rabbit classes. Thus, any instances of those classes will now have access to the `can_be_housebroken?` method. 


When you `include` a module, Ruby will insert the module into the class's ancestry chain just above our class, between our class and it's superclass. This chain can be seen when calling `.ancestors` on our Rabbit class.

```ruby 
Rabbit.ancestors
=> [Rabbit, IndoorPet, Animal, Object, PP::ObjectMixin, Kernel, BasicObject]
```

##`extend`
Now that we know how to use include to provide a class with access to a module's instance methods, you might be wondering how to provide a class with access to a module's *class* methods. The `extend` keyword does just that. It is used in exactly the same way as include, except that instead of instance methods which can be called on individual instances of our class, all of the methods we are *extending* to our class will be provided as class methods we can call on the class as a whole. 

Instead of including `can_be_housebroken?` as instance method, let's try extending it as a class method. First, we define our module.


```ruby
module IndoorPet
    def can_be_housebroken?
        true
    end
end  
```

Then we use the `extend` keyword the same way we used the `include` keyword previously. 

```ruby
class Animal 
end 

class Dog < Animal 
    extend IndoorPet
end 

class Cat < Animal
    extend IndoorPet
end 

class Rabbit < Animal 
    extend IndoorPet
end



roger = Rabbit.new("Roger")

roger.can_be_housebroken?
=> NoMethodError: undefined method `can_be_housebroken?' for  #<Rabbit:0x00007fa84389c748> 

Rabbit.can_be_housebroken?
=> true

```

With that, we have now *extended* the module's class method `can_be_housebroken?` to our Dog, Cat, and Rabbit classes. Now those classes as a whole have access to the `can_be_housebroken?` method. Note that you can no longer `call can_be_housebroken?` on an individual instance of the class. With `extend`, we have only given the method to the class as a class method. 


When you extend a module, ruby will provide the module's methods to the class as class methods. However, unlike with `include`, when you `extend` a module, Ruby will not insert the module into the class's ancestry chain. We can see this by calling `.ancestors` on our Rabbit class.

```ruby 
Rabbit.ancestors
=> [Rabbit, Animal, Object, PP::ObjectMixin, Kernel, BasicObject]
```


##`prepend`
The third keyword for providing a module's methods to a class is `prepend`. We can use `prepend` in the same way we would use `include` or `extend` by first defining the module and then using the keyword and name of the module within the class's definition, as below. 

```ruby
module IndoorPet
    def can_be_housebroken?
        true
    end
end  


class Animal 
end 

class Dog < Animal 
    prepend IndoorPet
end 

class Cat < Animal
    prepend IndoorPet
end 

class Rabbit < Animal 
    prepend IndoorPet
end
```

In many ways, `prepend` operates like `include`. Both provide a class access to a module's methods as instance methods. Therefore, we can call the imported methods that we have prepended from our module on specific instances of a class, and not on the class itself. 


```ruby 
roger = Rabbit.new("Roger")

roger.can_be_housebroken?
=> true 

Rabbit.can_be_housebroken?
NoMethodError: undefined method `can_be_housebroken?' for Rabbit:Class
```

However, the difference between `include` and `prepend` is related to the location in the ancestry chain where the module is placed. With `prepend`, the module is not inserted between the class and it's superclass as it was with `include`. It is actually inserted at the very bottom of the ancestry chain, as seen below.  

```ruby
Rabbit.ancestors
=> [IndoorPet, Rabbit, Animal, Object, PP::ObjectMixin, Kernel, BasicObject]
```

The importance of this distinction will be explored further in our next post! 

##`include` vs `extend` vs `prepend`
####`include`
* Provides a class with access to a module's methods as instance methods
* Allows the methods to be called on individual instances of the class  
* Does not allow methods to be called on the class as a whole
* Inserts module into ancestry chain between the class and superclass 

####`extend`
* Provides a class with access to a module's methods as class methods 
* Does not allow the methods to be called on individual instances of the class  
* Allows methods to be called on the class as a whole

####`prepend`
* Provides a class with access to a module's methods as instance methods 
* Allows the methods to be called on individual instances of the class  
* Does not allow methods to be called on the class as a whole
* Inserts module at the bottom of the ancestry chain