A Ruby programozási nyelv

Szabványos könyvtárak

A standard osztályokon kívül modulok formájában is rendelkezésünkre áll néhány hasznos kiegészítő szolgáltatás. Pl. dbm adatbázis interface, Kanji kódkonverziót támogató könyvtár :-), a program argumentumainak feldolgozását segítő modulok (getopts, parsearg), van már Tk interface, socketek használatát támogató programkönyvtár… Az egyes szolgáltatások átgondolt felületen keresztül használhatóak. Pl. az eldöntendő kérdésekre választ adó metódusok kérdőjellel végződnek, a rendszerhívásokhoz kapcsolódó metódusok elnevezése a C-ben megszokott nevekkel definiált, a konstruktornak szánt műveletek mindig megtalálhatóak new névvel is stb.

Alap be- és kivitel

Ruby kétféle megoldás kínál a be- és kivitel kezelésére.

Egyszerű interfész

print "Enter your name: " name = gets

Sok be- és kiviteli függvény található a kernel modulban (gets,open, print, printf, putc, readline, readlines), ami egyszerűvé és kényelmessé teszik a Ruby-programok írását. Ezek az eljárások általában a standard inputra és a standard outputra dolgoznak, ami nagyon hasznos lehet szűrők írásánál.
A másik mód, ami sokkal több kontrollt enged meg, az a io-objektum használata.

IO-objektum

A Ruby definiál egy alaposztályt (io), a be- és kivitel kezelésére. Ebből az alaposztályból származik néhány egyéb osztály, mint a File és BasicSocket, amelyek még specializáltabb viselkedést írnak elő, de az elv ugyanaz. Az io-objektum egy kétirányú csatorna egy Ruby- program és néhány külső erőforrás között. [ Azoknak, akik ismerik az implementációs részleteket: ez azt jelenti, hogy egy egyszerű io-objektum egyszerre több rendszer-fájldeszkriptort is tud kezelni. Például ha megnyitunk egy pár pipe-ot akkor az egyszerű io- objektum tartalmaz egy read pipe-ot és egy write pipe-ot. ]

Fájlok megnyitása és lezárása

Egy fájlt létre tudunk hozni File.new utasítás segítségével:

aFile = File.new("testfile", "r") # ... a fájl feldolgozása aFile.close

A File-objektumot létre lehet hozni olvasásra, írásra vagy mindkettőre egyszerre, a módstringnek megfelelően (a példában a testfile-t olvasásra nyitottuk meg az "r" segítségével). Egy fájl létrehozásakor opcionálisan megadhatjuk annak jogait is, ezek a mód- és hozzáférésbitek platformfüggőek. A fájl megnyitása után, máris dolgozhatunk vele, adatokat olvashatunk és írhatunk, ahogy kedvünk tartja. Végül lezárjuk a fájlt, hogy a pufferből minden mentésre kerüljön és az erőforrásokat felszabadítsuk.

Csakhogy a Ruby meg tudja könnyíteni a programozó életét. A File.open metódus szintén megnyit egy fájlt. Szokásos esetben úgy működik, mint a File.new. Azonban ha egy blokk van társítva a híváshoz, akkor az open másként viselkedik. Ahelyett, hogy visszaadna egy új File objektumot, meghívja a blokkot és az újonnan megnyitott File-t átadja neki paraméterként. A blokkból való kilépéskor a File automatikusan lezáródik.

Fájlok írása és olvasása

Az egyszerű I/O -hoz hasonlóan ugyanazok a metódusok elérhetők a fájl-objektum számára. Tehát a gets egy sort olvas a standard inputról és a File.read egy sort olvas az aFile fájl objektumból.
Viszont az I/O-objektumok további metódusokat biztosítanak, amelyek mind kényelmi célokat szolgálnak.

Iterátorok az olvasáshoz

Az adatok olvasásához a szokásos ciklusok mellett Ruby-iterátorokat is használhatunk. Az IO#each_byte meghívja a blokkot az adatfolyam következő 8 bitjével (ebben az esetben egy File típusú objektumból).

