A Ruby programozási nyelv Gyakorlat

Kovács Gábor

2011. február 15.

Amire szükségünk lesz a gyakorlat során: egy telepített Ruby környezet

1. A Ruby programokat a ruby értelmező futtatja, aminek paramétere a futtatni kívánt szkript fájl.

A Hello, World! fájl felépítése az alábbi kódrészletben látszódik. Az első sornak UNIX rendszereken van jelentősége, ott ugyanis a futtatókörnyezet képes ez alapján kiválasztani fájl értelmezőjét különben ez csak egy komment. A második amúgy szintén komment sor a fájlban használt karakterkódolást specifikálja, mivel az alapértelmezett kódolás az ASCII, ha ékezetes karaktereket akarunk írni, ezt minden egyes Ruby forrásunk elejére be kell szúrnunk. A fájl következő része azon függvénykönyvtárakat adja meg, amelyekből osztályokat kívánunk felhasználni ebben a fájlban. A függvénykönyvtár lehet egy Ruby modul vagy natív kiterjesztés. Ez körülbelül a C #include-nak vagy a Java import-nak felel meg. A fájl értelmezendő része a fájl végéig vagy az __END__sorig tart. Az utóbbi után a fájl még tartalmazhat belső feldolgozásra adatokat.

#!/ usr / bin / ruby −w
# Encoding : UTF−8
r e q u i r e ’ socket ’

__END__

I t t még van valami

A gyakorlaton az irb értelmezőt használjuk a Ruby értelmező demonstrálására. A dokumentációt az ri paranccsal olvashatjuk, amelynek a megtekinteni kívánt osztály (pl. $ri String) vagy metódus nevét (pl. $ri String.length) kell megadnunk.

1http://www.ruby-lang.org/en/downloads/

Írjuk meg mindjárt a szokásor Hello, world-öt. Ruby-ban a visszatérési érték az utolsó állítás, ami az interaktív értelmezőben a => szimbólum után látható.

kovacsg@debian :~ $ i r b
i r b ( main ) :001:0 > puts ’ Hello , ␣ world ’
Hello , world
=> nil
i r b ( main ) :002:0 > " Hello , ␣ world "
=> " Hello , ␣ world "

Literálok Rubyban:

• egész szám

• lebegőpontos szám

• string, amit megadhatunk ’ vagy " szimbólumok között

• reguláris kifejezés

• tömb, ami egy szögletes zárójelek között megadott vesszővel elválasztott lista

• hash, ami kapcsos zárójelek között tartalmaz kulcs-érték párokat => szimbólummal elválasztva

• szimbólum, ami nem más, mint egy lebutított string, és amelyet hash-

ekben előszeretettel alkalmazunk kulcsként

i r b ( main ) :003:0 > 1
=> 1
i r b ( main ) :004:0 > 1.1
=> 1.1
i r b ( main ) :005:0 > " Hello , ␣ world "
=> " Hello , ␣ world "
i r b ( main ) :006:0 > ’ Hello , ␣ world ’
=> " Hello , ␣ world "
i r b ( main ) :007:0 > / [ a−z ] /
=> / [ a−z ] /
i r b ( main ) :008:0 > [ 1 , 1 . 0 , "egy" ]
=> [ 1 , 1 . 0 , "egy" ]
i r b ( main ) :009:0 > h = { 1 => "egy" , ’ ketto ’ => 2.0}
=> {" ketto "=>2.0, 1=>"egy"}

i r b ( main ) :010:0 > h [ 1 ]
=> "egy"
i r b ( main ) :011:0 > : egy
=> : egy

Négyféle azonosítót különböztethetünk meg:

• konstans: nagybetűvel kezdődik

• (lokális) azonosító: kisbetűvel vagy _szimbólummal kezdődik • globális azonosító: $ szimbólummal kezdődik

• példányváltozó azonosító: @ szimbólummal kezdődik A Ruby megkülönbözteti a kis- és nagybetűket.

i r b ( main ) :012:0 > Konstans = 1
=> 1
i r b ( main ) :013:0 > Konstans = 2
( i r b ) : 1 3 : warning : already i n i t i a l i z e d constant

Konstans

