byGink▻ Sat, 10 Apr 2021
Modules in Ruby
A module in Ruby is a collection of methods and constants that can be included in one or more classes. Modules are used to encapsulate related behavior and make it easy to reuse code between multiple classes. Modules can contain methods, constants, and other modules, and they can be used to provide a namespace for organizing code.
module User
class User
# user class implementation
end
class Admin < User
# admin class implementation
end
end
In this example, the User module groups the User and Admin classes together, making it clear that they are related. This organization makes the code easier to maintain and understand.
module Greeting
def say_hello
puts "Hello, #{name}!"
end
end
class Cat
include Greeting
attr_accessor :name
end
class Dog
include Greeting
attr_accessor :name
end
cat = Cat.new
cat.name = "Kitty"
cat.say_hello # => Hello, Kitty!
dog = Dog.new
dog.name = "Scooby-Doo"
dog.say_hello # => Hello, Scooby-Doo!
By including the module, the classes can share the same method implementation, without the need for inheritance.
module MyModule
class MyClass
# class implementation
end
end
class MyClass
# class implementation
end
MyModule::MyClass.new # creates an instance of the MyModule::MyClass class
MyClass.new # creates an instance of the top-level MyClass class
By defining the class inside the module, it becomes possible to avoid naming conflicts with the top-level MyClass class.
module CSV
def to_csv
raise "Not implemented"
end
def from_csv(line)
raise "Not implemented"
end
end
And then implement a class with that module
class Book
include CSV
def to_csv
"#{@title},#{@page}"
end
def from_csv(line)
parts = line.split(",")
@title = parts[0]
@page = parts[1]
end
end
This looks and feels like interfaces in Java, however there is a problem. If we include the CSV module and forget to implement the to_csv
method, we won’t notice this mistake until we run our code.
To fix it, let's try a different solution: Testing. We can create some set of tests and describe our object:
shared_examples "a CSV serializable object" do
it { is_expected.to respond_to(:to_csv) }
it { is_expected.to respond_to(:from_csv) }
end
describe Book do
it_behaves_like "a CSV serializable object"
end
describe Sheet do
it_behaves_like "a CSV serializable object"
end
With this way, we can implement as many interfaces as we want. No need to put all actions into one abstract class and extend from it.
We went through all the benefits that module provides. Now let's get back to code reusability and dig deeper about the concept of Mixin.
A mixin is a type of module that is included in a class to add specific behavior to that class. A mixin is essentially a way to reuse code within a single class, rather than across multiple classes. When a mixin is included in a class, all of the methods defined in the mixin become available as methods in the class.
include
and extend
include
adds instance methods to a class. When a module is included in a class, the methods of the module become available to the instances of the class. In other words, the methods can be called on an object created from that class.extend
adds class methods to a class. When a module is extended in a class, the methods of the module become available to the class itself. In other words, the methods can be called on the class object itself, not on the instances of the class.module MyModule
def my_method
puts "Hello from MyModule!"
end
end
class MyClass
include MyModule
end
MyClass.new.my_method #=> "Hello from MyModule!"
class AnotherClass
extend MyModule
end
AnotherClass.my_method #=> "Hello from MyModule!"
include
or extend
a moduleself.included(base)
is a hook method that is called whenever the module is included in another module or class. The base
parameter in this method refers to the class or module that the module is being included in.module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def my_class_method
puts "This is a class method"
end
end
def my_instance_method
puts "This is an instance method"
end
end
class MyClass
include MyModule
end
MyClass.my_class_method
# Output: "This is a class method"
my_object = MyClass.new
my_object.my_instance_method
# Output: "This is an instance method"
extend
, we also have self.extended
module MyModule
def my_method
puts "Hello from MyModule!"
end
def self.extended(base)
puts "#{base} has extended MyModule!"
end
end
class MyClass
extend MyModule
end
# Output: MyClass has extended MyModule!
MyClass.my_method
# Output: Hello from MyModule!
The only thing we need to keep in mind is that everything in ruby is an object, including the class itself. And when extending, we do not apply the changes on class instances but on the class object.
With the help of include
and extend
methods, we have the ability to achieve code reuse and polymorphism in Ruby through the use of mixins. Mixins provide many benefits, including code organization and reusability, making them an important concept for Ruby developers to understand and use effectively.
© 2016-2024 GinkCode.com