aFile = File.new("testfile") aFile.each_byte {|ch| putc ch; putc ?. }

Eredmény:

T.h.i.s. .i.s. .l.i.n.e. .o.n.e. .T.h.i.s. .i.s. .l.i.n.e. .t.w.o. .T.h.i.s. .i.s. .l.i.n.e. .t.h.r.e.e. .A.n.d. .s.o. .o.n.......

IO#each_line meghívja a blokkot a fájl következő sorával. A következő példában az újsor- karaktereket tesszük láthatóvá a String#dump segítségével.

aFile.each_line {|line| puts "Got #{line.dump}" }

Eredmény:

Got "This is line one\n" Got "This is line two\n" Got "This is line three\n" Got "And so on...\n"

Át tudjuk adni az each_line-nak karakterek egy szekvenciáját, mint sorelválasztót és ennek megfelelően tördeli a sorokat, a sorvégződéssel visszatérve minden sor végén. Ezért láthattuk a "\n" karaktert az előző példa kimenetében. A következő példában az "e" karaktert használjuk, sorszeparátorként.

aFile.each_line("e") do |line| puts "Got #{ line.dump }" end

Eredmény:

Got "This is line" Got " one" Got "\nThis is line" Got " two\nThis is line" Got " thre" Got "e" Got "\nAnd so on...\n"

Ha kombináljuk az iterátor-elképzelést és az automatikus blokkzárást, akkor kapjuk az IO.foreach -et. Ez a metódus veszi az I/O nevét, megnyitja olvasásra, meghívja az iterátort minden sorra, majd automatikusan bezárja a fájl-t.

IO.foreach("testfile") { |line| puts line }

Eredmény:

This is line one This is line two This is line three And so on...

Ha úgy jobban tetszik, beolvashatjuk a fájl-t egy tömbbe:

arr = IO.readlines("testfile") arr.length >> 4 arr[0] >> "az első sor"

Ne feledjük azonban, hogy az I/O sosem biztonságos - minden hiba esetén kivételek váltódnak ki, melyek elkapására és megfelelő lekezelésére készen kell, hogy álljunk.

Fájlok írása

Egész idáig vígan hívtuk a puts és a print metódusokat egy régi objektum átadásával és bíztunk abban, hogy a Ruby helyesen teszi a dolgát (ez természetes így van). De mi is történik valójában? A válasz egyszerű. Néhány kivételtől eltekintve a puts-nak és print -nek átadott objektumok stringgé konvertálódnak a to_s metódussal. Ha valamiért a to_s nem ad érvényes stringet, akkor a létrejött string tartalmazza az objektum osztályának nevét és azonosítóját (id) : <ClassName:0x123456> A kivételek egyszerűek. A nil objektum a "nil" stringet adja vissza és a puts -nak átadott tömb olyan, mintha minden elemére külön-külön meghívnánk a puts-ot. Mi van akkor, ha bináris adatokat akarunk írni és nem akarjuk hogy a Ruby elrontsa azokat? Egyszerűen használhatjuk az IO#print-et, átadva neki egy olyan stringet, amely a bináris adatokat tartalmazza. De használhatunk alacsony szintű be- kivitelt is: IO#sysread és IO#syswrite Hogyan kerülnek a stringbe a bináris adatok? Ennek egyik lehetséges módja, hogy a byte-ról bytera beírjuk; a másik az Array#pack használata.

str= "" str << 1 << 2 << 3 [ 4, 5, 6 ].pack("c*")

„De nekem hiányzik a C++ Iostream-je!”

Ahogyan hozzáfűzhetünk egy objektumot egy Array -hez a << operátorral, úgy egy objektumot egy kimeneti iosteam-hez is:

endl = "\n" $stdout << 99 << " red balloons" << endl

Eredmény:

99 red balloons

A << ismét a to_s -t használja az argumentumok stringgé konvertálásához, mielőtt tovább küldené őket.

Hálózati kommunikáció

