Modules and Mixins#
Modules#
Module in Ruby this is a way to group some classes, functions, constants together. It is quite similiar to the class, but module can not have an instance or the subclasses - it is just a namespace for a group of objects. Module is defined by keywords module … end
. Below is an example of Network module containing method, class and constant:
module Network # module name
PROTOCOLS = [:http, :ftp, :telnet] # module constant
def Network.is_a_protocol?(protocol) # module method is defined like the class method
PROTOCOLS.include? protocol # module constact is accessible from the module method
end
class Ip # module class
def initialize(p1, p2, p3, p4)
@p1 = p1; @p2 = p2; @p3 = p3; @p4 = p4
end
def to_s
"#{@p1}.#{@p2}.#{@p3}.#{@p4}"
end
def inspect
"IP: #{to_s}"
end
end
end
To access the module method use Module_name.method_name
, Module_name::CONSTANT_NAME
for constants and Module_name::ClassName
for classes.
Network::PROTOCOLS
#=> [:http, :ftp, :telnet]
Network.is_a_protocol? :ftp
#=> true
ip = Network::Ip.new 192, 168, 1, 1
#=> IP: 192.168.1.1
But it would be annoying to write a module name many times, especially when you are writing a long script. To avoid this, you may insert all the things from the module using include ModuleName
statement to treat all the stuff like the local objects:
include Network
#=> Object
PROTOCOLS
#=> [:http, :ftp, :telnet]
is_a_protocol? :ftp
#=> true
ip = Ip.new 192, 168, 1, 1
#=> IP: 192.168.1.1
Math Module#
There is a build in module Math
contains trigonometric and transcendental function (see documentation ri Math
for complete list of methods). It is good to include Math
if you are planning to do more work with this module.
Math.sin(Math::PI/2)
#=> 1.0
include Math
#=> Object
sin(PI/2)
#=> 1.0
sqrt 2
#=> 1.4142135623730951
Etc Module#
Another interesting built-in module is called Etc
- a Ruby way to access information stored in /etc/passwd
and /etc/group
.
require 'etc' # you must load the file with Etc module before you can use it
#=> true
Etc.getlogin # Etc.getlogin returns the current user name
#=> "grych"
Etc.getpwnam(Etc.getlogin) # Etc.getpwnam returns object containing all the information from passwd
#=> #<struct Struct::Passwd name="grych", passwd="********", uid=501, gid=20, gecos="Tomasz Gryszkiewicz", dir="/Users/grych", shell="/bin/zsh", change=0, uclass="", expire=0>
Etc.getpwnam('grych').gecos # and this is how you can access this structure
#=> "Tomasz Gryszkiewicz"
Mixins#
While discussing objects we talked about inheritance: to remind, object can be a subclass of the other object, it inherits all the methods, variables from the other class. But object can have only one superclass, so if you want to use some methods from few different sources, you must use Mixin. Mixin it is a module to be loaded into the class. All the module methods, constants will be included to the class.
To have an example lets create the mixin called Pingable
with method alive?
returns true if ping to the server works.
module Pingable
def alive?
p = `ping -c 1 -t 10 #{self.full_name}` # like in Shell you can run any command by putting it in ``
p.include? '1 packets received' # if output of ping command include this string, returns true
end
end
self.fullname
in the code above does not refer to the mixin, but to the instance of an object, to which this mixin will be included. This is a kind of a protocol: we assume, that all object which want to include Pingable
must have defined full_name
method.
Let’s go and load the mixin to our Server class:
class Server
include Pingable # include previously defined mixin
def initialize(name, domain)
@name = name
@domain = domain
end
def full_name # mixin will call that function
@name + @domain
end
end
s = Server.new('www', '.startrek.com')
#=> #<Server:0x007fce68aee3e8 @name="www", @domain=".startrek.com">
s.alive?
#=> true
Server.new('www', '.starwars.com').alive?
#=> false