=> 2
i r b ( main ) :014:0 > $ g l o b a l i s=" h e l l o "
=> " h e l l o "
i r b ( main ) :015:0 > @a=" h e l l o "
=> " h e l l o "

Egysoros megjegyzést a # szimbólum után tehetünk.

i r b ( main ) :016:0 > # akarmi

Többsoros megjegyzés tételére két módunk van:

i r b ( main ) :018:0 > =begin
i r b ( main ) :019:0= Akarmi
i r b ( main ) :020:0= mert ez egy komment
i r b ( main ) :021:0= =end

#

# Dokumentacio

# #

Egy állítás jellenzően a következő soremelésig tart, kivéve, ha a sor utolsó lexikai eleme egy operátor, amely lehet az üzenetküldés operátor (. vagy ::) is.

i r b ( main ) :022:0 > 1+2
=> 3
i r b ( main ) :023:0 > 1+
i r b ( main ) : 0 2 4 : 0 ∗ 2
=> 3
i r b ( main ) :025:0 > 1
=> 1
i r b ( main ) :026:0 > +2
=> 2
i r b ( main ) :027:0 > 1 \
i r b ( main ) : 0 2 8 : 0 ∗ +2
=> 3
i r b ( main ) :029:0 > "egy" . s p l i t ( ’ g ’ )
=> [ "e" , "y" ]
i r b ( main ) :157:0 > a = 2.4
=> 2.4
i r b ( main ) :158:0 > a ∗∗2.4
=> 8.17536177518463

Függvényt a def kulcsszó után definiálhatunk a függvény azonosítója (27.sor), a formális paraméterlista és a törzs megadásával. A függvény törzse a def-fel egy szinten lévő end-ig tart (29.sor).

i r b ( main ) :027:0 > def f (n)
i r b ( main ) :028:1 > 2∗n
i r b ( main ) :029:1 > end
=> nil

Függvényhívásnál, ami nem más mint egy üzenet küldés egy objektum számára, ameddig a paraméterlista egyértelműen meghatározható, a zárójelek elhagyhatók.

i r b ( main ) :033:0 > f (1+2)+3
=> 9
i r b ( main ) :034:0 > f (1+2)+3
=> 12

Rubyban minden objektum, még az elemi típusok is. Kétféle egész létezik Fixnum és Bignum. A C-ben megszokott operátorkészlet áll itt is rendelkezésre, kiegészítve a hatványozás operátorával (**). Egész számok osztása egész számot ad eredményül, ha az eredmény negatív, akkor az értelmező mínusz végtelen felé kerekít.

i r b ( main ) :033:0 > 2 . class
=> Fixnum
i r b ( main ) :034:0 > 1_000_000_000_000 . class
=> Bignum
i r b ( main ) :035:0 > 1 . 0 . class
=> Float
i r b ( main ) :033:0 > f (1+2)+3
=> 9
i r b ( main ) :034:0 > f (1+2)+3
=> 12
i r b ( main ) :035:0 > 3/2
=>␣1
i r b ( main ) :036:0 > ␣−3/2
=> −2
i r b ( main ) :037:0 > −(3/ 2)
=>␣−1
i r b ( main ) :038:0 > ␣ 3.0 /2
=> 1.5
i r b ( main ) :039:0 > 2∗∗3
=> 8

Egy azonosító által reprezentált értékre #{} szintakszissal a kapcsos zárójelen belül hivatkozhatunk egy idézőjelben lévő stringben. A String sok tekintetben hasonlóan viselkedik, mint egy tömb. A karakterlánc karakterei tömbhozzáféréssel elérhetők, a negatív index a string végéről számol vissza.

i r b ( main ) :049:0 > " S t r i n g " . class
=> S t r i n g
i r b ( main ) :040:0 > $a=’ l o ’
=> " l o "
i r b ( main ) :041:0 > "Hel#{$a}"
=> " Hello "
i r b ( main ) :042:0 > ’ Hel#{$a} ’
=> "Hel\#{$a}"
i r b ( main ) :043:0 > a = "Hel#{$a}"
=> " Hello "
i r b ( main ) :044:0 > a [ 0 ]
=> 72
i r b ( main ) :045:0 > a [ −1]

=> 111