A Ruby kecses az Internet protokollokkal, legyen az low-level vagy high-level. A Ruby osztályok halmazával áll elő a socket-könyvtárban. Ezek az osztályok hozzáférést biztosítanak a TCP, az UDP, a SOCKS és a Unix domain socketekhez. Ezen kívül a nyelv támogatja a magasabb, alkalmazás szintű protokollokat, mint az FTP, HTTP és így tovább. A könyvár szerverek írásának megsegítésére is tartalmaz osztályokat.

Socket-ek

A socket-ek egy kétirányú kommunikációs csatorna végpontjai. Kommunikálhatnak folyamaton belül, folyamatok között egyazon számítógépen belül, vagy különböző számítógépek között. Természetesen ebben az esetben különálló folyamatok futnak a számítógépeken. A socket-ek különböző típusú csatornák fölött kerültek implementálásra.

Itt egy egyszerű program, amely adatokat kérdez le az Oracle lokális felhasználóról a finger protokoll segítségével.

require 'socket' client = TCPSocket.open('localhost', 'finger') client.send("oracle\n", 0) # 0 means standard packet puts client.readlines client.close

Eredmény:

Login: oracle
Name: Oracle installation
Directory: /home/oracle
Shell: /bin/bash

Magasabb szinten a lib/net könyvtár az application-level protokollok sokaságát támogatja (jelenleg FTP, HTTP, POP, SMTP és telnet ). A Ruby socketkezelő library osztályai ezeknek a használatára egységes felületet biztosítanak.

Például a következő program kilistázza a www.pragmaticprogrammer.com-on található képeket.

require 'net/http' h = Net::HTTP.new('www.pragmaticprogrammer.com', 80) resp, data = h.get('/index.html', nil) if resp.message == "OK" data.scan(/>img src="(.*?)"/) { |x| puts x } end

Eredmény:

images/dot.gif
images/dot.gif
images/dot.gif
images/aafounders_70.jpg
images/pp_cover_thumb.png
images/ruby_cover_thumb.png
images/dot.gif
images/dot.gif

Socket osztályhierarchia

Ősosztálya az IO, belőle származnak a különböző socket osztályok.

IO
└┬BasicSocket
 ├┬IPSocket
 │├┬TCPSocket
 ││└─TCPServer
 │└─UDPSocket
 ├─Socket
 └┬UNIXSocket
  └─UNIXServer

A socketek használatához forráskódban importálni kell a socket library-t, ehhez a következő kód szükséges

require 'socket'

A socketnek saját fogalmi rendszere alakult ki. Itt láthatóak a socketek létrehozásához szükséges paraméterek, rövid leírással és a paraméterek 1-1 lehetséges értékével.

domain paraméter

protokoll családot jelenti, ide tartozik pl. a Socket::PF_INET, Socket::PF_INET6, Socket::PF_UNIX. Ezek a protokoll családok elérhetőek Socket::Constants alatt, a névkonstansok helyett valójában integer számok lesznek a kódba behelyettesítve. A konstansokban a PF prefix jelenti a Protocol Family-t, az utána következő szöveg pedig a protokollt, de a Socket:: akár el is hagyható, ha include-dal include-olásra kerül a Socket::Constants

Példák
PF_INET – Internet Protocol (IP)
PF_INET6 – Internet Protocol v6 (IPv6)
PF_UNIX (vagy PF_LOCAL) – Egy gépen belüli kommunikáció folyamatok között.

type paraméter

A két végpont közötti socket kommunikáció típusát határozza meg.

Értékei
SOCK_STREAM – kapcsolatorientált protokoll (TCP)
SOCK_DGRAM – kapcsolatmentes (UDP)

protocol paraméter

a protokollcsaládban definiált protokoll. 0 az alapértelmezett protokoll az adott családból.

Értékei
Socket::IPPROTO_TCP - TCP az IP fölött
Socket::IPPROTO_UDP – UDP az IP fölött
Socket::IPPROTO_IP – RAW IP kapcsolat

