Ruby er et særdeles objekt-orientert språk. Metoder, klasser og objekter er de grunnleggende byggesteinene.
Ruby har verken prosedyrer eller funksjoner; kun metoder som kalles på objekter.
1| # Husker du denne? 2| def si_hei 3| puts "Hei verden!" 4| end 5| 6| # Hva er vel en funksjon uten argumenter? 7| def si_hei_til(hva) 8| puts "Hei #{hva}" 9| end 10| 11| si_hei_til("Bergen!") #=> "Hei Bergen!" 12| 13| # Funksjoner kan ta flere argumenter og de kan ha default verdier 14| def send_julegave(til, fra="nissen") 15| puts "God jul, #{til}. Hilsen #{fra}." 16| end 17| 18| send_julegave("Junior" ) #=> "God jul, Junior. Hilsen nissen." 19| send_julegave("Ola", "far") #=> "God jul, Ola. Hilsen far." |
Du syntes kanskje det så lite objekt-orientert ut? Ikke var metodene definert i noen klasse, og ikke kalte vi dem på noe objekt heller. Det tror du. Alle metoder som defineres på toppnivå defineres i Object-klassen, og vi har implisitt en toppnivå Object-instans. (Prøv self.type
og se selv.)
Ruby returnerer normalt den siste verdien i metoden, hvis ikke return
kalles eksplisitt.
1| def legg_sammen( a, b) 2| a + b # det siste uttrykket returneres 3| end 4| puts legg_sammen(9, 6) #=> 15 5| 6| # fibonacci 7| def fib(i) 8| if i <= 1 9| return 1 # vi kan returnere eksplisitt 10| end 11| return fib(i-1) + fib(i-2) 12| end 13| puts fib(3) #=> 3 14| puts fib(5) #=> 8 |
I tillegg til &
-prefikset som brukes for å pakke en block gitt til en metode inn i et Proc-objekt, brukes *
-prefikset for å samle flere argumenter i en liste (Array).
1| # * prefikset brukes for å pakke argumentlista inn i en Array 2| def list_opp(og_frase, *args) 3| puts args[0..-2].join(", ").capitalize + 4| og_frase + args[-1] + '.' 5| end 6| 7| list_opp(" og ", "epler", "pærer", "bananer") 8| #=> "Epler, pærer og bananer." 9| 10| # eller pakke opp en Array for å bruke elementene som argumenter 11| a = [ " and ", "apples", "pears", "bananas" ] 12| list_opp(*a) #=> "Apples, pears and bananas." |
Som ethvert objekt-orientert språk har Ruby klasser.
1| # En enkel klasse. 2| # Klassenavn må begynne med stor bokstav. 3| class Person 4| # Person.new videresender argumentene til initialize 5| def initialize(etternavn, fornavn, alder = 0) 6| # attributter prefikses med @ 7| @etternavn = etternavn 8| @fornavn = fornavn 9| @alder = alder 10| end 11| 12| # en vanlig instansmetode 13| def to_s 14| "#{@fornavn} #{@etternavn} er #{@alder} år." 15| end 16| end 17| 18| if __FILE__ == $0 # Kun når vi kjører denne filen: 19| p = Person.new("Nordmann", "Ola", 23) 20| puts p #=> "Ola Nordmann er 23 år." 21| end |
Ruby lar deg ikke få tak i et objekts attributter (felter, dataverdier, instansvariable) direkte. Alle attributter er "private". Enhver tilgang fra utsiden til objektet går via metodekall, såkalte get/set metoder.
Instansvariable er "private" på en måte som er mer som protected i andre språk; metoder i sub- og superklasser har tilgang. Men andre instanser har ikke tilgang, selv ikke instanser av samme klasse.
1| # Vi vil bruke Person-klassen videre 2| require_relative 'klasse1' 3| 4| # Klasser er "åpne" skop, og kan enkelt utvides. 5| class Person 6| # Ruby tillater deg ikke å få tak i attributtene 7| # fra utsiden av objektet. Du må gå via metodekall. 8| 9| # get-metode 10| def alder 11| @alder 12| end 13| # set-metode 14| def alder=( ny_alder ) 15| @alder = ny_alder 16| end 17| 18| # tungvint? Jepp, så Ruby har en snarvei: 19| attr_accessor :alder # definerer metodene over automatisk 20| 21| # Vi vil også gjerne kunne lese navnene til personen 22| attr_reader :etternavn, :fornavn 23| 24| end 25| 26| if __FILE__ == $0 # Kun når vi kjører denne filen: 27| p = Person.new( "Nordmann", "Baby" ) 28| p.alder = 3 # Vi setter alderen 29| puts p.alder #=> 3 30| p.alder += 1 # Øk alderen med et år 31| puts p.alder #=> 4 32| puts p.fornavn #=> "Baby" 33| end |
Disse er symboler. (instanser av Symbol
-klassen) De ligner litt på String, men kan ikke endres, de er "internert" og begrenser seg til lovlige navn på klasser, metoder, variabler o.l.
(Symbolet for instansvariabelen @alder
, metoden alder
og den lokale
variabelen alder
er alle sammen :alder
.)
Dette er metoder i Module
-klassen som lager get/set metoder for deg. Som argument tar de symbolene til attributtene du vil lage get/set metoder for.
1| # Fortsetter der vi slapp... 2| require_relative 'klasse2' 3| 4| # Arv - alle studenter er en submengde av alle personer 5| class Student < Person 6| def initialize(etternavn, fornavn, alder = 0, studiested = "NTNU" ) 7| # kall super-klassens versjon av metoden 8| super( etternavn, fornavn, alder ) 9| @studiested = studiested 10| @karakterer = [] # Eventuelt Array.new 11| end 12| 13| # redefinerer Person#to_s metoden 14| def to_s 15| "#{@etternavn}, #{@fornavn} - studerer ved #{@studiested}." 16| end 17| 18| def ta_eksamen(karakter) 19| @karakterer.push karakter 20| end 21| 22| def karaktersnitt 23| sum = 0 24| @karakterer.each do |karakter| 25| sum += karakter 26| end 27| sum.to_f / @karakterer.size 28| end 29| 30| end 31| 32| if __FILE__ == $0 # Kun når vi kjører denne filen: 33| flinkis = Student.new("Einstein", "Al", 128, "Mensa") 34| flinkis.ta_eksamen(1.0) 35| flinkis.ta_eksamen(2.0) 36| puts flinkis #=> "Einstein, Al - studerer ved Mensa." 37| puts flinkis.karaktersnitt #=> 1.5 38| end |
et alias for superklassens versjon av den metoden vi er i nå.
Beklager. Det er ikke lov å la en klasse arve fra mer enn en superklasse i Ruby.
Derimot har Ruby mixin, som kan legge til funksjonalitet fra flere moduler inn i en klasse. Dvs, du kan bare arve fra en klasse, men kan blande inn funksjonalitet i fra flere moduler.
1| class Familie 2| # Vi inkluderer funksjonalitet fra modulen kalt Enumerable. 3| include Enumerable 4| 5| # Enumerable forventer at each metoden yield'er 6| # det som skal itereres over. 7| def each 8| yield "Far" 9| yield "Mor" 10| yield "Sønn" 11| yield "Datter" 12| end 13| end 14| 15| f = Familie.new 16| 17| # include? og sort metodene er mikset inn fra Enumerable. 18| puts f.include?("Sønn") #=> true 19| puts f.sort.join(", ") #=> "Datter, Far, Mor, Sønn" |
Dette tilbyr multippel arv av funksjonalitet.
Klassevariabler er variabler som deles mellom alle instanser av klassen, samt instanser av subklasser. (tilsvarende static variabler i Java)
1| class Bil 2| # En klassevariabel for å telle antall biler i verden. 3| @@num_biler = 0 4| def initialize 5| @@num_biler += 1 6| end 7| def Bil.antall 8| @@num_biler 9| end 10| end 11| 12| class Lada < Bil 13| end 14| 15| class Yugo < Bil 16| def krasj 17| # klassevariabelen er felles for alle instanser av Bil, 18| # samt instanser av subklasser av bil 19| @@num_biler -= 1 20| end 21| end 22| 23| lada = Lada.new 24| yugo = Yugo.new 25| puts Bil.antall #=> 2 26| 27| yugo.krasj 28| puts Bil.antall #=> 1 |
Objekter fødes, brukes og dør. I Ruby dør objektene når de hentes av søppeltømmeren. (garbage collector)
Når det skjer, er usikkert. Ingen referanser til objektet må eksistere og garbage collectoren må startes eksplisitt eller implisitt, som skjer jevnlig.
1| # 2| streng = "Hvil i fred." 3| 4| # Vi gir en block som skal kjøres når streng objektet dør. 5| ObjectSpace.define_finalizer(streng) do |id| 6| puts "Objektet med ID=#{id} er nå dødt. " 7| puts "Rest in peace." 8| end 9| 10| # Starter søppeltømmeren eksplisitt. 11| puts "Henter søppel!" 12| GC.start 13| # Men ingenting skjer, da det ennå er en referanse til strengen. 14| 15| # Prøver en gang til... 16| streng = nil 17| puts "Henter mer søppel!" 18| GC.start 19| # finalizer blocken blir kjørt. |
Legg merke til at objektet allerede er dødt når finalizer-blocken kalles. Ressurser som må lukkes eksplisitt, holdes via bindingen til blocken. (Bare pass på at blocken ikke også holder en referanse til objektet.)
Flere finalizers kan registreres på et objekt.