A Ruby tömb tetszőleges típusú értékekből összeállított lista. Gyakori feladat tömb elemeinek összefűzése (ld. PHP implode), illetve string objektumok szétválasztása valamely elválasztó karakterre tekintettel (ld. PHP explode).

i r b ( main ) :046:0 > arr= [ "1" , 1 . 0 , 1 ]
=> [ "1" , 1 . 0 , 1 ]
i r b ( main ) :047:0 > arr ∗2
=> [ "1" , 1 . 0 , 1 , "1" , 1 . 0 , 1 ]
i r b ( main ) :048:0 > arr . j o i n ( ’ : ’ )
=> " 1 : 1 . 0 : 1 "
i r b ( main ) :073:0 > tmp . s p l i t ( " : " )
=> [ "1" , " 2.0 " , " Hello " ]

A hash egy kulcs-érték párokat tartalmazó halmaz, leginkább a PHP asszociatív tömbhöz hasonlít.

i r b ( main ) :049:0 > h
=> {" ketto "=>2.0, 1=>"egy"}
i r b ( main ) :050:0 > h [ 1 ]
=> "egy"
i r b ( main ) :051:0 > h [ 3 ] = 3 . 0
=> 3.0
i r b ( main ) :052:0 > h
=> {" ketto "=>2.0, 1=>"egy" , 3=>3.0}

Speciális lexikai elem a range, amely egész vagy karakter literálok egymás utáni elemeiből álló halmaz. A két ponttal definiált range a jobb oldali elemet is tartalmazza, a három ponttal definiált range a jobb oldali elemet már nem tartalmazza.

i r b ( main ) :053:0 > r = ’ a ’ . . ’ f ’
=> "a" . . " f "
i r b ( main ) :055:0 > r . each { | l | p r i n t "␣#{l }␣" }
a b c d e f => "a" . . " f "
i r b ( main ) :056:0 > r . each { | l | p r i n t l }
abcdef=> "a" . . " f "

Két boolean literál létezik a true és a false. A nil a nem definiált pointernek felel meg. A false nil-lé konvertálódik boolean kifejezésekben.

i r b ( main ) :089:0 > true

=> true
i r b ( main ) :090:0 > false
=> false
i r b ( main ) :091:0 > nil
=> nil

A Ruby kétféle vezérlési szerkezetet nyújt a programkód feltételes elágaztatására, amelyek csak szintakszisukban térnek el a C-ben megismertektől: if/unless, illetve case. Az if szerkezet formálisan:

if <feltetel> then
 <blokk>
{elsif <feltetel> then <blokk>}*
[else <blokk>]
end

. Az if feltételének negálása helyett használható az unless. Az if blokkja kiemelhető a sor elejére. Többszörös elágazást a case szerkezettel hozható létre:

case <objektum>
{when <kifejezes> <blokk>}+
[else <blokk>]
end

rb ( main ) :059:0 > a = 2
=> 2
i r b ( main ) :060:0 > i f a >1 then puts "nagy" e l s i f a==1
then puts "egy" else puts " k i c s i " end

nagy

=> nil
i r b ( main ) :061:0 > unless a==1 then puts "nemegy" end

nemegy

=> nil
i r b ( main ) :062:0 > case a
i r b ( main ) :063:1 > when 1
i r b ( main ) :064:1 > p r i n t "egy"
i r b ( main ) :065:1 > when 2 . . 1 0
i r b ( main ) :066:1 > p r i n t "a␣tartomanyba␣ e s i k "
i r b ( main ) :067:1 > end
a tartomanyba e s i k=> nil
i r b ( main ) :068:0 > a = 1 i f a==2

=> 1
i r b ( main ) :069:0 > case a
i r b ( main ) :070:1 > when 0
i r b ( main ) :071:1 > p r i n t " n u l l a "
i r b ( main ) :072:1 > when 2 . . 1 0
i r b ( main ) :073:1 > p r i n t "a␣tartomanyba␣ e s i k "
i r b ( main ) :074:1 > else
i r b ( main ) : 0 7 5 : 1 ∗ p r i n t "egy"
i r b ( main ) :076:1 > end
egy=> nil

Kétféle ciklus áll rendelkezésre: a while/until, ami megfelel a C while ciklusának, illetve a for, ami egy halmaz összes elemére hajtja végre a belső blokkot.