hostname paraméter

a hálózati csatoló azonosító neve, lehet egy szöveges host név, IP cím, IPv6 cím. Üzenetszórás is készíthető a <broadcast> szöveggel vagy az INADDR_BROADCAST konstanssal. Üres szöveggel vagy INADDR_ANY konstanssal bármely csatoló használható.

Értékei
INADDR_BROADCAST - üzenetszórás
INADDR_ANY - bármely csatoló használata

port paraméter

Minden szerver egy (esetleg több) porton figyel. Értéke lehet egy Fixnum vagy egy protokollra jellemző konstans.

IO osztály
Leírását lásd itt.

BasicSocket osztály

Az összes Socket osztály ősosztálya.

BasicSocket osztályszintű metódusai/tulajdonságai

do_not_reverse_lookup tulajdonság

Beállítható, hogy a Socket osztályok létrehozásakor lekérje-e az IP címhez tartozó domain nevet (reverse DNS). Alapértelmezésként mindig lekéri. Ha nem állítjuk át, előfordulhat, hogy az alkalmazás megáll egy időre, mert a DNS lekérdezésekor 15 másodpercet vár a válaszra. Amíg nem érkezik meg, vagy le nem telik az idő, addig az alkalmazás várakozik.

BasicSocket.do_not_reverse_lookup = bool

for_fd(fd) függvény

Egy Socket objektummal tér vissza, ami tartalmazza a paraméterben kapott file leírót (file descriptor)

BasicSocket.for_fd(fd) => basicsocket
BasicSocket példánymetódusai

close_read függvény

Megtiltja a további olvasást a socketről

basicSocket.close_read => nil

close_write függvény

close_read párja, megtiltja a további írást a socketre

basicSocket.close_write => nil

connect_address() függvény

Egy socket címmel tér vissza, amivel a lokális géphez lehet csatlakozni. Ha a lokális címhez nem lehet csatlakozni, SocketError kerül kiváltásra. Pl. az IPv4 és IPv6 címek 0-ás portjaira nem lehet csatlakozni. A Unix domain socketre, ahol nincs megadva path.

basicSocket.connect_address()

local_address függvény

Egy Addrinfo objektummal tér vissza, ami tartalmazza a helyi socket kapcsolódási tulajdonságait

local_address => addrinfo

recv függvény

Üzenet fogadása a socketen keresztül, két paraméter adható meg, a maxlen és a flags. A maxlen megadja a fogadható bájtok maximális számát, a flags pedig bitenként összefűzött (vagy OR-ral összefűzött) Socket::MSG_* konstansokat tartalmaz.

recv(maxlen) => mesg recv(maxlen, flags) => mesg

recv_nonblock függvény

Csak az üzenetsorból tud olvasni, nem blokkolja a folyamatot. A paraméterei megegyeznek a blokkoló recv függvény paramétereivel. Ha nem tud olvasni, IO::WaitReadable típusú kivételt dob.

recv_nonblock(maxlen) => mesg recv_nonblock(maxlen, flags) => mesg

send függvény

A recv függvény párja, üzenet küldhető a socketen keresztül. Három paramétere adható át, ebből kettő kötelező. Paraméterei sorban a mesg, flags és a dest_sockaddr. Mesg paraméterben stringként kell átadni az üzenetet, flags paraméter a fogadáshoz hasonlóan a Socket::MSG_* konstansok közül kell a szükségeseket logaikai éssel összerakni. A dest_sockaddr címzett címe, kapcsolatmentes protokoll esetén.

send(mesg, flags [, dest_sockaddr]) => numbytes_sent

IPSocket osztály

A TCPSocket és az UDPSocket ősosztálya

IPSocket osztályszintű metódusai

getaddress(host) függvény

Megkeresi a hosthoz tartozó ip címet.

IPSocket.getaddress(host) => ipaddress
IPSocket példánymetódusai

addr([reverse_lookup]) függvény

