Você está na página 1de 5

# 8.

1 Getting Modular Mixing it up ============ Ruby modules allow you to create groups of methods that you can then include or mix into any number of classes. Modules only hold behaviour, unlike classes, whi ch hold both behaviour and state. Since a module cannot be instantiated, there is no way for its methods to be cal led directly. Instead, it should be included in another class, which makes its m ethods available for use in instances of that class. There is, of course, more t o this story, but let's keep it simple for now. In order to include a module into a class, we use the method include which takes one parameter - the name of a Module. Example Code: module WarmUp def push_ups "Phew, I need a break!" end end class Gym include WarmUp def preacher_curls "I'm building my biceps." end end class Dojo include WarmUp def tai_kyo_kyu "Look at my stance!" end end puts Gym.new.push_ups puts Dojo.new.push_ups In the example above, we have a Gym and a Dojo. They each have their own, distin ct behaviour - preacher_curls and tai_kyo_kyu respectively. However, both requir e push_ups, so this behaviour has been separated into a module, which is then in cluded into both classes. Some hierarchy and a little exercise Just like all classes are instances of Ruby's Class, all modules in Ruby are ins tances of Module. Interestingly, Module is the superclass of Class, so this means that all classes are also modules, and can be used as such. For detailed lessons on inheritance in Ruby, do take a look at our chapter on the subject in the "Ruby Primer: Ascen t." Example Code: module WarmUp end puts WarmUp.class # Module puts Class.superclass # Module

puts Module.superclass # Object Time for some practice! As always, make the tests pass. Note that the perimeter of both a square and a rectangle is calculated by summing up all of its sides. module Perimeter def perimeter sides.inject(0) { |sum, side| sum + side } end end class Rectangle include Perimeter def initialize(length, breadth) @length = length @breadth = breadth end def sides [@length, @breadth, @length, @breadth] end end class Square include Perimeter def initialize(side) @side = side end def sides [@side, @side, @side, @side] end end # 8.2 Modules as Namespaces Collision course ================ Namespacing is a way of bundling logically related objects together. Modules ser ve as a convenient tool for this. This allows classes or modules with conflictin g names to co-exist while avoiding collisions. Think of this as storing differen t files with the same names under separate directories in your filesystem. Modules can also hold classes. In this example, we'll try and define an Array cl ass under our Perimeter module from the last lesson. Notice how it does not affe ct Ruby's Array class at all. Example Code: module Perimeter class Array def initialize @size = 400 end end end our_array = Perimeter::Array.new ruby_array = Array.new p our_array.class

p ruby_array.class We have these two classes alongside each other. This is possible because we've n amespaced our version of the Array class under the Perimeter module. :: is a constant lookup operator that looks up the Array constant only in the Pe rimeter module. What happens when we don't namespace our Array class? Example Code: class Array def initialize @size = 400 end end our_array = Array.new p our_array.class Because Ruby has open classes, doing this simply extends the Array class globall y throughout the program, which is dangerous and of course not our intended beha viour. The examples above are a bit contrived for the sake of simplicity. The real prob lem that namespacing solves is when you're loading libraries. If your program bu ndles libraries written by different authors, it is often the case that there mi ght be classes or modules defined by the same name. We're assuming these two libraries gym and dojo have classes as shown in the com ment above them. Example Code: # class Push # def up # 40 # end # end require "gym" # up returns 40 # class Push # def up # 30 # end # end require "dojo" # up returns 30 dojo_push = Push.new p dojo_push.up gym_push = Push.new p gym_push.up As the dojo library is loaded after gym, it has overriden gym's class definition of Push and therefore creates an instance of Push defined in dojo. The way to solve this problem is to wrap these classes in appropriate namespaces using modules. Example Code: # module Gym

# class Push # def up # 40 # end # end # end require "gym" # module Dojo # class Push # def up # 30 # end # end # end require "dojo" dojo_push = Dojo::Push.new p dojo_push.up gym_push = Gym::Push.new p gym_push.up When you're creating libraries with Ruby, it is a good practice to namespace you r code under the name of your library or project. Constant lookup =============== We used the constant lookup (::) operator in the last section to scope our class to the module. As the name suggests, you can scope any constant using this oper ator and not just classes. Example Code: module Dojo A = 4 module Kata B = 8 module Roulette class ScopeIn def push 15 end end end end end A = 16 B = 23 C = 42 puts "A - #{A}" puts "Dojo::A - #{Dojo::A}" puts puts "B - #{B}" puts "Dojo::Kata::B - #{Dojo::Kata::B}" puts puts "C - #{C}" puts "Dojo::Kata::Roulette::ScopeIn.new.push - #{Dojo::Kata::Roulette::ScopeIn.n ew.push}"

There are a few things going on the example above. Constant A is scoped within Dojo and accessing it via :: works as expected. Same for constant B which is nested further inside Kata. Class ScopeIn is nested even deeper inside Roulette which has a method retur ning 15. This tells us two important things. One, we can nest constant lookups as deep as we want. Second, we aren't restricted to just classes and modules. You are given a library called RubyMonk. It contains a module Parser which defin es a class CodeParser. Write another class TextParser in the same namespace that parses a string and returns an array of capitalized alphabets. module RubyMonk module Parser class TextParser def self.parse(str) str.upcase.split("") end end end end If you prepend a constant with :: without a parent, the scoping happens on the t opmost level. In this exercise, change push to return 10 as per A = 10 in the to pmost level, outside the Kata module. module Kata A = 5 module Dojo B = 9 A = 7 class ScopeIn def push ::A end end end end A = 10 Using modules and namespacing is the standard way of organizing libraries with R uby. It's a good practice to keep this in mind while writing one.

Você também pode gostar