i r b ( main ) :077:0 > i = 1
=> 1
i r b ( main ) :078:0 > while i < 3 do p r i n t "#{i=i +1}" end
23=> nil
i r b ( main ) :079:0 > i = 1
=> 1
i r b ( main ) :080:0 > until i > 3 do p r i n t "#{i=i +1}" end
234=> nil
i r b ( main ) :081:0 > for i in 1 . . 3 do p r i n t "#{i }" end
123=> 1 . . 3
i r b ( main ) :082:0 > i
=> 3
i r b ( main ) :083:0 > for i in 1 . . 3 do p r i n t i end
123=> 1 . . 3

Blokkot kétféle szintakszissal definiálhatunk: vagy do-end párok között vagy kapcsos zárójelekben. A blokkoknak speciális szerepük is lehet Rubyban. Függvényhívásoknak paraméterül adható egy procedurális blokk, ami akkor hívódik meg, ha a függvény törzse anonim esetben yield, nevesített esetben block.call sorhoz érkezik (a block itt a blokk nevesített azonosítója).

Ay 55-56. sorokban bemutatott range típus each metódusa is ilyen. A 86-89. sorokban a t metódus definíciója kétszer hívja meg a metódushívás paramétereként a 90. sorban átadott blokkot. A blokkban definiált procedúra paraméterezhető, ahogy azt a 91-93. sorok mutatják. A 94. sorban meghívjuk a 137. sorban definiált t azonosítójú metódust egy string paraméterrel. A metódus törzsében, vagyis a 92. sorban átadja a vezérlést a 94. sor blokkja törzsének átadva az a értéket. A 94. sor procedúrája a paraméterül kapott értékre a lokális l azonosítóval hivatkozik, ami a törzsben felhasználható. A törzs végével a vezérlést visszakapja a hívott metódus, vagyis a végrehajtás a 93. sorban folytatódik.

i r b ( main ) :084:0 > r
=> "a" . . " f "
i r b ( main ) :085:0 > r . each { | l | p r i n t "␣#{l }␣" }
a b c d e f => "a" . . " f "
i r b ( main ) :086:0 > def t
i r b ( main ) :087:1 > yield
i r b ( main ) :088:1 > yield
i r b ( main ) :089:1 > end
=> nil
i r b ( main ) :090:0 > t { p r i n t "a" }
aa=> nil
i r b ( main ) :091:0 > def t ( a )
i r b ( main ) :092:1 > yield a
i r b ( main ) :093:1 > end
=> nil
i r b ( main ) :094:0 > t ( ’ h e l l o ’ ) { | l | p r i n t l }
h e l l o=> nil

Ruby-ban az azonosítók referenciaként viselkednek. Típusuk nem deklaráció során, hanem az első használatkor dől el. Ha egy objektumhoz több referenciát rendelünk, akkor annak értéke referencián keresztül megváltoztatható. Az objektumok egyenlősége vizsgálható érték szerint (==) és referencia szerint (eql?) metódus.

i r b ( main ) :096:0 > a = ’ h e l l o ’
=> " h e l l o "
i r b ( main ) :097:0 > b = a
=> " h e l l o "
i r b ( main ) :098:0 > a . r e v e r s e !
=> " o l l e h "
i r b ( main ) :099:0 > a
=> " o l l e h "
i r b ( main ) :100:0 > b
=> " o l l e h "
i r b ( main ) :101:0 > a . r e v e r s e
=> " h e l l o "
i r b ( main ) :102:0 > a

=> " o l l e h "
i r b ( main ) :103:0 > a = b = c = 0
=> 0
i r b ( main ) :104:0 > a =b
=> true
i r b ( main ) :105:0 > a = ’ l o ’
=> " l o "
i r b ( main ) :106:0 > a =b
=> false
i r b ( main ) :107:0 > a = 1
=> 1
i r b ( main ) :108:0 > b = 1.0
=> 1.0
i r b ( main ) :109:0 > a = b
=> true
i r b ( main ) :110:0 > a . e q l ? b
=> false

Az objektumok konvertálhatók más típusúvá. A konverzió történhet automatikusan, mint például a 37. vagy esetleg az 57. sorban történik, vagy explicit módon a to_s, to_i vagy to_f metódusokkal.