Egy socket példányról visszaad egy tömböt, ami tartalmazza a kapcsolat helyi interfészének következő tulajdonságait: address_family, port, hostname és numeric_address. Ha a paraméter true vagy :hostname, akkor a hostname paraméterben a host nevét kapjuk vissza. Ha false vagy :numeric akkor a hostname-ben ugyan az az érték lesz, mint a numeric_addressben.

ipSocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]

peeraddr([reverse_lookup]) függvény

Ugyan azt az információt adja vissza, mint az előző, de a kapcsolat másik végpontjáról.

ipSocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address]

recvfrom(maxlen[, flags]) függvény

Egy üzenetet fogad a socketről és beteszi egy tömb 0. indexű pozíciójába. 1. pozícióba a küldő ip címe kerül. Egy vagy két paraméterrel hívható a függvény. Maxlen a fogadott bájtok maximális száma, a flags pedig a Socket::MSG_* konstansok közül kerül ki.

ipSocket.recvfrom(maxlen) => [mesg, ipaddr] ipSocket.recvfrom(maxlen, flags) => [mesg, ipaddr]

TCPSocket osztály

Egy TCP socket kliens, szerverhez kapcsolódásra képes. Ősosztálya az IPSocket.

require 'socket' s = TCPSocket.new 'localhost', 6000 while line = s.gets # sorok olvasása a socketről puts line # és azok kiírása end s.close # socket lezárása
TCPSocket osztályszintű metódusai

gethostbyname(hostname) függvény

Megkeresi a host információkat, ha szükséges az internetről is, és egy tömbbel tér vissza, ami tartalmazza az információt.

TCPSocket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list]

new(remote_host, remote_port, local_host=nil, local_port=nil) függvény

Kapcsolat nyitása a remote_host felé a remote_port-on. Ha a másik két paraméter is adott, akkor a kapcsolat létrehozásához felhasználásra kerül.

TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil)

TCPServer osztály

Egy TCP socket szerver, bejövő kapcsolatokat képes fogadni.

Egy egyszerű TCP szerver létrehozása

require 'socket' # szerver bindolása a 2000-es porthoz server = TCPServer.new 2000 loop do client = server.accept # Várakozás kliens csatlakozására client.puts "Hello !" client.puts "Az idő: #{Time.now}" client.close end

Több klienst is kiszolgálni képes szerver

require 'socket' #szerver létrehozása a 6000-es porton server = TCPServer.new 6000 loop do Thread.start(server.accept) do |client| client.puts "Hello !" client.puts "Time is #{Time.now}" client.close end end
TCPServer osztályszintű metódusai

new([hostname,] port) függvény

Létrehoz egy szerver socketet és hozzáköti a porthoz. Ha a hostnév adott, rajta lesz a socket.

TCPServer.new([hostname,] port) => tcpserver
TCPServer példánymetódusai

accept függvény

Egy bejövő kapcsolatra vár, kliens csatlakozásig blokkolódik.

tcpServer.accept => tcpsocket

accept_nonblock függvény

Bejövő kapcsolatra vár, de közben nem blokkol, várakozó klienseket szolgál ki, előtte célszerű beállítani a listen(int)-et legalább 1-re.

tcpServer.accept_nonblock => tcpsocket

listen(int) függvény

Beállítható egy szerveren, hogy hány kliens állhat sorba accept előtt. Csak SOCK_STREAM vagy SOCK_SEQPACKET típus esetén értelmezett.

tcpServer.listen(int) => object

Addrinfo osztály ( Addrinfo < Data )

Socket létrehozásához szükség van cím (address) információkra. A Socket objektum ezt a „struct sockaddr” manipulálásával valósítja meg, ami valójában egy átláthatatlan bináris szöveg. Ruby 1.9.2-es verziójától kezdve a Socket az Addrinfo objektummal is létrehozható, de a régi módszer is megmaradt kompatibilitási okok miatt.

Az Addrinfo magában foglalja a domaint (protocol family), socket típust, protokollt és socket addresst, ezzel a 4 változóval egyértelműen azonosít egy socket végpontot.

