30. ledna byla oznámena verze 1.9.1 oficiálního interpretu jazyka Ruby, první stabilní verze série 1.9. Tato zpráva představila jeden z nejvíce zlomových okamžiků ve vývoji Ruby za poslední roky. Pojďme se teď v rámci našeho seriálu stručně zorientovat v situaci a podívat se, na jaké změny je třeba se připravit a jaké nové vlastnosti mohou vývojáři využívat.

Začněme historickým ohlédnutím. Japonský vývojář Yukihiro Matsumoto – známý komunitě jako Matz – vytvořil první verzi Ruby již v polovině devadesátých let. Mimo Japonsko se jazyk začal rozšiřovat až v novém tisíciletí. Jeho popularita však rychle rostla a s větším rozšířením a častějším nasazením na větších projektech začalo být zřejmé, že Matzův interpret (označovaný jako MRI – Matz’s Ruby Interpreter) má v některých směrech značný prostor pro zlepšování. Nejčastější připomínky se týkaly a týkají (platí pro MRI do série 1.8.x) následujících oblastí:

  • Výkon – Ruby, podobně jako ostatní interpretované univerzální jazyky, nemá rychlost provádění programu jako prioritu. Nicméně MRI využívá poměrně málo efektivní přímou práci s abstraktním syntaktickým stromem. Bylo zřejmé, že využití nějaké formy bytového kódu a virtuálního stroje by poskytlo podstatné zvýšení výkonu.
  • Podpora lokalizace a internacionalizace – MRI sice díky svému původu nabízí podporu pro japonštinu, ale zejména pro webové projekty je brzdou chybějící vestavěná podpora pro řetězce v Unicode.
  • Podpora vláken (threads) – MRI podporuje vlákna, která jsou ovšem spravována na úrovni interpretu (někdy bývá tento způsob označován jako green threads). To na jedné straně umožňuje vysokou přenositelnost takového řešení, ale vylučuje to například možnost, aby každé vlákno běželo na jiném procesoru. Také výkon s využitím nativního řešení příslušného operačního systému by byl vyšší.

