Ruby Modules
Modules as Namespaces
Prevent name collisions
module Network
class Scanner
def initialize(range)
@range = range
end
def sweep
puts "Scanning #{@range}"
end
end
end
module Security
class Scanner
def audit(target)
puts "Auditing #{target}"
end
end
end
# No collision
net = Network::Scanner.new("10.50.1.0/24")
sec = Security::Scanner.new
Mixins
include adds instance methods, extend adds class methods
module Pingable
def reachable?
system("ping -c 1 -W 2 #{ip} > /dev/null 2>&1")
end
end
module Loggable
def log(message)
puts "[#{Time.now.strftime('%H:%M:%S')}] #{self.class.name}: #{message}"
end
end
class Server
include Pingable
include Loggable
attr_reader :hostname, :ip
def initialize(hostname, ip)
@hostname = hostname
@ip = ip
end
def check
log("Checking #{hostname}")
reachable? ? log("UP") : log("DOWN")
end
end
srv = Server.new("vault-01", "10.50.1.40")
srv.is_a?(Pingable) # => true
Prepend
prepend inserts module before class in method resolution
module Auditable
def save
puts "AUDIT: saving #{self.class.name}"
super # calls the original save
puts "AUDIT: saved"
end
end
class DeviceConfig
prepend Auditable
def save
puts "Saving configuration..."
end
end
DeviceConfig.new.save
# AUDIT: saving DeviceConfig
# Saving configuration...
# AUDIT: saved
DeviceConfig.ancestors
# => [Auditable, DeviceConfig, Object, Kernel, BasicObject]
Enumerable Mixin
Adding Enumerable to custom classes
class DeviceList
include Enumerable
def initialize
@devices = []
end
def add(device)
@devices << device
self
end
def each(&block)
@devices.each(&block)
end
end
list = DeviceList.new
list.add("sw-01").add("sw-02").add("rtr-01")
list.map(&:upcase) # => ["SW-01", "SW-02", "RTR-01"]
list.select { |d| d.start_with?("sw") }
list.count # => 3