A socket address-nek (sockaddr) megvan a saját belső strukturája. Ennek a strukturának a mérete a socket protokoll családjától függ. Pl. egy PF_INET socketben lehet TCP vagy UDP kapcsolat, amihez az IP-cím és a port, míg egy PF_LOCAL sockethez egy útvonal szükséges. Egy sockaddr struktúra létrehozható tömbként vagy bináris szövegként. Általában a tömbös létrehozást használják struktúra létrehozásakor, a bináris szöveg formátum API-ból való visszatérési értékként kapható.

Példaként a PF_INET létrehozásához a következő paraméterek szükségesek
[ protocol family, port, hostname, address ]
Az egyes mezők megfelelnek a fentebb tárgyalt ugyanilyen nevűekkel.

Unix domain sockethez szükséges address a következőképp néz ki

[ protocol family, path ]
a protocol family értéke Socket::PF_LOCAL (de egyszerűen PF_LOCAL vagy LOCAL is lehet)

Egy bináris szöveg formátum így néz ki

> Socket.sockaddr_in(80,"www.ruby-lang.org")
=> "\x02\x00\x00P\xDD\xBA\xB8D\x00\x00\x00\x00\x00\x00\x00\x00"
Addrinfo osztályszintű metódusai

getaddrinfo(nodename, service) függvény

A paraméterekből előállítható összes lehetséges Addrinfo objektumot visszaadja egy tömbben.

Addrinfo.getaddrinfo(nodename, service) => [addr... ]

foreach(nodename, service) függvény

getaddrinfo-t hívja meg és a visszakapott tömbön lefuttattja a {|addr|…} lambdát.

Addrinfo.foreach(nodename, service) {|addr|... } => [addr... ]

ip(host) függvény

egy olyan Addrinfo példánnyal tér vissza, ahol ki van töltve az ip mező.

Addrinfo.ip(host) => addr

Addrinfo.new(sockaddr [,family[, socktype[, protocol]]]) függvény

Új Addrinfo objektum létrehozása a paraméterben megadott értékekből. Hiányos csak a familytől függően lehet.

Addrinfo.new(sockaddr [,family[, socktype[, protocol]]]) => addr

Addrinfo.tcp(host, port) függvény

Egy host-port párossal feltöltött TCP Addrinfo objektumot ad vissza eredményül

Addrinfo.tcp(host, port) => addr

Addrinfo.udp(host, port) függvény

Egy host-port párossal feltöltött UDP Addrinfo objektumot ad vissza eredményül

Addrinfo.udp(host, port) => addr

unix(path, socktype="SOCK_STREAM") függvény

Visszatér egy PF_LOCAL Addrinfo objektummal, amelyben feltöltött az útvonal tulajdonság.

Addrinfo.unix(path, socktype="SOCK_STREAM") => addr
Addrinfo példánymetódusai

igaz-hamis értéket visszaadó függvények

addrinfo.ip?, addrinfo.ipv4?, addrinfo.ipv4_loopback?, addrinfo.ipv4_multicast?, addrinfo.ipv4_private?, addrinfo.ipv6?, addrinfo.ipv6_linklocal?, addrinfo.ipv6_loopback?, addrinfo.ipv6_mc_global?, addrinfo.ipv6_mc_linklocal?, addrinfo.ipv6_mc_nodelocal?, addrinfo.ipv6_mc_orglocal?, addrinfo.ipv6_mc_sitelocal?, addrinfo.ipv6_multicast?, addrinfo.ipv6_sitelocal?, addrinfo.ipv6_unspecified?, addrinfo.ipv6_v4compat?, addrinfo.ipv6_v4mapped?, addrinfo.unix?

tulajdonságok

addrinfo.afamily => integer addrinfo.ip_port => integer addrinfo.pfamily => integer addrinfo.protocol => integer addrinfo.socktype => integer addrinfo.unix_path => string

bind függvény