V posledních letech vzniklo několik projektů s cílem vyvinout kompatibilní interpret, který by některé z těchto problémů řešil. MRI přitom slouží jako de-facto standard Ruby, protože neexistuje formální specifikace jazyka. Z nejznámějších pokusů uveďme:

  • Rubinius (http://rubini.us/) – důležitým výhonkem tohoto projektu je snaha vytvořit specifikaci jazyka RubySpec (http://rubyspec.org/)
  • JRuby (http://jruby.codehaus.org/) – do velké míry kompletní a výkonný interpret Ruby pro JVM
  • IronRuby (http://www.ironruby.net/) – interpret pro platformu .NET
  • YARV (http://www.atdot.net/yarv/) – bytekódově orientovaný virtuální stroj Ruby, který byl vybrán jako základ pro Ruby 1.9+ (kdysi se hovořilo o Ruby založeném na VM jako o verzi 2.0, nakonec ale zůstalo číslování zatím u jedničkové řady)

Jak již bylo naznačeno v seznamu, byl do oficiálního vývoje MRI zahrnut projekt YARV a na jeho základě byla vytvořena série 1.9. Ta má za primární cíl odstranění výše uvedených nedostatků. Stručná charakteristika verze 1.9.1 je tedy následující:

  • Využívá bytekódově orientovaný virtuální stroj a dosahuje v průměru minimálně dvojnásobné rychlosti oproti verzi 1.8. (Porovnání rychlosti různých implementací lze nastudovat na http://antoniocangiano.com/2008/12/09/the-great-ruby-shootout-december-2008/.)
  • Třída String a IO třídy podporují kódování (per instanci) včetně Unicode.
  • Zavádí podporu pro nativní vlákna. (Původní koncept vláken realizovaných v rámci interpretu zůstává zachován pod jiným názvem.)
  • Integruje balíčkovací systém RubyGems (http://www.rubygems.org/).
  • (A aby to nebyla samá pozitiva:) Zavádí změny v jazyce, které sice nejsou dramatické, ale přeci jen vedou k určité nekompatibilitě s předchozími verzemi.

Protože valná většina široce používaných knihoven není zatím uzpůsobena pro použití s verzí 1.9.1, není aktuální nasazení nejnovějšího interpretu v produkčním prostředí příliš reálné. Na druhé straně je na čase začít testovat kompatibilitu aplikací a experimentovat s novými vlastnostmi jazyka. Novou verzi získáme na obvyklé adrese http://www.ruby-lang.org/en/downloads/. Bezkonfliktní instalaci nové verze Ruby v unixovém prostředí souběžně s verzí 1.8.x zabezpečíme pomocí přípony. Na mé stanici s FreeBSD to probíhalo následovně.

fetch ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p0.tar.bz2
tar -xvf ruby-1.9.1-p0.tar.bz2
cd ruby-1.9.1-p0
./configure –prefix=/usr/local –program-suffix=1.9
make
sudo make install
ruby1.9 -v

Alternativně můžeme naopak pomocí volby prefix nainstalovat nové Ruby do libovolného adresáře a pak nastavit cestu do podadresáře bin. Výhodou je v tomto případě, že postačí běžná uživatelská oprávnění. V prostředí MS Windows můžete stáhnout předkompilovaný balíček (klasický zip). Ten rozbalíte do zvoleného adresáře a opět zbývá nastavit podadresář bin na začátek proměnné PATH.

Pojďme se podrobněji podívat na změny ve třídě String, které jsou v českých podmínkách jistě zajímavé. Navíc by měla být čtenáři této série již důvěrně známá. Podrobnější popis dalších změn a inovací bude v některých z příštích dílů. Začněme krátkým testovacím prográmkem.

#!ruby1.9
# encoding: UTF-8
puts __ENCODING__
puts ‚–‚
str = ‚Příliš žluťoučký kůň‘
puts str.encoding.name
puts ‚–‚
asc = str.encode(‚ISO-8859-2‘)
puts asc.encoding.name
puts asc
puts ‚–‚
me = File.open($0)
puts me.external_encoding.name
me.close
puts ‚–‚
me = File.open($0, ‚r:UTF-8‘)
puts me.external_encoding.name
me.close

Tento krátký program ukazuje, že zdrojový kód Ruby nyní může být pomocí direktivy v komentáři označen požadovaným kódováním. Hodnota je uložena v konstantě __ENCODING__. Dále následuje řetězcový literál, který obsahuje diakritiku. Program vypíše název kódování tohoto řetězce. Protože literál je ve zdrojovém kódu, bude toto kódování shodné se zdrojovým kódem (UTF-8). Další úsek převede kódování řetězce na standard obvyklý unixovém prostředí. Jelikož mám na toto kódování nastaven terminál, na kterém jsem program testoval, potřeboval jsem tuto konverzi, abych mohl korektně vypsat obsah řetězce.

Další část programu se zabývá načítáním souboru. Pro jednoduchost načítá program sám sebe a vypisuje název kódování, které je tentokrát označeno jako externí (oproti internímu z vlastního zdrojového kódu). Jak uvidíme vzápětí na výpisu, je kódování nastaveno opět podle prostředí terminálu na ISO. To však není správně a proto poslední část programu ukazuje, jak nastavit požadované kódování načítaného souboru. Po spuštění na mém terminálu dopadl výpis takto:

~$ ruby1.9 enctest.rb
UTF-8

UTF-8

ISO-8859-2
Příliš žluťoučký kůň

ISO-8859-2
ara:~$ ruby1.9 enctest.rb
UTF-8

UTF-8

ISO-8859-2
Příliš žluťoučký kůň

ISO-8859-2

UTF-8
~$

Možná si vzpomenete, že v Ruby do verze 1.8.x je řetězec sekvencí bytů. Viz následující relace v irb.

irb(main):001:0> RUBY_VERSION
=> „1.8.6“
irb(main):002:0> str = ‚ahoj‘
=> „ahoj“
irb(main):003:0> str.length
=> 4
irb(main):004:0> str[0]
=> 97
irb(main):005:0> str[0, 1]
=> „a“

Povšimněte si mírně nestandardního chování indexovacího operátoru, pokud je jako index pozice znaku. Vrací ASCII kód tohoto znaku – příslušný byte. Oproti tomu indexování pomocí pozice a délky podřetězce vrací v případě délky 1 znak (jednoznakový řetězec). Tohle je vlastně zvláštní idiom, jak v Ruby získat znak na určité pozici řetězce. V Ruby 1.9.1 vypadá situace poněkud jinak.

irb(main):001:0> RUBY_VERSION
=> „1.9.1“
irb(main):002:0> __ENCODING__
=> #<Encoding:ISO-8859-2>
irb(main):003:0> str = ‚ahoj‘
=> „ahoj“
irb(main):004:0> str.length
=> 4
irb(main):005:0> str[0]
=> „a“

Zatím je vidět jen ta změna, že máme definovanou konstantu __ENCODING__ s očekávanou hodnotou a indexovací operátor pracuje podle očekávání. Je to proto, že řetězec je nyní skutečně sekvencí znaků. Výrazněji se to projeví, pokud zvolíme kódování, kde je znak kódován více byty. Pro tento účel jsem přepnul terminál do UTF-8.

irb(main):001:0> __ENCODING__
=> #<Encoding:UTF-8>
irb(main):002:0> str = ‚kůň‘
=> „kůň“
irb(main):003:0> str.size
=> 3
irb(main):004:0> str.bytesize
=> 5
irb(main):005:0> str[1, 1]
=> „ů“
irb(main):006:0> str.getbyte(1)
=> 197

Jak je vidět, metoda size vrací počet znaků v řetězci, ačkoliv novou metodou bytesize zjistíme, že ve skutečnosti má řetězec více bytů. Indexování funguje korektně. Pokud bychom kód od řádku 2 do řádku 5 spustili ve verzi 1.8.x, dostali bychom nesmyslné hodnoty: délka (vrácená metodou size) by byla 5 a indexem bychom získali nějaký semigrafický znak z horní poloviny ASCII tabulky.

Jako poslední věc si řekněme, jak docílit ve verzi 1.9.1 obdobného chování řetězců jako v 1.8.x a starších. Je pro to vyhrazeno kódování s označením ASCII-8BIT (případně také BINARY). V řetězcích s tímto kódováním by mělo vše fungovat, jako by znaky stále odpovídaly bytům.

Žádný příspěvek v diskuzi

Odpovědět