i r b ( main ) :111:0 > a . to_s
=> "1"
i r b ( main ) :112:0 > b = a . to_s
=> "1"
i r b ( main ) :113:0 > b . to_i
=> 1
i r b ( main ) :114:0 > b . to_f
=> 1.0
i r b ( main ) :115:0 > b : : to_f
=> 1.0

Mivel egy azonosító bármilyen típus jelenthet, nem lehetünk mindig biztosak abban, hogy az adott objektumra vonatkozóan egy-egy metódus értelmezette. Ekkor futás közben a respond_to? metódussal állapítható meg, hogy kaphatunk-e választ egy hívásra vagy sem. Fejlesztési időben ugyanebben a dokumentáció segíthet nekünk, amelyet az ri paranccsal érünk el konzolon,

például ri String.

i r b ( main ) :116:0 > 1.+2
=> 3
i r b ( main ) :117:0 > 1 . class

=> Fixnum
i r b ( main ) :118:0 > b . respond_to ? ’+’
=> true

A Ruby programnyelv objektumorientált, építőkockái az osztályok és a modulok, amelyek egymással a nyilvános felületükön definiált metódusaikkal kommunikálnak egymással. A 187. sorban a Math modul sqrt metódusát hívjuk meg, a híváskor a . vagy a :: operátort használhatjuk. Az üzenetek egymásba ágyazhatóak, lásd a 154. sort.

i r b ( main ) :187:0 > Math : : s q r t 4
=> 2.0
i r b ( main ) :154:0 > A. new . a=3
=> 3

Az osztály közös tulajdonságokkal és viselkedéssel bíró objektumokról képez mintát. A Ruby osztály egységbe zárja a viselkedést (metódusok) és a tulajdonságokat (attribútumok), bár az utóbbiak nem érhetők el az osztály példányának referenciáján keresztül.

A 119-123. sorban egy osztályt definiálunk, amelynek m metódusa összeadja a két paraméterül adott számot. A 124. sorban létrehozunk egy példányt az implicit ősosztályból, vagyis az Object-ből módon örökölt new metódussal, a 125. sorban meghívjuk az m metódus két paraméterrel.

i r b ( main ) :119:0 > class A
i r b ( main ) :120:1 > def m( p1 , p2 )
i r b ( main ) :121:2 > p1+p2
i r b ( main ) :122:2 > end
i r b ( main ) :123:1 > end
=> nil
i r b ( main ) :124:0 > a = A. new
=> #<A:0 x7fe0f8e41d90 >
i r b ( main ) :125:0 > a .m 2 ,3
=> 5

A kezdeti viselkedést az initialize metódus (felül)definiálásával határozhatjuk meg. A 130-132. sorokban az A osztály a azonosítójú példányváltozóját 2-re állítjuk be. A a példányváltozót nem kell külön deklarálnunk, az az első hivatkozás hatására létrejön. A a példányváltozó nem férhető hozzá kívülről, azonban az osztályon belül használható, ahogy azt a 127-129. sorban felüldefiniált m metódusban megtesszük.

Ha egy osztálydefiníció során egy már létező osztály azonosítóját adjuk meg, akkor az a definíció kibővíti vagy felüldefiniálja az osztály viselkedését.

A 130-132. sorban bővítjük, a 127-129. sorban felüldefiniáljuk a viselkedést.

i r b ( main ) :126:0 > class A
i r b ( main ) :127:1 > def m( p1 , p2 )
i r b ( main ) :128:2 > p1+p2+@a
i r b ( main ) :129:2 > end
i r b ( main ) :130:1 > def i n i t i a l i z e
i r b ( main ) :131:2 > @a =2
i r b ( main ) :132:2 > end
i r b ( main ) :133:1 > end
=> nil
i r b ( main ) :134:0 > a = A. new
=> #<A:0 x7fe0f8e12e50 @a=2>
i r b ( main ) :135:0 > a .m 2 ,3
=> 7

A példányváltozókhoz setter és getter metódusokkal férhetünk hozzá. A setter jellemzője, hogy a változó azonosítója mögé egy egyenlőségjelet írunk (137-139. sor), a getter pedig maga a változó azonosítója (140-142. sor). Használatukat a 144. és a 145. sor mutatja.