Hozzáköt egy socketet a belső állapotával reprezentált kapcsolathoz és visszatér egy Socket objektummal. Amennyiben paraméterben kap egy blokkot, akkor a blokkon belül kell a Socket objektumot feldolgozni. Ekkor a blokk végén a kapcsolatot lezárja és a blokk visszatérési értékével tér vissza.

addrinfo.bind => sock addrinfo.bind {|sock|... } => obj

canonname függvény

Névfeloldást végez, de csak akkor, ha az objektum a Socket::AI_CANONNAME beállítással került létrehozásra.

addrinfo.canonname → string vagy nil

connect függvény

Csatlakozik az address objektumban megadott szerverhez és visszatér egy Socket-tel. Ha blokkot is kap paraméterül, akkor a bind-hez hasonlóan a blokkon belül kell a Sockettel dolgozni. Blokk végén a kapcsolat lezárul, a visszatérési érték pedig a blokk visszatérési értéke lesz.

addrinfo.connect => sock addrinfo.connect {|sock|... } => obj

family_addrinfo függvény

Létrehoz egy új Addrinfo objektumot, ugyanolyan family_address-el, de más host/port párossal.

addrinfo.family_addrinfo(*) => new_addr

ip_unpack függvény

Név-, és portfeloldást végez.

addrinfo.ip_unpack => [host, port]

Parancssori argumentumok

Egy Ruby-parancssor három részből áll:

opciók a Ruby-interpreternek
(opcionálisan) a futtatandó program neve
(opcionálisan) a program paraméterei

ruby [ options ] [--] [ programfile ] [ arguments ]

Ha nincs megadva fájl-név vagy az egy "-", akkor a Ruby a program forrását a standard inputról olvassa. A program argumentumai a neve után következnek. Például:

% ruby -w - "Hello World"

Engedélyezzünk a figyelmeztetéseket, a programot a standard inputról olvassuk és átadjuk neki az idézőjelek közti szöveget.

Fontosabb parancssori opciók:

-C directory: megváltoztatja az aktuális munka könyvtárat a futtatás előtt. -c: csak a szintaktikát ellenőrzi, a programot nem futtatja --copyright: a szerzői jogok megjelenítése -d, --debug: a $DEBUG értékét (amelyet a programban hibakereséshez használhatunk) true-ra állítja -I directories: extra modulkönyvtár megadása -h --help: rövid segítséget ad -p: a programkódot a következő ciklusba helyezi: "while gets; ...; print; end." --version: a Ruby verziójának számát jeleníti meg

ARGV

Minden parancssori argumentum a program számára elérhető a globális ARGV< tömbbel. Ha a Ruby-t a következőképpen futtatjuk:

% ruby -w ptest "Hello World" a1 1.6180

az a következő ARGV-hez vezet: ["Hello World", a1, 1.6180] Tehát az ARGV[0] nem a program neve, ahogyan azt C-ben megszokhattuk, hanem az első paraméter. A program neve a $0 globális változóban található.

A program terminálása

A Kernel#exit metódus terminálja a programunkat, visszatérve egy értékkel az operációs rendszer számára. Azonban az exit nemcsak azonnal termináltatja a programot, hanem kivált egy SystemExit kivételt is, amit elkaphatunk és általa jó néhány tisztogatást elvégezhetünk.

Környezetváltozók

Lehetőség van az operációs rendszer környezetváltozóinak elérésére az előre definiált ENV változó segítségével. Néhány környezetváltozót a Ruby első indulásakor beolvas. Ezek a változók az interpreter működését befolyásolják. A Ruby által használt környezetváltozók: Változó neve leírása RUBYOPT további parancssori opciók a Ruby-nak, a valódi parancssori változók után vizsgálja ($SAFE 0 kell, hogy legyen) RUBYLIB további keresési útvonalak a Ruby-programok számára ($SAFE 0 kell, hogy legyen) RUBYPATH a-S opcióval keresési útvonalak a Ruby-programok számára RUBYSHELL a Shell, amelyet programok indítására használjon; ha nincs megadva, akkor SHELL vagy megnézi a COMSPEC-et DLN_LIBRARY_PATH keresési útvonal a dinamikusan betöltött modulok számára RUBYLIB_PREFIX (csak Windowsnál) megcsonkítja a RUBYLIB keresési útvonalat a következő prefix hozzáadásával

