Ruby er et særdeles objekt-orientert språk. Metoder, klasser og objekter er de grunnleggende byggesteinene.
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("Lutvann!") #=> "Hei Lutvann!" 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." |
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 blokk gitt til en metode inn i et Proc-objekt, brukes *
-prefikset for å samle flere argumenter i en 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 'klasse1.rb' 3| 4| class Person # Klasser er "åpne" skop, og kan enkelt utvides. 5| 6| # get-metode 7| def alder 8| @alder 9| end 10| # set-metode 11| def alder=( ny_alder ) 12| @alder = ny_alder 13| end 14| 15| # tungvint? Jepp, så Ruby har en snarvei: 16| attr_accessor :alder # definerer metodene over automatisk 17| 18| # Vi vil også gjerne kunne lese navnene til personen 19| attr_reader :etternavn, :fornavn 20| 21| end 22| 23| if __FILE__ == $0 # Kun når vi kjører denne filen: 24| p = Person.new( "Nordmann", "Baby" ) 25| p.alder = 3 # Vi setter alderen 26| puts p.alder #=> 3 27| p.alder += 1 # Øk alderen med et år 28| puts p.alder #=> 4 29| puts p.fornavn #=> "Baby" 30| 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.
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| require 'klasse2.rb' # Fortsetter der vi slapp... 2| 3| # Arv - alle studenter er en submengde av alle personer. 4| class Student < Person 5| def initialize( etternavn, fornavn, alder = 0, studiested = "NTNU" ) 6| # kall super-klassens versjon av metoden 7| super( etternavn, fornavn, alder ) 8| @studiested = studiested 9| @karakterer = Array.new # Eventuelt [] 10| end 11| 12| # redefinerer Person#to_s metoden. 13| def to_s 14| "#{@etternavn}, #{@fornavn} - studerer ved #{@studiested}." 15| end 16| 17| def ta_eksamen( karakter ) 18| @karakterer.push karakter 19| end 20| 21| def karaktersnitt 22| sum = 0 23| @karakterer.each{ |karakter| 24| sum += karakter 25| } 26| sum.to_f / @karakterer.size 27| end 28| 29| end 30| 31| if __FILE__ == $0 # Kun når vi kjører denne filen: 32| flinkis = Student.new("Einstein", "Al", 128, "Mensa") 33| flinkis.ta_eksamen( 1.0 ) 34| flinkis.ta_eksamen( 2.0 ) 35| puts flinkis #=> "Einstein, Al - studerer ved Mensa." 36| puts flinkis.karaktersnitt #=> 1.5 37| 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 6| # itereres over innholdet. 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" |
I kontrast til Java tilbyr dette multippel arv av funksjonalitet. Java bruker interface til å "etterligne" multippel arv, men tilbyr ikke arv av implementasjon og løser noe som er et ikke-problem når man har dynamisk typing.
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| # En klassemetode (eller singleton-metode) 8| def Bil.antall 9| @@num_biler 10| end 11| end 12| 13| class Lada < Bil 14| end 15| 16| class Yugo < Bil 17| def krasj 18| # klassevariabelen er felles for alle instanser av Bil, 19| # samt instanser av subklasser av bil 20| @@num_biler -= 1 21| end 22| end 23| 24| lada = Lada.new 25| yugo = Yugo.new 26| puts Bil.antall #=> 2 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, f.eks. når det begynner å bli lite ledig minne.
1| streng = "Hvil i fred." 2| 3| # Vi gir en block som skal kjøres når streng objektet dør. 4| ObjectSpace.define_finalizer(streng){|id| 5| puts "Objektet med ID=#{id} er nå dødt. " 6| puts "Rest in peace." 7| } 8| 9| # Starter søppeltømmeren eksplisitt. 10| puts "Henter søppel!" 11| GC.start 12| # Men ingenting skjer, da det ennå er en referanse til strengen. 13| 14| # Prøver en gang til... 15| streng = nil 16| puts "Henter mer søppel!" 17| GC.start 18| # finalizer blocken blir kjørt. |
Legg merke til at objektet allerede er dødt når finalizer-blokken kalles. Ressurser som må lukkes eksplisitt, holdes via bindingen til blokken. (Bare pass på at blokken ikke også holder en referanse til objektet.)
Flere finalizere kan registreres på et objekt.