i r b ( main ) :136:0 > class A
i r b ( main ) :137:1 > def a=( val )
i r b ( main ) :138:2 > @a = val
i r b ( main ) :139:2 > end
i r b ( main ) :140:1 > def a
i r b ( main ) :141:2 > @a
i r b ( main ) :142:2 > end
i r b ( main ) :143:1 > end
=> nil
i r b ( main ) :144:0 > a . a=1
=> 1
i r b ( main ) :145:0 > a . a
=> 1

A Ruby egyszerűbb módot is nyújt a setterek, getterek létrehozására. Az attr_accessor mind a settert, mind a gettert automatikusan létrehozza a paraméterül adott szimbólum string reprezentációjának megfelelő azonosítóhoz, az attr_reader csak a gettert, az attr_writer csak a settert hozza létre. Az üzenetek létrejöttének igazolását, illetve hiányát a 151. és a 152. sor mutatja.

i r b ( main ) :146:0 > class A

i r b ( main ) :147:1 > attr_accessor : c
i r b ( main ) :148:1 > attr_reader : d
i r b ( main ) :149:1 > attr_writer : e
i r b ( main ) :150:1 > end
=> nil
i r b ( main ) :151:0 > a . c=2
=> 2
i r b ( main ) :152:0 > a . f=2
NoMethodError : undefined method ‘ f=’ ␣ f o r ␣#<A: 0
x7fe0f8e12e50 ␣@a=1,␣@c=2>
␣␣␣␣␣␣␣␣from␣ ( i r b ) :152
␣␣␣␣␣␣␣␣from␣ : 0

Metódust definiálhatunk egy-egy példányra specializálva is, ekkor az a metódus szingletonnak nevezzük. A 160-162. sorban az a példányra vonatkozóan definiálunk egy metódust, ami az A osztály más példányára már nem hozzáférhető, és elfedi az osztály azonos nevű metódusát.

i r b ( main ) :160:0 > def a .m ( val )
i r b ( main ) :161:1 > @a = val
i r b ( main ) :162:1 > end
=> nil
i r b ( main ) :163:0 > a .m 2 ,3
ArgumentError : wrong number of arguments (2 for 1)
from ( i r b ) : 1 6 3 : in ‘m’
␣␣␣␣␣␣␣␣from␣ ( i r b ) :163
␣␣␣␣␣␣␣␣from␣ : 0

Ruby-ban is definiálhatók osztálymetódusok (248-250.sor), amelyek az osztály példányainak létezése nélkül is meghívhatók (252.sor), illetve közösek az összes példányra vonatkozóan.

i r b ( main ) :247:0 > class A
i r b ( main ) :248:1 > def A.m3
i r b ( main ) :249:2 > " h e l l o "
i r b ( main ) :250:2 > end
i r b ( main ) :251:1 > end
=> nil
i r b ( main ) :252:0 > A.m3
=> " h e l l o "

A Ruby egyik kellemes tulajdonsága a hash, amelyet metódus formális paramétereként felhasználva a formális paramétereket opcionálissá tehetjük, valamint tetszés szerinti sorrendben adhatjuk meg őket. Ez a metódus definíciója során többletmunkát igényel, viszont megkönnyíti a használatot. A 181-186. sorban egy ilyen definíciót látunk. A metódus paraméterét mint hash objektum kezeljük, amelynek az :n szimbólummal jelölt értékét hozzárendeljük az n lokális változóhoz, vagy ha az :n szimbólum nem szerepel a hívás argumentumai között mint kulcs, akkor 0-ra inizicializáljuk. A másik két lokális változó definíciója hasonlóképp történik. A hash argumentummal definiált metódusok hívására a 193-194. sor mutat példát.

A :azonosito egy speciális lexikai elem Ruby-ban, ún. szimbólum, ami egy konstans String-et takar. A szimbólumok és a stringek kölcsönösen átalakíthatók egymásba.