Írás környezetváltozókba

Egy Ruby-programnak lehetősége van az ENV objektumba való íráshoz, ami a legtöbb rendszerben a hozzá tartozó környezetváltozó értékét változtatja meg. Azonban ez a változtatás lokális a processz és bármely gyermeke számára. Ha egy alprocessz megváltoztat egy környezetváltozót, akkor ez a változás érzékelhető az általa elindított processzben, de nem érzékelhető az eredeti szülőben. (Ez is azt bizonyítja, hogy a szülő sosem tudja, hogy a gyermekei mit is csinálnak.)

Hol találhatók a Ruby moduljai?

A require vagy a load segítségével tölthetünk be egy modult a Ruby-program számára. Ezek közül néhány a Ruby része (vagy a Ruby Application Archive -ből való, vagy magunk írtuk). De hogyan is találja meg ezeket a Ruby? Amikor a Ruby az adott rendszerre le lett fordítva, akkor néhány standard könyvtárat előre definiáltak. Ezek helye gépfüggő, a parancssorból könnyen megállapítható:

% ruby -e 'puts $:'

Egy tipikus linuxon a következőt kapjuk:

/usr/local/lib/ruby/site_ruby/1.6/i686-linux /usr/local/lib/ruby/site_ruby/1.6 /usr/local/lib/ruby/site_ruby /usr/local/lib/ruby/1.6/i686-linux /usr/local/lib/ruby/1.6

A site_ruby könyvtár az általunk írt modulokat és kiterjesztéseket tárolja. A platformfüggő könyvtárak (ebben az esetben az i686-linux) futtatható állományokat és erre a gépre specifikus dolgokat tartalmaznak. Ezeket a könyvtárakat automatikusan tartalmazza a Ruby modul kereső része, de ez néha nem elegendő. Lehet, hogy egy nagy projekten dolgozunk Ruby alatt és meg akarunk osztani egy már megírt programkönyvtárat. Erre e Ruby több megoldás kínál: Ha a programunk setuid-os, akkor beállíthatjuk a RUBYLIB környezetváltozót egy vagy több könyvtárra (az elválasztó karakter platformfüggő: Windows alatt pontosvessző, Unix alatt kettőspont). Ha a programunk nem setuid-os, akkor használhatjuk a -i parancssori opciót. Végül a $: Ruby változó egy tömb azon helyek listáival, ahol a betöltendő modulokat keressük. Ez a változó a standard könyvtárak, a RUBYLIB környezetváltozó és a -i opció alapján inicializálódik - ehhez aztán a programfutás közben is hozzávehetünk további könyvtárakat.

Egy konkrét példa. Megszámoljuk, hogy egy szöveges fájl szavai hányszor fordulnak elő benne és kiírja a tíz leggyakoribbat:

if !ARGV[0] puts "Használat: ruby #{__FILE__} filenév" exit end szavak = Hash.new(0) IO.foreach(ARGV[0]) do |sor| for szo in sor.split do szavak[szo] += 1 end end szavak = szavak.sort do |x,y|

A program a Perl-ből ismerős asszociatív tömbbel működik. A asszociatív tömböt nemcsak egész számokkal lehet indexelni, mint azt megszokhattuk, hanem stringekkel is. Az asszociatív tömböt a:

szavak = Hash.new(0)

sorral állítjuk elő. A 0 az alapértelmezett paraméter, tehát ha egy szó nem fordul elő a fájlban az 0-szor fordul elő. A programot önmagára lefuttatva a következő eredményt kapjuk:

end : 5 x fordul elő = : 4 x fordul elő szavak : 4 x fordul elő do : 3 x fordul elő <=> : 2 x fordul elő puts : 2 x fordul elő in : 2 x fordul elő for