Moduler kan fungere som navnerom (eng: namespace) i tillegg til multippel arv av funksjonalitet.
1| # Moduler som navnerom 2| module Text 3| class Parser ; end 4| module SGML 5| class Parser ; end 6| module HTML 7| class Parser ; end 8| end 9| module XML 10| class Parser ; end 11| end 12| end 13| end 14| 15| # Bruke skop-operatoren 16| htmlparser = Text::SGML::HTML::Parser.new 17| 18| # Åpne spesifikke navnerom 19| class Text::SGML::HTML::Parser 20| def non_breakable_space 21| " " 22| end 23| end 24| 25| # Hente inn navnerom 26| include Text::SGML 27| xmlparser = XML::Parser.new |
Du kan også bruke klasser som navnerom i de fleste tilfeller, da (Class.ancestors.include? Module) == true
,
men du kan ikke include
en klasse, da Ruby ikke har multippel arv fra klasser.
Singleton-metoder eksisterer kun på et objekt.
Dette er gjerne klassemetoder, slik som new
.
1| class EnKlasse 2| def self.metode # Klassemetoder er singleton metoder. 3| end 4| end 5| 6| arr = ['usr', 'local', 'bin'] # Nesten alle objekter kan få 7| def arr.to_str # kan få singleton metoder 8| sep = File::SEPARATOR # lagt til seg når som helst. 9| File::SEPARATOR + self.join(sep) 10| end 11| puts arr.to_str 12| 13| class <<arr # Du kan også åpne et objekts 14| def write( *arg ) # singleton-klasse for å definere 15| self.push( *arg ) # metoder. 16| end 17| end 18| arr.write "irb" 19| puts arr.to_str |
Går vi dypere i metahierarkiet til Ruby, finne vi at singleton-metodene er definert i singleton-klasser.
1| class MinKlasse 2| def self.en_metode # Klassemetoder kan også defineres... 3| puts "en" 4| end 5| class <<self # ... i klassens singleton klasse. 6| def annen_metode 7| puts "annen" 8| end 9| end 10| end 11| 12| MinKlasse.annen_metode #=> "annen" |
Singleton-klasser er et "usynlig" mellomledd mellom et objekt og dets klasse, hvor singleton-metodene havner.
(Forventet du noe om et design pattern kalt Singleton her?
Titt på modulen singleton
i standardbiblioteket.)
"If it walks like duck..."
Ruby benytter seg av en klar distinksjon mellom klasse og type. Duck typing vil si at det meldingene et objekt reagerer på som angir typen, og ikke klassen.
1| def list_filer( sti ) 2| Dir.open( sti ) do |dir| # Dir#open forventer en String, 3| puts dir.entries.join(' ') # men benytter duck typing. 4| end 5| end 6| 7| class KatalogSti < Array 8| end 9| 10| sti = KatalogSti.new 11| sti << "usr" << "local" << "bin" 12| 13| begin 14| list_filer( sti ) # Gir konverteringsfeil: 15| rescue TypeError=>err 16| puts err 17| end 18| 19| class KatalogSti 20| def to_str # to_str er en metode som angir 21| sep = File::SEPARATOR # at et objekt kan benyttes som 22| sep + self.join(sep) # en String. 23| end 24| end 25| 26| list_filer( sti ) # Da kan KatalogSti brukes... |
Vi kan bare arve fra en klasse om gangen. Dermed er ofte moduler et bedre valg for å samle funksjonalitet som deles. Dette gjelder spesielt dersom den delte funksjonaliteten konseptuelt ikke utgjør en superklasse.
1| module NyttigDill 2| def foo; "foo"; end 3| end 4| module NyttigDall 5| def foo; "bar"; end 6| end 7| 8| class DillDall 9| include NyttigDill # Multippel arv av 10| include NyttigDall # funksjonalitet. 11| end 12| puts DillDall.new.foo #=> "bar" 13| 14| class DillDall 15| extend NyttigDill # Utvider klasseobjektet. 16| end 17| puts DillDall.foo #=> "foo" |
For de ganger hvor man ønsker å nøste opp kallstakken og det ikke er et unntak, kan man bruke throw/catch.
1| def som_kaster( level = 0 ) 2| if level > 0 3| som_kaster( level - 1 ) 4| else 5| throw :bunnen_truffet, level 6| end 7| puts level # blir aldri kalt 8| end 9| 10| level = catch(:bunnen_truffet) do 11| som_kaster( 10 ) 12| end 13| puts level |
Continuations omtales ofte som kontrollert goto, men er likevel et vanskelig konsept å vri hjernen rundt.
1| def callcc_eksempel 2| callcc{|cont| 3| puts "Returnerer Continuation" 4| return cont 5| } 6| puts "Avslutter metoden." 7| nil 8| end 9| 10| cont = callcc_eksempel 11| puts "Ferdig?." 12| cont.call if cont |
Et mer komplisert eksempel fra Dave Thomas (med kodemassasje gjort at Avi Bryant)
1| def with_context(params) 2| k, name = catch(:context) {yield; return} 3| k.call(params[name] || find_in_context(name)) 4| end 5| def find_in_context(name) 6| callcc{|k| throw(:context, [k, name])} 7| end 8| 9| def update_widget 10| name = find_in_context(:name) 11| color = find_in_context(:color) 12| puts "<#{color}>#{name}</#{color}>" 13| end 14| 15| with_context( :name => 'dave', :color => 'red' ) do 16| with_context( :name => 'avi', :color => 'green' ) do 17| update_widget #=> "<green>avi</green>" 18| end 19| update_widget #=> "<red>dave</red>" 20| end |