i r b ( main ) :180:0 > class A
i r b ( main ) :181:1 > def m3( a )
i r b ( main ) :182:2 > n = a [ : n ] | | 0
i r b ( main ) :183:2 > m = a [ :m] | | 1
i r b ( main ) :184:2 > l = a [ : l ] | | 0
i r b ( main ) :185:2 > n+m+l
i r b ( main ) :186:2 > end
i r b ( main ) :187:1 > end
=> nil
i r b ( main ) :188:0 > a = A. new
=> #<A:0 x7fe0f8e51880 @a=2>
i r b ( main ) :193:0 > h= { : n=>3, : l =>4}
=> { : l =>4, : n=>3}
i r b ( main ) :194:0 > a .m3 h
=> 8

Az osztálydefiníció metódusai alapértelmezés szerint nyilvánosak (public), vagyis bármely példányon keresztül elérhetők. A Ruby két másik láthatósági szintet is definiál, a protected csak az adott osztály, illetve a leszármazott osztályok számára hozzáférhető, míg a private csak az adott osztályban használható. Az oszlálydefinícióban a láthatósági módósítók közötti blokkban lévő összes metódus az aktuális láthatósággal bír.

class A
def i n i t i a l i z e
@a = 2

end

def m( p1 , p2 )
p1+p2+@a

end protected

def a

@a end

p r i v a t e

def a=( val )
@a = val

end

end

Az osztályok a < operátorral specializálhatók. A leszármazott osztály kiegészítheti vagyis specializálhatja, illetve felüldefiniálhatja az ősosztály viselkedését. A 212-216. sorban a B osztályt definiáljuk, amely rendelkezik az A oszltály összes metódusával, illetve példányváltozójával. A 213-215. sor felüldefiniálja az a attribútum setter metódusát, így az másképp fog viselkedni, mint azt a 144. sorban láttuk.

i r b ( main ) :207:0 > class B < A
i r b ( main ) :208:1 > end
=> nil
i r b ( main ) :212:0 > class B <A
i r b ( main ) :213:1 > def a=( val )
i r b ( main ) :214:2 > @a = val ∗2
i r b ( main ) :215:2 > end
i r b ( main ) :216:1 > end
=> nil
i r b ( main ) :285:0 > b . a=2
=> 2
i r b ( main ) :286:0 >
i r b ( main ) : 2 8 7 : 0 ∗ b . a
=> 4

Metódusokhoz hasonlóan operátorok is (felül)definiálhatók egy oszlályon belül (224-226. sor), mivel az operátorok önmaguk is metódushívások, lásd 228. sor.

i r b ( main ) :223:0 > class A
i r b ( main ) :224:1 > def +( val )
i r b ( main ) :225:2 > @a+val
i r b ( main ) :226:2 > end
i r b ( main ) :227:1 > end

=> nil
i r b ( main ) :228:0 > a.+2
=> 4

A Ruby másik egysége az osztály mellett a modul. A modul akárcsak az osztály egységbe zár attribútumokat és metódusokat. Modulba olyan metódusokat helyezhetünk el, amelyek több osztályra vonatkozóan közösek. A modul rokonságot mutat a Java interfész fogalmával, azzal a különbséggel, hogy a modul nemcsak deklarál egy bizonyos viselkedést a megvalósító osztály számára, hanem mindjárt specifikálja is.

A 229-236. sorok egy modult definiálnak egy példányváltozóval és egy setter-getter párral. A modulban definiált metódusokkal bármely osztály viselkedését kibővíthetjük (237. sor). Egy osztály tetszőleges számú modult integrálhat magába.

i r b ( main ) :229:0 > module Szin
i r b ( main ) :230:1 > def s z i n =( val )
i r b ( main ) :231:2 > @szin=val
i r b ( main ) :232:2 > end
i r b ( main ) :233:1 > def s z i n
i r b ( main ) :234:2 > @szin
i r b ( main ) :235:2 > end
i r b ( main ) :236:1 > end
=> nil
i r b ( main ) :237:0 > class A
i r b ( main ) :238:1 > i n c l u d e Szin
i r b ( main ) :239:1 > end
=> A
i r b ( main ) :240:0 > a = A. new
=> #<A:0 x7fe0f8ead338 @a=2>
i r b ( main ) :241:0 > a . s z i n=’ kek ’
=> "kek"

A modulok emellett lehetővé teszik az esetleges osztálynév-ütközések elkerülését, névtereket definiálhatunk velük. Ekkor az összetartozó osztályok definícióját a modul definícióján belül helyezzük el.