P°epracování knihovny pro SubVersion v prost°edí Smalltalk/X
Transkript
P°epracování knihovny pro SubVersion v prost°edí Smalltalk/X
České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačů Diplomová práce Přepracování knihovny pro SubVersion v prostředí Smalltalk/X Bc. Daniel Milde Vedoucí práce: Ing. Jan Vraný, Ph.D. Studijní program: Otevřená informatika Obor: Softwarové inženýrství 2. ledna 2013 iv Poděkování Chtěl bych poděkovat vedoucímu práce, Ing. Janu Vranému, Ph.D., za laskavý přístup, za trpělivost a za podnětné připomínky, kterými směřoval vývoj této práce. v Prohlášení Prohlašuji, že jsem práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon). V Praze dne 15. 5. 2011 ............................................................. Abstract The thesis documents redesign of existing implementation of the SubVersion library for the Smalltalk/X programming environment. The reason for this project is that current implementation does not cover whole SubVersion metamodel and does not properly separate the code realizing SubVersion metamodel and the code working with the smalltalk code stored in SubVersion. This is why emphasis in this project is placed on architecture design, design of clean low-level API and code coverage. The thesis also deals with the possibilities of the transition to a decentralized version control system Git. Abstrakt Práce dokumentuje přepracování existující implementace SubVersion knihovny pro programovací prostředí Smalltalk/X. Důvodem vzniku projektu je nepokrytí celého metamodelu SubVersion současnou implementací knihovny a nedostatečné oddělení kódu realizujícího metamodel SubVersion a kódu pracujícího se smalltalkovým kódem uloženým v SubVersion. Velký důraz v projektu je proto kladen na návrh architektury, návrh čistého nízkoúrovňového API a pokrytí kódu testy. Práce se v neposlední řadě zabývá možnostmi přechodu na decentralizovaný verzovací systém Git. vi Obsah 1 Úvod 1.1 Struktura práce . . . . . . . . . . 1.2 Smalltalk . . . . . . . . . . . . . 1.3 Verzování . . . . . . . . . . . . . 1.4 Historie verzovacích systémů . . . 1.4.1 První generace . . . . . . 1.4.2 Druhá generace . . . . . . 1.4.3 Třetí generace . . . . . . 1.5 Verzování smalltalkového kódu . 1.6 Problémy současné implementace . . . . . . . . . . . . . . . . . . 2 Úvod do jazyka Smalltalk 2.1 Základní koncepty . . . . . . . . . . 2.2 Třídy . . . . . . . . . . . . . . . . . 2.3 Zprávy . . . . . . . . . . . . . . . . . 2.3.1 Unární zprávy . . . . . . . . 2.3.2 Binární zprávy . . . . . . . . 2.3.3 Slovní zprávy . . . . . . . . . 2.3.4 Pravidla zasílání zpráv . . . . 2.4 Metody . . . . . . . . . . . . . . . . 2.4.1 Vykonávání metod . . . . . . 2.4.2 Odkazování v metodách . . . 2.4.3 Návratová hodnota . . . . . . 2.5 Bloky . . . . . . . . . . . . . . . . . 2.5.1 Parametry bloku . . . . . . . 2.5.2 Návratová hodnota bloku . . 2.5.3 Speciální znak návratu uvnitř 2.6 Proměnné . . . . . . . . . . . . . . . 2.6.1 Instanční proměnné . . . . . 2.6.2 Třídní proměnné . . . . . . . 2.6.3 Třídní instanční proměnné . . 2.6.4 Parametry . . . . . . . . . . . 2.6.5 Dočasné proměnné . . . . . . 2.7 Řízení toku . . . . . . . . . . . . . . 2.7.1 Podmíněné vykonání . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bloku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 2 3 3 3 4 4 5 . . . . . . . . . . . . . . . . . . . . . . . 6 6 7 8 8 8 9 9 10 10 10 10 10 11 11 11 12 12 13 13 13 13 14 14 OBSAH viii 2.7.2 Cykly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3 Návrh 3.1 Architektura knihovny . . . . . . . . 3.2 Metamodel Subversion . . . . . . . . 3.2.1 Základy práce se Subversion . 3.2.2 Doménový model Subversion 3.3 Metamodel knihovny . . . . . . . . . 3.3.1 Třídy druhé vrstvy . . . . . . 3.3.1.1 Repository . . . . . 3.3.1.2 WorkingCopy . . . . 3.3.1.3 Revision . . . . . . . 3.3.1.4 Entry . . . . . . . . 3.3.1.5 RevisionEntry . . . 3.3.1.6 WCEntry . . . . . . 3.3.1.7 Commit . . . . . . . 3.3.1.8 Action . . . . . . . . 3.3.1.9 RevisionSpec . . . . 3.3.1.10 Author . . . . . . . 3.3.1.11 EntryType . . . . . 3.3.1.12 Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 15 15 16 17 17 17 17 18 18 18 18 18 18 19 19 19 19 4 Implementace 21 4.1 Implementace první vrstvy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 4.2 Implementace druhé vrstvy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 5 Testování 5.1 Teorie testování . . . . 5.1.1 Motivace . . . 5.1.2 Druhy testů . . 5.1.3 Unit testy . . . 5.1.4 Integrační testy 5.2 Testování na platformě 5.3 Testy v knihovně . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Smalltalk/X . . . . . . . . 6 Použití knihovny 6.1 Vytvoření nové revize . . . 6.2 Aktualizace working copy 6.3 Práce s repozitářem . . . 6.4 Vyhledávání v historii . . 7 Možnosti 7.1 Git . 7.1.1 7.1.2 7.1.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 26 26 26 27 28 28 29 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 30 31 31 32 . . . . 34 34 35 36 36 přechodu na Git . . . . . . . . . . . . . . . . . . . Vnitřní fungování Gitu . . . . . Odlišnosti v práci oproti SVN . Změny v knihovně potřebné pro . . . . . . . . . . . . . . . přechod . . . . . . . . . . . . na Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OBSAH ix 7.1.3.1 7.1.3.2 Změny v první vrstvě . . . . . . . . . . . . . . . . . . . . . . 36 Změny v druhé vrstvě . . . . . . . . . . . . . . . . . . . . . . 36 8 Závěr 38 8.1 Zhodnocení splnění cílů . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 8.2 Další pokračování projektu . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 A Seznam použitých zkratek 40 Seznam obrázků 2.1 2.2 Prohlížeč smalltalkového kódu . . . . . . . . . . . . . . . . . . . . . . . . . . . Hierarchie základních tříd ve Smalltalk/X . . . . . . . . . . . . . . . . . . . . 3.1 3.2 3.3 Doménový model Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Class diagram první vrstvy knihovny . . . . . . . . . . . . . . . . . . . . . . . 17 Class diagram druhé vrstvy knihovny . . . . . . . . . . . . . . . . . . . . . . . 20 4.1 Vykonání příkazu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 5.1 5.2 Třída ArticleService s jejími závislostmi . . . . . . . . . . . . . . . . . . . . . 27 Třída ArticleService s nahrazenými závislostmi . . . . . . . . . . . . . . . . . 27 7.1 7.2 7.3 Typy objektů a jejich vztah . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Ukázka DAG storage grafu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Class diagram druhé vrstvy knihovny při použití Gitu . . . . . . . . . . . . . 37 x 7 7 Kapitola 1 Úvod Cílem této práce je přepracovat existující implementaci SubVersion knihovny pro programovací prostředí Smalltalk/X a definovat nové nízkoúrovňové API, které bude jednoduché na použití, bude odpovídat metamodelu SubVersion a bude pokryto jednotkovými testy. 1.1 Struktura práce V úvodní části práce budou nastíněny technologie a techniky, kterých se práce dotýká. Bude popsán programovací jazyk Smalltalk a jeho specifika oproti jiným programovacím jazykům. Dále bude popsán koncept verzování, stručně přiblížena historie verzovacích systémů, popsány způsoby, kterými je možné verzovat smalltalkový kód a nakonec načrtnuty problémy současné implementace knihovny. Druhá kapitola[2] poskytuje stručný úvod do syntaxe jazyka Smalltalk. Budou probrány základní koncepty a konstrukty tohoto jazyka, doplněné řadou příkladů. Ve třetí kapitole[3] bude nastíněna celková architektura knihovny a popsán návrh jednotlivých vrstev. Čtvrtá kapitola[4] se zaměřuje na samotnou implementaci knihovny. Budou probrány postupy a technologie, které byly použity. Pátá kapitola[5] rozebírá základní techniky testování softwaru a popisuje testy použité v knihovně. Šestá kapitola[6] obsahuje seznam několika komentovaných příkladů použití knihovny. Sedmá kapitola[7] se zabývá možnostmi přechodu na verzovací systém Git. Poslední, osmá kapitola[8] shrnuje výsledky práce a dosažení cílů. 1.2 Smalltalk Smalltalk je objektově orientovaný programovací jazyk vytvořený v 70. letech minulého století skupinou vědců v Palo Alto Research Center (PARC), která byla financovaná firmou Xerox[8]. Skupina se snažila o vyvoření zcela nového konceptu osobního počítače, nazvaného “DynaBook”. Smalltalk byl jeho základní součástí, zajišťující úlohu jak operačního systému 1 KAPITOLA 1. ÚVOD 2 tak i programovacího jazyka s vývojovým prostředím. Jazyk je nejvíce ovlivněn funkcionálním jazykem LISP a prvním objektově orientovaným jazykem Simula. Smalltalk ovlivnil mnoho pozdějších programovacích jazyků, např. Objective-C, C++, Java, C#, Ruby nebo CoffeScript. Dnes má Smalltak mnoho dialektů, které mají různé cílové skupiny a zaměření. Hojně používané jsou komerční VisualAge Smalltalk vyvíjený firmou IBM, svobodná implementace GNU Smalltalk zastřešená projektem GNU, zdarma dostupný Smalltalk/X vyvíjený firmou eXept Software, svobodný dialekt Squeak a jeho mladší sourozenec Pharo. Smalltalk je čistě objektový programovací jazyk, což znamená, že jediný konstrukt, se kterým můžeme v jazyku pracovat jsou objekty. Objektům můžeme posílat zprávy, což je obdoba volaní metod. Jazyk neobsahuje příkazy řízení toku (if, switch, while, for), definice funkce ani příkazy skoku, běžné v jiných programovacích jazycích, všechna tato funkcionalita je realizována pouze pomocí posílání zpráv objektům. Jedním ze specifik Smalltalku je jeho persistence na úrovni binárního obrazu[10], což znamená, že kód programu i stav programu jsou uloženy v binární podobě v jednom souboru. Většina implementací Smalltalku dokonce ani nerozlišuje mezi stavem programu a kódem, protože kód programu - třídy - jsou také objekty, tedy stav programu. Programy jsou proto vyvíjeny přímo v běžícím Smalltalkovém programu, který obsahuje celou vývojovou platformu. Vzhledem k těmto skutečnostem vznikají značné požadavky na vývoj samotného vývojového prostředí, aby poskytovalo vývojářům vlastnosti jako kompletace kódu, formátování kódu, zvýrazňování kódu a verzování kódu. V této práci se zaměříme na poslední jmenovanou aktivitu, kterou od vývojového prostředí požadujeme - verzování zdrojového kódu. 1.3 Verzování Verzování je způsob uchovávání historie provedených změn u digitálního obsahu. Nejčastěji se s tímto termínem setkáme u zdrojových kódů software, ale verzovat lze v podstatě jakékoliv soubory, včetně těch binárních. Verzování v jednoduchosti funguje tak, že systém správy verzí (dále jen verzovací systém), uchovává informace o tom, kdo, kdy a co změnil ve verzovaném souboru. Z těchto informací je pak možné vyčíst celou historii změn daného souboru, zjistit stav souboru k určitému datu, případně vrátit stav souboru do libovolné předchozí verze (revize). Verzovací data mohou být přímo součástí samotného dokumentu, jako např. v kancelářských balících OpenOffice nebo Microsoft Office, kde je verzování dokumentu umožněno nástrojem sledování změn, mohou ale také být umístěny v samostatném souboru a to dokonce na jiném stroji. Verzování může probíhat automaticky na základě vstupu od uživatele - např. se uloží stav souboru při dokončení každého slova nebo věty - nebo v pravidelných časových intervalech. Automatické verzování se využívá zejména v integrovaných vývojových prostředích (IDE). Daleko častější je manuální verzování, kdy uživatel musí sám sdělit verzovacímu systému, že současný stav verzovaného souboru má uložit, k čemuž slouží akce přijmout neboli commit. Postupem času se dalším úkolem verzovacích systémů stalo usnadnění práce více vývojářů na jednom projektu. Verzovací systém zabraňuje, aby si vývojáři navzájem přepisovali změny, KAPITOLA 1. ÚVOD 3 umožňuje snadné sdílení změn mezi nimi a pomáhá při řešení kolizí, tedy situací, kdy několik vývojářů provede změny ve stejné části souboru. U verzovacích systémů se setkáváme se třemi základními pojmy, které je potřeba vyjasnit: • Revize je synonymum pro verzi a označuje stav souboru nebo projektu, který je uložen pomocí verzovacího systému. K takto uloženému stavu se můžeme později kdykoliv vrátit. • Repozitář je soubor nebo skupina souborů, jejichž smyslem je uchovávat data verzovacího systému (např. data jednotlivých revizí). • Pracovní kopie je kopie verzovaných souborů získaná z repozitáře. Samotná práce (úpravy) se soubory pak probíhá v této pracovní kopii. 1.4 Historie verzovacích systémů Počátky verzovacích systémů jsou spojeny se systémy správy změn používaných na mainframech v 60. letech minulého století[6]. Verzovací systémy se často, v závislosti na své architektuře, rozdělují do tří generací. V dalších odstavcích budou popsány všechny tři generace verzovacích systémů a u každé generace bude vybrán jeden zástupce, který bude podrobněji analyzován. 1.4.1 První generace První generace verzovacích systémů má repozitář i pracovní kopii umístěny na stejném počítači. První VCS patřící do této skupiny nazvaný Source Code Control System (SCCS) napsal Marc Rochkind roku 1972. SCCS není dnes již příliš používaný na rozdíl od svého nástupce Revision Control System (RCS). RCS je velmi jednoduchý verzovací systém určený k verzování jednotlivých souborů, který má v základu pouze dvě operace: check in a check out. Příkaz check in (ci) uloží obsah pracovního souboru do archivačního (datového) souboru, kterému se říká jednoduše RCS soubor. Pokud naopak chceme načíst do pracovního souboru nějakou revizi, použijeme příkaz check out (co). RCS pracuje se zamykáním souborů, takže pokud chceme soubor změnit, musíme ho načíst s přepínačem pro zamknutí (-l), upravit, a poté můžeme změny archivovat do RCS souboru (a vytvořit tak novou revizi) příkazem check in. RCS soubory jsou vytvářeny v adresáři ./RCS s podobným jménem jako pracovní soubor lišícím se pouze koncovkou “,v”. RCS dále podporuje vlastní číslování revizí, doplňování informací o revizi do souboru, správu oprávnění k souboru, zobrazení rozdílů mezi revizemi a základní nástroj pro slučování změn z více revizí. 1.4.2 Druhá generace Druhá generace verzovacích systémů představila model klient-server umožňující použití vzdálených repozitářů a možnost verzování celého projektu (sady souborů a adresářů) jako celek. Nejznámějšími zástupci druhé generace je verzovací systém Concurrent Versions System (CVS) a jeho nástupce Subversion (SVN). KAPITOLA 1. ÚVOD 4 Projekt CVS vytvořil Dick Grune v roce 1986. CVS myšlenkově vychází z RCS a používá například stejný systém ukládání změn do souborů s koncovkou “,v”. Na rozdíl od RCS verzuje CVS celý adresář místo jednotlivých souborů. CVS obsahuje některé nedostatky jako například nemožnost přesunu a kopírování adresářů nebo časová a prostorová náročnost větvení a tagování. Projekt Subversion byl založen firmou CollabNet v roce 2000 ve snaze vytvořit svobodný verzovací systém, který by pracoval podobným způsobem jako CVS, ale odstranil jeho nedostatky. Subversion brzy získal velkou popularitu a je dnes jedním z nejpoužívanějších verzovacích systémů, a to zejména v podnikové sféře. Vývoj Subversion byl roku 2010 přenesen pod hlavičku organizace Apache Software Foundation. Subversion přineslo do světa verzování zásadní změny ve způsobu uchovávání dat. Zatímco předchozí VCS uchovávaly revize ve velmi jednoduchém textovém formátu, který je lidsky čitelný, a pro každý verzovaný soubor vytvářely jeden datový soubor, Subversion uchovává data v Bekeley DB nebo ve vlastním formátu nazvaném FSFS[5]. FSFS, který je výchozím úložištěm, uchovává všechny změny přidané v jedné revizi v jediném neměnném binárním souboru. 1.4.3 Třetí generace Třetí generace verzovacích systémů přinesla koncept decentralizace. Každý uživatel má tedy u sebe pracovní kopii i plnohodnotný repozitář. Pro sdílení změn mezi uživateli neexistuje jeden jednotný způsob, jako tomu bylo u předešlých VCS. Uživatelé si změny mohou předat mnoha způsoby (použita je terminologie systému Git): • zvolit jeden repozitář jako hlavní a změny sdílet jeho prostřednictvím (příkazy push a pull) • stáhnout si změny přímo z repozitáře jiného uživatele příkazem pull • poslat patch ostatním uživatelům (např. emailem) 1.5 Verzování smalltalkového kódu Verzování smalltalkového kódu může být v zásadě prováděno třemi způsoby. • Vývojáři exportují definice tříd do textových souborů pomocí fileOut mechanismu, a pak používají verzovací systém k jejich správě. • Veškerá logika verzování kódu je implementována přímo ve smalltalkové platformě (jako například ve smalltakové implementaci Pharo[4]). • Ve smalltalku je implementována knihovna, která interně volá příkazy jednoho z běžných verzovacích systému. Pro tento projekt byla zadavatelem vybrána třetí možnost, což znamená, že ve Smalltalkové platformě bude implementována knihovna umožňující práci s verzovacím systémem (v tomto případě SVN). KAPITOLA 1. ÚVOD 1.6 5 Problémy současné implementace Současná knihovna pro práci se SubVersion ve Smalltalk/X vznikala velmi organicky na základě postupně se objevujících požadavků, v důsledku čehož neprošla důkladnějším procesem návrhu rozhraní a její kód je víceméně monolitický (není rozdělen do vrstev). To ve výsledku činí knihovnu těžko rozšiřitelnou o pokročilejší funcionality jako je například vyhledávání v historii. Kvůli pevnému propojení mezi kódem zajišťujícím práci se SubVersion a kódem pracujícím se smalltalkovými balíčky není možné knihovnu použít pro verzování ničeho jiného než smalltalkového kódu. Kapitola 2 Úvod do jazyka Smalltalk Smalltalk je interpretovaný jazyk, což znamená, že k jeho vykonání není potřeba zdrojový kód kompilovat do strojového kódu nebo linkovat s knihovnami. Zdrojový kód je spouštěn pomocí interpreteru jazyka Smalltalk, kterému se také někdy říká virtuální stroj. Je dokonce možné změnit zdrojový kód právě běžící aplikace a změny se projeví ihned při příštím vykonávání kódu. Není tedy potřeba aplikaci znovu spouštět nebo nasazovat, jak je to často potřeba v jiných programovacích jazycích. Smalltalk se od ostatních programovacích jazyků odlišuje především tím, že u většiny jeho dialektů je přímou součástí programovacího jazyka také vývojové prostředí. Při spuštění interpreteru se nespustí vykonání jednoho souboru nebo projektu, ani se nespustí interaktivní konzolové rozhraní (výjimkou je GNU Smalltalk), ale namísto toho se otevře grafické rozhraní určené pro vývoj smalltalkových aplikací. Ústředním prvkem smalltalkových vývojových prostředí je průzkumník kódu, který má zpravidla podobu čtyř sloupců (balíčky, třídy, kategorie a metody), které umožňují přehledné procházení zdrojového kódu, pod kterými je editor zdrojového kódu, ve kterém je možné nejen kód měnit, ale také spouštět části kódu. Ukázka průzkumníku kódu je na obrázku 2.1. Dalším zajímavou vlastností Smalltalku je, že zdrojové kódy všech tříd a knihoven smalltalkového prostředí (kromě intepreteru) je možné v průzkumníku procházet a dokonce měnit. Můžeme si tak vývojové prostředí snadno upravit podle svých potřeb. 2.1 Základní koncepty Smalltalk je čistě objektový jazyk, což znamená, že v něm v podstatě není možné psát běžný procedurální kód. Veškerá programová logika je realizována pomocí definice objektů a posílání zpráv objektům. Posílání zpráv objektům je trochu odlišná formulace konceptu volání metod objektů, na který jsme zvyklí z ostatních jazyků, která klade důraz na fakt, že je to samotný objekt, který rozhoduje o tom, jaký kód bude vykonán, nikoliv volající. Objektu nikdy neříkáme, co má dělat - místo toho ho slušně požádáme něco udělat tím, že mu pošleme zprávu [1]. 6 KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 7 Obrázek 2.1: Prohlížeč smalltalkového kódu 2.2 Třídy Třída je předpis, který definuje jakým způsobem se mají vytvářet objekty daného typu. Vše ve Smalltalku je objekt a každý objekt je instancí třídy. Aby toto pravidlo bylo splnitelné, existuje ve Smalltalk/X cyklická hierarchie základních tříd, zachycená na obrázku 2.2. Obrázek 2.2: Hierarchie základních tříd ve Smalltalk/X Čistě objektové pojetí jazyka Smalltalk jde tak daleko, že dokonce i definice třídy není nic jiného než poslání zprávy objektu. nil subclass:#Object Takovýmto způsobem vypadá definice základní třídy Object v prostředí Smalltalk/X. Objektu nil posíláme zprávu subclass s argumentem #Object. Říkáme tedy objektu nil, aby vytvořil potomka sama sebe s název Object. Takto vytvořená třída není nic jiného než dalším objektem, který ale na rozdíl od ostatních “běžných” objektů můžeme všude adresovat globálním jménem třídy. KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 8 Každá třída obsahuje speciální metodu new, která vytvoří novou instanci třídy. Tuto metodu můžeme také překrýt a nadefinovat tak například chování, kdy se nad nově vytvořenou instancí třídy zavolá metoda initialize. new ^super new initialize 2.3 Zprávy Veškeré vykonávání logiky se děje prostřednictvím posílání zpráv objektům. Ve Smalltalku existují tři druhy zpráv. 2.3.1 Unární zprávy Unární zpráva je zpráva bez argumentů. Příkladem může být zaslání zprávy asString objektu typu Integer. 1 asString 2.3.2 Binární zprávy Binární zpráva má stejný zápis jako aritmetický operátor. Binární v tomto kontextu znamená, že se zde pracuje se dvěma objekty. Příkladem může být porovnání dvou čísel nebo spojování řetězců. 1 == 2. ’string1’ , ’string2’. V tomto příkladu je poprvé použit speciální znak . (tečka), který slouží k oddělování výrazů. Jako binární zprávu je možné použít tyto operátory (s vysvětlením výchozího chování): + sčítání čísel - odečítání čísel * násobení čísel / dělení čísel nebo vytvoření zlomku (pokud je dělení se zbytkem) ** mocnění // dělení beze zbytku zaokrouhlující dolů \\ modulo < je menší (lze použít na čísla, znaky i řetězce) KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 9 <= je menší nebo rovno > je vetší >= je větší nebo rovno = porovnání hodnotou ~= rozdílné hodnotou == identické (jde o stejný objekt) ~~ není identické & logický AND, vyhodnoceny jsou oba operandy a to i v případě, že první operand se vyhodnotí jako false | logický OR, vyhodnoceny jsou oba operandy a to i v případě, že první operand se vyhodnotí jako true , spojení dvou kolekcí (např. řetězců) @ vytvoření instance třídy Point -> vytvoření instance třídy Association 2.3.3 Slovní zprávy Slovní zprávy se skládají z jednoho nebo více slov a stejně tak obsahují jeden nebo více argumentů. Každé slovo zprávy je následované dvojtečkou, která uvozuje předání jednoho argumentu. Jako příklad můžeme uvést zprávu between: and: třídy SmallInteger, která vrátí true pokud je hodnota příjemce zprávy v rozmezí obou operandů. 1 between: 0 and: 2 2.3.4 Pravidla zasílání zpráv Jako ve většině programovacích jazyků i ve Smalltalku je možné zasílání zpráv řetězit. ’string’ reverse asUppercase Vyhodnocování pak probíhá zleva doprava tak, že výsledek prvního volání je příjemcem druhé zprávy. Zápis tedy můžeme s pomocí závorek přepsat beze změny významu takto: (’string’ reverse) asUppercase Zprávy mají různou prioritu vyhodnocování podle jejich typu. Nejdříve se vykonávají unární zprávy, poté binární zprávy a nakonec slovní zprávy. Transcript nextPutLine: ’string’ = ’GNIRTS’ reverse asLowercase Ve výše uvedeném příkladu se proto vyhodnotí nejprve obě unární zprávy reverse a asUppercase, poté se provede birární zpráva porovnání a nakonec se celý výsledek předá jako argument zprávy nextPutLine. KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 2.4 10 Metody Metoda je pojmenovaná část kódu třídy, která je vykonána v případě, že instanci třídy je doručena odpovídající zpráva. 2.4.1 Vykonávání metod Pokud je objektu zaslána zpráva, které neodpovídá žádná metoda, je metoda vyhledávána v rodičovské třídě. Pokud ani tam není nalezena, pokračuje se v její rodičovské třídě. Pokud není metoda ve třídě ani v žádném z jejích předků nalezena, je původnímu příjemci zprávy odeslána zpráva doesNotUnderstand. 2.4.2 Odkazování v metodách Pokud se v kódu metody chceme odkázat na příjemce zprávy, tedy aktuální instanci, nad kterou je metoda volána, můžeme použít klíčové slovo self. String>>upperAndReverse ^self reverse asUppercase Příklad ukazuje definici unární metody upperAndReverse ve třídě String. Metoda posílá aktuálnímu příjemci zprávy (instance třídy String nebo nějakého jejího potomka) zprávu reverse a asUppercase a vrací výsledek volajícímu. Podobným způsobem se můžeme odkazovat také na rodičovskou třídu a to klíčovým slovem super. new ^super new initialize Zde je jako příklad uvedeno již dříve zmiňované překrytí metody new. 2.4.3 Návratová hodnota Vrácení hodnoty z metody (a ukončení vykonávání metody) se provádí speciálním znakem ˆ, za který uvedeme požadovanou návratovou hodnotu. V případě, že vrácení z metody vůbec neuvedeme, vrátí metoda objekt příjemce zprávy (self). 2.5 Bloky Blok je část kódu, která je vykonána v okamžiku, kdy je bloku zaslána zpráva value. Blok kódu je v prostředí Smalltalk/X reprezentován instancí třídy Block. Svým chováním můžeme blok ve smalltalku přirovnat k anonymním funkcím, které se objevují v jiných jazycích. [ Transcript nextPutLine: ’string’ ] value V uvedeném příkladu definujeme blok kódu, který obsahuje poslání zprávy nextPutLine objektu nextPutLine. Bloku je poslána zpráva value a jeho obsah je tak vykonán. KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 2.5.1 11 Parametry bloku Pokud chceme do bloku předat argumenty, můžeme je uvést na začátku bloku, přičemž jejich seznam ukončíme svislítkem. Každý uvedený argument musí být navíc uvozen dvojtečkou. Pří vykonávání bloku se pak používá zpráva value: pro jeden argument, value: value: pro dva argumenty apod. [ :x :y | Transcript nextPutLine: (x * y) asString ] value: 2 value: 5 V uvedeném příkladu je blok, který přijímá dva parametry, které mezi sebou vynásobí a výsledek vypíše na obrazovku. 2.5.2 Návratová hodnota bloku Výsledek posledního výrazu bloku je vrácen jako návratová hodnota bloku. Transcript nextPutLine: ([ :str | str at: 2 put: $b. str ] value: ’aac’) V tomto příkladu máme blok, který přijímá jeden argument (řetězec) a posílá mu zprávu at: put:, což je vložení prvku na danou pozici v kolekci. Zde vkládáme znak (uvozený speciálním znakem $) b na druhou pozici. Za tímto výrazem následuje ještě jeden, který nic nevykonává, pouze vrací předaný argument. Tento blok vykonáme s argumentem aac a návratovou hodnotu bloku pošleme jako argument zprávy nextPutLine. Výsledkem je vypsaní řetězce abc. Pokud bychom neuvedli poslední výraz bloku, byl by návratovou hodnotou bloku znak b, což je hodnota, kterou v tomto případě vrací metoda at: put:. 2.5.3 Speciální znak návratu uvnitř bloku Znak ˆ (návratu) slouží pro vrácení hodnoty z metody, a to i v případě, že je uveden uvnitř bloku. Pokud je vykonán blok, který obsahu znak návratu, je navrácena hodnota z metody, kde byl blok původně definován, nikoliv z právě prováděné metody. Díky tomuto faktu můžeme napsat rekurzivní metodu hledání čísla půlením intervalů v seřazeném poli, která při nalezení výsledku nemusí předávat výsledek postupně ze zanořených volání, ale může jej vrátit ihned. search: aValue self searchRecursive: aValue from: 1 to: array size withBlock: [ :value | ^value ] Ukázková implementace půlení intervalů sestává ze dvou metod, kdy první metoda pouze vytvoří právě onen zmiňovaný blok, který umožní okamžitý návrat, a zavolá druhou rekurzivní metodu, které předá blok jako argument. KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 12 searchRecursive: aValue from: from to: to withBlock: aBlock |pivotIndex pivot| pivotIndex := ((to - from) // 2 ) + from. pivot := array at: pivotIndex. aValue = pivot ifTrue: [ aBlock value: pivotIndex. ] ifFalse: [ aValue = (array at: to) ifTrue: [ aBlock value: to ]. aValue < pivot ifTrue: [ self searchRecursive: aValue from: from to: pivotIndex withBlock: aBlock. ] ifFalse: [ self searchRecursive: aValue from: pivotIndex to: to withBlock: aBlock. ] ] V druhé metodě se nejprve vyhledá prostřední prvek prohledávané části pole (tzv. pivot) a porovná se s hledanou hodnotou. Pokud hodnota odpovídá, je blok vykonán. Jako argument bloku je předán index nalezené hodnoty, který je tak při vykonání bloku rovnou vrácen z metody search, tedy místa, kde byl blok původně definován. Pokud hodnota neodpovídá, pokračuje se prohledáváním levé nebo pravé části pole. 2.6 Proměnné Ve Smalltalku se rozlišuje několik druhů proměnných podle kontextu jejich definice. 2.6.1 Instanční proměnné Instanční proměnné jsou privátní proměnné, ke kterým má přístup pouze samotný objekt, na kterém jsou definovány. Instanční proměnné se definují zprávou instanceVariableNames:. ArithmeticValue subclass:#Complex instanceVariableNames:’real imaginary’ V ukázce je difinice třídy reprezentující komplexní čísla, která má dvě instanční proměnné. Každá instance této třídy má vlastní proměnné real a imaginary, které může měnit jen tato instance. Smalltalk používá zapouzdření na úrovni objektů, což znamená, že k instančním proměnným má přístup opravdu jen samotný objekt. To je značně odlišný přístup od jazyků jako je C++, Java, PHP a další, které používají zapouzdření na úrovni tříd, což znamená, že k instančním (privátním) proměnným mají přístup všechny instance dané třídy. KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 2.6.2 13 Třídní proměnné Třídní proměnné jsou společné pro všechny instance dané třídy. Každá instance k nim může přistupovat i do nich zapisovat. ArithmeticValue subclass:#Complex classVariableNames:’ComplexOne ComplexZero’ Ve třídě Complex jsou definované dvě třídní proměnné, které reprezentují komplexní čísla 0 a 1. Obdobou třídních proměnných jsou statické proměnné v jazyku Java. 2.6.3 Třídní instanční proměnné Ve Smalltalku je možné definovat také třídní instanční proměnné, které jsou svým chováním podobné jako třídní proměnné, ale liší se tím, že jejich hodnota není sdílena mezi všemi instancemi třídy, ale pouze mezi instancemi stejného potomka třídy (nebo třídy samotné). ExternalStructure class instanceVariableNames:’cType’ Třída ExternalStructure má třídní instanční proměnnou cType. Pokud tedy budeme mít dva potomky této třídy, pak instance jednoho potomka budou sdílet tuto proměnnou mezi sebou, ale nebudou mít přístup k proměnné druhého potomka. 2.6.4 Parametry Parametr je proměnná, která je předána do metody nebo bloku. Smalltalk je netypový jazyk, kde obecně není brán zřetel na to, jakého typu je daná proměnná, ale spíše na to, jaké má rozhraní, tedy jaké zprávy jí můžeme poslat. V důsledku toho neexistuje ve Smalltalku typová kontrola parametrů. Parametr se od ostatních proměnných odlišuje především tím, že ho nemůžeme znovu přiřadit. Níže uvedený kód proto skončí chybou. value: aVal aVal := nil 2.6.5 Dočasné proměnné Dočasné proměnné jsou definovány v rámci metody nebo bloku a jejich životnost končí po ukončení této části kódu. V metodě se dočasné proměnné definují jejich výčtem, který obklopují z obou stran svislítka. searchRecursive: aValue from: from to: to withBlock: aBlock |pivotIndex pivot| KAPITOLA 2. ÚVOD DO JAZYKA SMALLTALK 14 Takto vypadá definice dočasných proměnných v příkladu s půlením intervalů, který byl uveden již dříve. V bloku se dočasné proměnné definují stejným způsobem. Pokud blok přijímá parametry, pak se definice dočasných proměnných uvede až za definici parametrů. [ | z | z := 1 + 2 ] value. [ :x | | z | z := x * x ] value: 5. 2.7 Řízení toku Jak bylo řečeno hned na začátku kapitoly, neobsahuje Smalltalk žádné konstrukty pro řízení toku programu. Místo toho se používá zasílání zpráv objektům. 2.7.1 Podmíněné vykonání Pokud chceme vykonat část kódu pouze při splnění určité podmínky, můžeme použít zaslání zprávy ifTrue: libovolnému objektu typu Boolean nebo výrazu jehož výsledkem je Boolean hodnota. Tato zpráva přijímá jeden parametr, a to blok, který je vykonán v případě, že podmínka je splněna. 1 + 2 >= 3 ifTrue: [ Transcript nextPutLine: ’correct’ ] Stejným způsobem funguje zpráva ifFalse:, která vykoná blok, pokud podmínka není splněna. Můžeme použít také zprávu ifNil:, kterou přijímá jakýkoliv objekt a která vykoná blok, pokud hodnota je nil. Opakem je zpráva ifNotNil:. 2.7.2 Cykly Velmi často se ve Smalltalku setkáme také s iterativním procházením kolekcí (polí, řetězců), které se provádí posláním zprávy do: kolekci. Zpráva do: vyžaduje jako parametr blok, který přijímá jeden argument. Tento blok je vykonán tolikrát, kolik je prvků v kolekci, přičemž prvky kolekce jsou postupně předávány jako parametr bloku. |sum| sum := 0. #(1 2 3 4 5) do: [ :each | sum := sum + each ]. Transcript nextPutLine: sum. Příklad ukazuje vypočítání součtu všech čísel pole a jeho vypsání. Kapitola 3 Návrh V této kapitole bude popsána celková architektura knihovny, bude podrobně rozebrán metamodel verzovacího systému Subversion, rozebrány scénáře použití knihovny a nakonec navržen metamodel knihovny. 3.1 Architektura knihovny Knihovna bude rozdělena do několika vrstev: • vrstva umožňující volání příkazů SVN • vrstva nabízející verzování souborů • vrstva umožňující verzování konstruktů jazyka Smalltalk • vrstva poskytující grafické uživatelské rozhraní pro práci s verzováním Každá vrstva bude napsána tak, aby komunikovala pouze se svými sousedy, tedy buď s vrstvou nad sebou nebo pod sebou. Díky tomuto principu na sobě vrstvy nebudou tak silně závislé a bude snadnější v budoucnosti jednu z nich vyměnit. Podobný princip je používán i v jiných technologiích, např. v ISO/OSI modelu. Úkolem této práce je realizovat první dvě vrstvy a poskytnout tak ve výsledku smalltalkovým programům API, pomocí kterého budou moci verzovat libovolné soubory. 3.2 3.2.1 Metamodel Subversion Základy práce se Subversion Verzovací systém Subversion funguje na principu klient-server. Roli serveru zastává centrální repozitář, ke kterému se připojuje libovolné množství klientů. Pokud máme přístup k existujícímu repozitáři, můžeme si nechat příkazem log vypsat seznam všech revizí (verzí). Tento seznam obsahuje pro každou revizi její číslo, autora, datum změny, počet změněných řádek a textový popis změn (tzv. commit message). 15 KAPITOLA 3. NÁVRH 16 Pokud budeme chtít pracovat se zdrojovým kódem, musíme nejdříve vytvořit tzv. working copy, čehož dosáhneme použitím příkazu checkout. Jakmile dokončíme úpravy v kódu a budeme je chtít odeslat do repozitáře, zavoláme příkaz commit, který vytvoří novou revizi. V případě, že jsme vytvořili nový soubor a chceme ho začít verzovat, musíme na něm nejprve provést příkaz add. Se zdrojovým kódem často nepracuje pouze jeden vývojář, a proto si musí svojí working copy aktualizovat příkazem update, který aplikuje na lokální soubory změny vytvořené ostatními vývojáři. 3.2.2 Doménový model Subversion Z výše uvedených základních scénářů práce se Subversion lze vykonstruovat velmi zjednodušený doménový model Subversion (obrázek 3.1). Obrázek 3.1: Doménový model Subversion Popis vazeb a objektů diagramu: • Repository udržuje informace o všech Revision. • Revision zná svého autora, datum vzniku, zprávu a číslo revize. • S jednou Repository může pracovat neomezeně mnoho WorkingCopy. • Každá WorkingCopy zná svojí aktuální Revision a seznam všech verzovaných Entry (souborů). • Entry uchovává cestu k souboru a svůj stav. • Commit obsahuje zprávu, autora, datum a seznam začleněných Entry. KAPITOLA 3. NÁVRH 3.3 17 Metamodel knihovny Jak již bylo řečeno v úvodu této kapitoly, část knihovny implementovaná v této práci je rozdělena do dvou vrstev. První vrstva věrně kopíruje příkazové rozhraní Subversion, zatímco druhá vrstva vychází z doménového modelu Subversion. Obrázek 3.2: Class diagram první vrstvy knihovny Velice užitečným a intuitivním se ukázalo použití speciálních znaků pro pojmenování některých metod. Například pro metodu Repository»getRevision, která vrací revizi na základě předaného identifikátoru revize, byl vytvořen alias v podobě znaku @. Použití pak vypadá následovně: revision := repository @ 5 Podobným způsobem byla vytvořena metoda WorkingCopy»/, která vrací WCEntry na základě předaného názvu souboru (cesty). entry := workingCopy / ’README.txt’ 3.3.1 Třídy druhé vrstvy V dalších odstavcích budou postupně popsány třídy druhé vrstvy. Bude probrán jejich význam, nejdůležitější metody a vazby na ostatní třídy. Celkový pohled na třídy druhé vrstvy pak zachycuje obrázek 3.3. 3.3.1.1 Repository Repository je třída reprezentující vzdálený centrální SubVersion repozitář. Třída obsahuje metodu getRevision (a její alias @), která vrací instanci třídy Revision. Neméně důležitá je metoda makeWorkingCopy, která vytvoří ve zvolením umístění working copy repozitáře a vrátí instanci třídy WorkingCopy. 3.3.1.2 WorkingCopy Třída WorkingCopy reprezentuje lokální working copy repozitáře. Třída obsahuje vazbu na Repository, která vyjadřuje repozitář, z něhož byla working copy vycheckoutována. Dále pak KAPITOLA 3. NÁVRH 18 obsahuje vazbu na Revision, která odpovídá aktuální revizi working copy. Nakonec obsahuje mnohonásobnou vazbu na WCEntry reprezentující fyzické soubory v lokálním working copy. Patrně nejdůležitější metodou je getEntry (s aliasem /), která vrací instanci WCEntry na základě zadané cesty. Třída obsahuje také metodu update pro změnu aktuální revize working copy a commit pro odeslání aktuálních změn do repository a s tím související vytvoření nové revize. 3.3.1.3 Revision Třída Revision reprezentuje jednu revizi SubVersion repozitáře. Obsahuje vazbu na třídu Author vyjadřující autora revize, dále vazbu na Repository, vícečetnou vazbu na třídu RevisionEntry, která reprezentuje soubory v této revizi a nakonec vazbu na potomky třídy Action, která představuje všechny změny v této revizi. Nejdůležitější metodou je getEntry, která vrací instanci RevisionEntry na základě zadané cesty. 3.3.1.4 Entry Třída Entry je abstraktní třída, která je společným předkem pro třídy WCEntry a RevisionEntry. Obsahuje vazbu na potomky třídy EntryType vyjadřující typ entry, dále pak vazbu na Revision vyjadřující aktuální revizi entry a vazbu na Property, která vyjadřuje SubVersion property. Důležitou metodou je readStream, která vrací stream určený ke čtení obsahu souboru entry. 3.3.1.5 RevisionEntry Třída RevisionEntry je potomkem Entry a vyjadřuje entry uloženou ve vzdáleném SubVersion repozitáři. 3.3.1.6 WCEntry Třída WCEntry je potomkem Entry a vyjadřuje entry uloženou v lokálním working copy. Obsahuje vazbu na potomky třídy Status, která vyjadřuje současný stav entry. Třída obsahuje metodu writeStream, která vrací stream určený k zápisu a umožňuje tak měnit obsah souboru entry. 3.3.1.7 Commit Třída Commit reprezentuje nový commit, který může být následně odeslán do vzdáleného repozitáře a vytvořena tak nová revize. Třída obsahuje setter a getter metodu message, která slouží pro nastavení a získání zprávy nového commitu. 3.3.1.8 Action Třída Action je abstraktní třída reprezentující jeden změněný soubor v revizi. Její potomci vyjadřují přidání nového souboru, změnu stávajícího a nebo smazání stávajícího. Třída obsahuje dvojitou vazbu na třídu RevisionEntry, která vyjadřuje z jakého stavu a do jakého stavu byl soubor změněn. KAPITOLA 3. NÁVRH 3.3.1.9 19 RevisionSpec Třída RevisionSpec je abstraktní třída, která vyjadřuje libovolnou identifikaci některé revize nebo revizí. Jejími potomky jsou třídy RevisionHead, která reprezentuje poslední revizi v repozitáří, a třída RevisionNumber, která označuje revizi podle jejího čísla. 3.3.1.10 Author Třída Author reprezentuje autora revize, který je identifikován celým jménem a emailovou adresou. 3.3.1.11 EntryType Třída EntryType je abstraktní třída, která reprezentuje typ entry. Jejími potomky jsou File zastupující běžný soubor, Dir zastupující složku a External zastupující externí zdroj. 3.3.1.12 Status Třída Status je abstraktní třída, která reprezentuje současný stav entry ve working copy. Potomky třídy jsou: • Added reprezentuje nový soubor, který byl přidán k verzování. • Conflicted reprezentuje soubor, který se v důsledku slučování revizí dostal do konfliktního stavu. • Deleted reprezentuje smazaný soubor. • Ignored reprezentuje soubor vynechaný z verzování. • Missing reprezentuje chybějící soubor, tedy soubor smazaný z working copy ale neoznačený ke smazání. • Modified reprezentuje změněný soubor. • NotVersioned reprezentuje nový, zatím neverzovaný soubor. • Replaced označující, že soubor byl zcela odstraněn a znovu přidán s jiným obsahem. • Unchanged reprezentuje nezměněný soubor. KAPITOLA 3. NÁVRH Obrázek 3.3: Class diagram druhé vrstvy knihovny 20 Kapitola 4 Implementace 4.1 Implementace první vrstvy Jak již bylo nastíněno dříve, úkolem první vrstvy je zajištění komunikace se Subversion pomocí příkazového rozhraní (CLI). Uživatel knihovny se tak nebude muset starat o to, v jakém prostředí je platforma právě provozována (Linux, Unix, Windows, Mac OS X atd.) a jakým způsobem by měl ze smalltakového kódu volat příkazy Subversion. Druhou neméně důležitou zodpovědností první vrstvy je správné zpracování textového výstupu z volaných příkazů Subversion. Základem první vrstvy jsou dvě třídy: • třída Command • třída CommandParser Třída Command zajišťuje abstrakci nad vykonáváním příkazů a nabízí rozhraní pro volání příkazů Subversion. Command checkout: ’https://swing.fit.cvut.cz/svn/stx/libsvn/branches/libsvn2’ Vnitřně se třída chová tak, že při vykonání výše uvedeného příkladu: 1. vytvoří instanci svojí soukromé podtřídy (nazvané také checkout) 2. nastaví přes settery všechny potřebné atributy instance 3. pošle instanci zprávu execute 4. metoda execute (implementovaná v předkovi checkout, což je třída Command) provede samotné zavolání příkazu Subversion 5. výstup z provedeného příkazu je poslán pomocí zprávy parse opět instanci 6. instance v metodě parse pošle zprávu parseCheckout třídě CommandParser 21 KAPITOLA 4. IMPLEMENTACE 22 Obrázek 4.1: Vykonání příkazu 7. metoda parseCheckout vrátí instanci třídy WorkingCopy Tento proces je zachycen také na obrázku 4.1. Třída CommandParser používá parsovací framework PetitParser[7], který umožňuje dynamickou tvorbu parserů za použití prvků jazyka Smalltalk. PetitParser přidává některým základním třídám smalltalku metodu asParser. Velmi jednoduchý parser pro parsování celého čísla tak může vypadat následovně: parser := #digit asParser star. parser parse: ’123’. Pro snazší pochopení složitějších parserů je vhodné podívat se na vnitřní fungování PetitParseru. Metoda Symbol»asParser obsahuje: ^ PPPredicateObjectParser perform: self Třídní metoda perform vytvoří novou instanci třídy PPPredicateObjectParser a nastaví ji tak, aby při parsování dovolila zpracovat jen symbol #digit. Pokud je předán k parsování jiný vstup, vrátí instance chybovou hlášku. Takto připravené instanci je předána zpráva star, která provede tento kód: ^ PPRepeatingParser on: self V proměnné parser tedy nakonec máme instanci PPRepeatingParser, která obsahuje referenci na instanci PPPredicateObjectParser. Když pošleme této instanci zprávu parse, provede se rekurzivní sestup, který můžeme formálně popsat gramatikou: KAPITOLA 4. IMPLEMENTACE 23 S => D | SD D => 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 Třída CommandParser je potomkem třídy PPCompositeParser, která umožňuje velmi přehledné a snadné vytváření složitých gramatik a postupné sestavování gramatiky za běhu. Celý průběh parsování je možné ukázat na příkladu zpracování výstupu z příkazu svn info. Příkaz vrací textový výstup s položkami oddělenými odřádkováním, např.: Path: . URL: https://swing.fit.cvut.cz/svn/stx/libsvn/branches/libsvn2 Repository Root: https://swing.fit.cvut.cz/svn/stx/libsvn Repository UUID: 7f386b4d-09f9-4445-b346-7557cd2bf498 Revision: 485 Node Kind: directory Tento výstup je předán metodě CommandParser»parseInfo: parseInfo:aStream startSymbol := #info. ^ self parse:aStream asPetitStream collection asString. Metoda nastaví startovací symbol jako metodu info a pošle sama sobě zprávu parse. Metoda parse pošle zprávu parse novému parseru, který je vyroben v metodě #info: ^ (infoEntry, endLine, endLine) plus ==> [:nodes | |entries| entries := OrderedCollection new. nodes do:[:node | entries add:(node at:1). ]. entries ] Metoda vytvoří parser, který očekává infoEntry následovaný dvěma odřádkováními, přičemž tato sekvence se může opakovat. InfoEntry je atribut (neboli instanční proměnná) třídy CommandParser. Tento atribut byl přednastaven v metodě parse hodnotou, kterou vrátila metoda stejného jména, tedy CommandParser»infoEntry. Díky tomu, že se během parsování všude pracuje s těmito atributy, můžeme kterýkoliv z nich změnit a tím za běhu upravit chování celého parseru. Poprvé je zde použita zpráva ==>, která umožňuje dodatečné zpracování zparsovaných dat. Parametrem zprávy je blok, který přijímá pole zparsovaných prvků. Následuje zkrácený výpis dalších metod podílejících se na parsování prvního řádku výstupu, tedy cesty. KAPITOLA 4. IMPLEMENTACE 24 infoEntry ^ (pathLine, endLine, urlLine, endLine, "..." ) ==> [:nodes | SVNv2::Info new path:(nodes at:2) asString; "..." ]. pathLine ^ ’Path: ’ asParser, path. path: ^ (#word asParser / #digit asParser / $/ asParser / $. asParser / $_ asParser) star. Uvedené metody se velmi podobají funkcím, které se zapisují při tvorbě běžného syntatického analyzátoru rekurzivním sestupem. Je ale vhodné poukázat na fakt, že zde uvedené metody jsou o poznání jednodušší a intuitivnější. Zároveň se nikde v kódu nevyskytuje volání metod show a compare, které bývají při psaní syntaktických analyzátorů používány. 4.2 Implementace druhé vrstvy Druhá vrstva knihovny je implementována tak, že vnitřně používá rozhraní první vrstvy, ale uživatele od ní zcela odstiňuje. Základní použití druhé vrstvy může vypadat například takto: repo := Repository new baseUrl: ’https://...’ wc := repo makeWorkingCopyIn: ’.’ entry := wc / ’a.txt’. content := entry contents. Rozhraní druhé vrstvy je natolik obecné, že by první vrstva mohla být nahrazena vrstvou pracující s jiným verzovacím systémem, aniž by to ovlivnilo její použití. Komplikace by samozřejmě nastaly v případě, že bychom chtěli místo Subversion použít decentralizovaný verzovací systém, například dnes velmi populární Git, protože s decentralizovanými VCS se pracuje odlišným způsobem. Pro zvýšení odezvy uživatelského rozhraní a snížení objemu přenášených dat se napříč druhou vrstvou používá tzv. lazy-loading. To v jednoduchosti znamená, že data, která lze získat pouze ze serveru, se načítají až v okamžiku, kdy jsou opravdu potřeba. Uvedeným způsobem je realizováno například načítání obsahu vzdáleného souboru v metodě Entry»contents: KAPITOLA 4. IMPLEMENTACE contents ifNil:[ contents := Command cat:revision repository baseUrl, ’/’, name revision:revision revisionNumber number. ]. ^ contents 25 Kapitola 5 Testování Testování mělo v tomto projektu minimálně stejně důležitou roli jako implementace, což může to znít pro mnohé překvapivě. Vyplývá to především z toho, že cílem práce bylo zejména navrhnout dobře použitelné rozhraní (API). Aby bylo dosaženo cíle, byly nejprve napsány testy, které měly na jednotlivých případech užití ukázat vhodnost zvoleného návrhu. 5.1 5.1.1 Teorie testování Motivace Primárním účelem testování je měření kvality software[3], tedy zjišťování, zda kód dělá to, co dělat má a jestli neobsahuje chyby. S příchodem metodik jako je TDD dostalo testování další roli - pomáhat při návrhu rozhraní software. Kromě těchto dvou přímých pozitivních vlivů na kvalitu kódu, může testování ovlivňovat kód i nepřímo. Pokud je totiž software řádně pokrýván jednotkovými (unit) testy, dojdou dřív nebo později vývojáři ke zjištění, že je potřeba psát dobře testovatelný kód. Dobře testovatelný kód lze většinou napsat jen tehdy, když se napříč kódem používá dependency injection. Díky použití dependency injection se kód stane méně provázaným (tzv. looselycoupled ) a lépe znovupoužitelným. 5.1.2 Druhy testů Testy se rozdělují na dva základní druhy: testy manuální a testy automatické. Manuální testy jsou takové, které vývojář nebo tester provede ručně a pro jejichž zopakování je zapotřebí lidská interakce. Za manuální testy lze považovat veškeré kontroly, které vývojář provádí během vývoje software. Každé zkontrolování správnosti obsahu webové stránky, každá kontrola výstupu skriptu, každé vypsání obsahu proměnné a každé krokování programu za účelem ověření hodnot v proměnných, jsou manuálními testy. Oproti tomu testy automatické mohou být spouštěny opakovaně a bez potřeby jakéhokoliv zásahu ze strany vývojáře. Automatické testy je potřeba pouze vytvořit a udržovat, 26 KAPITOLA 5. TESTOVÁNÍ 27 samotné spouštění testů může probíhat na integračním serveru, který bude posílat vývojářům upozornění v případě, že některý z testů selže. Automatických testů existuje velké množství druhů, např.: • jednotkové (unit) testy • integrační testy • systémové testy • GUI testy • zátěžové testy 5.1.3 Unit testy Jak již napovídá jejich název, cílem jednotkových testů je otestovat jednu jedinou jednotku kódu, což ve světě OOP znamená zpravidla jednu třídu. Principem unit testů je izolovat co nejvíce testovanou třídu od ostatních částí aplikace. To v praxi znamená, že pokud má třída nějaké závislosti (pomocí skládání pracuje s dalšími třídami), nahradíme je v testu tzv. dvojníky 1 . Dvojník je objekt, který má stejné rozhraní jako původní třída, ale značně zjednodušenou (případně prázdnou) implementaci. Princip nahrazení závislostí za dvojníky je zobrazen na obrázcích 5.1 a 5.2. Obrázek 5.1: Třída ArticleService s jejími závislostmi Obrázek 5.2: Třída ArticleService s nahrazenými závislostmi Podle toho, jak jsou dvojníci v testech používány a jakou mají implementaci, je možné je rozdělit do čtyř skupin [2]: • Dummy je objekt, které je sice předáván, ale ve skutečnosti není v testované třídě použit. Pokud se s dummy objekty v testech pracuje, může to signalizovat chybu v návrhu aplikace. 1 Z anglického “test double” KAPITOLA 5. TESTOVÁNÍ 28 • Fake je objekt, který sice má fungující implementaci, ale není z nějakého důvodu vhodné ho používat v produkčním prostředí. Příkladem může být třída ukládající obrázky na disk místo do vzdáleného úložiště (CDN). • Stub je objekt, jehož metody vrací při zavolání zpravidla pouze jednu a tu samou hodnotu. • Mock je objekt, který kromě toho, že může vracet v závislosti na parametrech různé hodnoty, umožňuje také ověřit počet zavolání metody a správnost předaných parametrů. V uvedeném případě s třídou ArticleService by mohl dvojník implementovaný jako mock kontrolovat, zda je metoda ArticleRepository»getAll zavolána právě jednou a s očekávaným parametrem. Izolací testované třídy od zbytku aplikace získávají unit testy pro vývojáře velmi ceněné vlastnosti: • Protože testovaná třída pracuje pouze s několika dvojníky a ty už nemají žádné další závislosti, jsou jednotkové testy ve výsledku velmi málo náročné na spotřebu paměti i na výpočetní výkon. Dále se v unit testech vyhneme práci s databází a se sítí, což bývají úzká hrdla aplikace. Výsledkem je tedy velmi dobrá rychlost testů. • Jelikož testovaná třída nepracuje se svými skutečnými závislostmi, nemohou testy selhat v důsledku chyby v této závislosti. Díky tomu se dozvíme pouze o chybách, které se týkají výhradně testované třídy. Unit testy přináší přesné určení místa vzniku chyby. 5.1.4 Integrační testy Úkolem integračních testů je otestovat spolupráci několika vybraných tříd. Na rozdíl od jednotkových testů se zde nepoužívá izolace testované třídy, vývojář se naopak snaží připravit pro běh testů prostředí co nejvíce podobné produkčnímu. Integrační testy bývají pomalejší a paměťově náročnější než unit testy, mohou ale odhalit některé chyby, které jednotkové testy odhalí jen velmi složitě, např. korektnost SQL příkazu. 5.2 Testování na platformě Smalltalk/X Testování v prostředí Smalltalk/X se provádí pomocí testovacího frameworku SUnit. Základem frameworku je třída TestCase, od které dědí všechny testy. Třída poskytuje dvě základní metody pro provádění kontrol v testech: assert a should raise. Metoda assert přijímá jako parametr logickou hodnotu (boolean) a vyvolá selhání, pokud hodnota není rovna True. Metoda should raise přijímá jako první parametr blok a selže, pokud při vykonání bloku není vyhozena výjimka určená druhým parametrem. Ukázka základního testu může vypadat například takto: KAPITOLA 5. TESTOVÁNÍ 29 testAddition |result| result := 1 + 1. self assert: 2 = result. SUnit nabízí pro větší pohodlí vývojáře několik dalších metod: assertTrue, assertFalse, deny a shouldnt raise. 5.3 Testy v knihovně Při návrhu druhé vrstvy byl použit přístup Test Driven Development (programování řízené testy). Nejprve byly tvořeny testy, jejichž psaním docházelo k praktickému prověření navrženého API a jeho dalšímu zlepšování. Veškeré testy, které jsou součástí projektu, jsou testy integračními. Jednotkové testy nejsou použity, protože knihovna nepracuje s žádnými externími zdroji, které by běh testů zásadněji zpomalovaly, a není tedy potřeba používat testovací dvojníky. Testy v projektu pracují s testovacími repozitáři, které jsou součástí projektu. Repozitáře jsou uloženy v podobě dump souboru, který se před spuštěním každého testu nahraje do nově vytvořeného repozitáře. Po proběhnutí testu jsou testovací repozitáře opět smazány. Základem testů v projektu je třída TestCase, od které dědí všechny testy. Pro připravení repozitáře se používá metoda createRepositoryNamed: createRepositoryNamed:nm repositoryUrls ifNil:[repositoryUrls := Set new]. ^repositoryUrls add:(TestRepositoryResource current createRepositoryNamed:nm). Metoda pracuje se třídou TestRepositoryResource, která má na starosti vytvoření potřebných dočasných adresářů, vytvoření a načtení repozitáře a úklid po proběhnutí testu. Knihovna v současné době obsahuje 66 testů. Ty jsou pravidelně kontrolovány v integračním serveru Jenkins, který je spouští nad platformami Linux a Windows. Kapitola 6 Použití knihovny Stěžejním bodem práce bylo navrhnout snadno použitelné rozhraní pro práci se Subversion. Realizace spočívala zejména v důkladném pokrytí rozhraní knihovny integračními testy. V této kapitole bude na několika ukázkových scénářích předvedena práce s knihovnou. 6.1 Vytvoření nové revize V prvním příkladě je prezentováno vytvoření working copy, provedení změn v souboru a nakonec založení nové revize. |repo wc entry is c| repo := Repository new baseUrl:’repository URL’. wc := repo makeWorkingCopyIn:’.’. entry := wc / ’a.txt’. is := entry writeStream. [is nextPutAll:’new content’] ensure:[is close]. c := Commit new. c message:’a.txt updated’. wc commit:c. Na prvním řádku příkladu jsou definovány dočasné proměnné, které jsou v kódu dále používany. Na druhém řádku je třídě Repository zaslána zpráva new a je tak vytvořena nová instance této třídy. Instanci je následně zaslána zpráva baseUrl:, což je setter, který nastaví odpovídající instanční proměnnou. Instance třídy je uložena do dočasné proměnné repo. Na třetím řádku je instanci Repository zaslána zpráva makeWorkingCopyIn:, která provede operaci checkout a vytvoří tak novou working copy. Metoda vrátí instanci třídy WorkingCopy, která je uložena do dočasné proměnné wc. 30 KAPITOLA 6. POUŽITÍ KNIHOVNY 31 Na pátém řádku je instanci WorkingCopy zaslána zpráva /, což je alias pro getEntry:. Ve working copy je vyhledána entry podle zadaného jména a následně je vrácena instance třídy WCEntry, která je uložena do dočasné proměnné entry. Na šestém řádku je instance WCEntry zaslána zpráva writeStream, která vrátí stream pro zápis, kterým můžeme zapisovat do entry. Stream je uložen do dočasné proměnné is. Na sedmém řádku je definován blok, ve kterém instanci streamu zasíláme zprávu nextPutAll, která přidá na konec streamu nový obsah. Bloku posíláme zprávu ensure:, která provede vykonání bloku s tím, že ať už vykonání bloku proběhne v pořádku, nebo dojde k vyhození výjimky, je vždy proveden blok, který zprávě předáváme jako parametr. V tomto případě blok předaný jako parametr provede uzavření streamu pomocí zaslání zprávy close. Uzavření streamu je nezbytné, protože jinak nedojde k zápisu změn do souboru entry. Na devátém řádku je vytvořena instanci třídy Commit a uložena do dočasné proměnné c. Na desátém řádku je poslána instanci třídy Commit zpráva message:, která nastaví stejně pojmenovanou instančí proměnnou. Na dvanáctém řádku je poslána instanci WorkingCopy zpráva commit: a předána instance třídy Commit jako parametr. Výsledkem je provedení Subversion příkazu commit a tedy vytvoření nové revize, ve které jsou uloženy změny, které byly provedeny v entry. 6.2 Aktualizace working copy Druhý příklad obsahuje ukázku aktualizace working copy na poslední revizi a načtení obsahu souboru. |repo wc entry content| repo := Repository new baseUrl:’repository URL’. wc := repo makeWorkingCopyIn:’.’. wc update. entry := wc / ’a.txt’. content := entry contents. První tři řádky jsou totožné s prvním příkladem a nevyžadují proto další komentář. Na pátém řádku je instanci WorkingCopy zaslána zpráva update, která vykoná Subversion příkaz update a provede tak aktualizaci celé working copy na poslední revizi. Na pátém řádku je získána zasláním zprávy / instance třídy WCEntry. Na posledním šestém řádku je instanci WCEntry zaslána zpráva contents, která vrací obsah entry jako řetězec. Ten je uložen do dočasné proměnné content. 6.3 Práce s repozitářem Třetí případ zachycuje práci s repozitářem bez vytváření working copy. Konkrétně se z repozitáře získává revize podle čísla, z této revize se vybere jeden soubor a u něj se zjišťuje, kdo je jeho autorem. KAPITOLA 6. POUŽITÍ KNIHOVNY 32 |repo revision author| repo := Repository new baseUrl:’repository URL’. revision := repo @ 1. author := (revision / ’b.txt’) author. Na čtvrtém řádku je instanci třídy Repository zaslána zpráva @, což je alias getRevision, která vrací instanci třídy Revision. Na pátém řádku je instanci Revision zaslána zpráva / a je vrácena instance třídy RevisionEntry. Instanci RevisionEntry je zaslána zpráva author, která vrací jméno autora, který danou entry naposledy měnil. 6.4 Vyhledávání v historii Čtvrtý více komplexní případ ukazuje vyhledání poslední revize, ve které soubor WorkingCopy.st ještě neobsahuje zadaný řetězec. Tímto způsobem lze například dohledat, v jaké revizi byla do kódu zanesena chyba. |repo rev entry search resultRevision| search := ’filePath asFilename contents asString’. repo := Repository new. repo baseUrl: ’https://swing.fit.cvut.cz/svn/stx/libsvn/branches/libsvn2’. rev := repo getRevision: RevisionHead new. entry := rev / ’SVNv2__WorkingCopy.st’. entry revisions do: [ :e | resultRevision ifNil: [ (e contents indexOfSubCollection:search startingAt:1 ifAbsent:nil caseSensitive:false) ifNil: [ resultRevision := e revision revisionNumber number. ]. ]. ]. Transcript nextPutLine: ’First revision without given string is: ’, resultRevision asString. Na druhém řádku se do dočasné proměnné search ukládá řetězec, který chceme vyhledávat v entry. Na šestém řádku se posílá instanci třídy Repository zpráva getRevision, které je předávána instance třídy RevisionHead reprezentující poslední revizi. Vrácena je instance třídy Revision. Na osmém řádku je zaslána instanci třídy RevisionEntry zpráva revisions, která vrací kolekci instancí třídy RevisionEntry. V kolekci je jedna instance RevisionEntry pro každou KAPITOLA 6. POUŽITÍ KNIHOVNY 33 revizi, ve které se zadaná entry změnila. Této kolekci se posílá zpráva do:, pomocí které se kolekce iterativně prochází. Pro každý prvek kolekce je vykonán blok předaný zprávě jako parametr. V bloku se nejprve zaslání zprávy ifNil: kontroluje, zda-li již vyhledávaná revize nebyla nalezena. Pokud ne, je vykonán blok předaný zprávě jako parametr. V tomto bloku se posílá zpráva indexOfSubCollection:, která vrací pozici vyhledávaného řetězce v obsahu entry. Pokud je jako pozice vrácen nil, čili řetězec není nalezen, je číslo aktuální revize uloženo do dočasné proměnné resultRevision. Proměnná resultRevision je společně s popisem na posledním řádku vypsána. Kapitola 7 Možnosti přechodu na Git Vzhledem k tomu, že popularita distribuovaných verzovacích systémů velmi rychle narůstá, je součástí této práce analýza možnosti přechodu knihovny na DVCS, konkrétně Git. Základní výhodou distribuovaných verzovacích systémů je daleko menší závislost na internetovém připojení, protože většina operací probíhá pouze lokálně. Tím získáme nejen možnost pracovat i v prostředí se špatnou nebo žádnou konektivitou, ale výrazně se také zkrátí doba provádění většiny operací. Neméně důležitou výhodou je flexibilita architektury, kdy může například každé vývojové oddělení mít vlastní centrální repozitář a teprve vedoucí oddělení pak hotové a otestované změny dává dál do hlavního firemního repozitáře. Podobně například probíhá vývoj Linuxového jádra, kdy každý subsystém má svého správce a kopii zdrojových souborů kernelu ve svém repozitáři. Správce subsystému pak otestované změny přeposílá hlavnímu správci, který je zařazuje do hlavního vývojového repozitáře. Jen na samotném vývoji Linuxového jádra se tak aktuálně podílí necelých 400 repozitářů. Další výhodou moderních DVCS je, že zpravidla umožňují daleko snadnější práci s větvemi a některé umožňují i měnit již vytvořené commity (nebo celé větve). 7.1 Git Git se těší velké oblíbenosti zejména v komunitě kolem svobodného software, za což velkou měrou vděčí projektu Github.com, který umožnil velmi snadnou spolupráci vývojářů na open-source projektech. Git vytvořil Linus Torvalds pro potřeby verzování Linuxového jádra, kterého je také autorem. Díky cílení na verzování zdrojových kódů, které aktuálně obsahují zhruba 35 miliónů řádků a které mají velmi košatý způsob vývoje zahrnující vytváření a slučování velkého množství větví, byl při návrhu Gitu kladen velký důraz na rychlost provádění operací a snadnost práce s větvemi. Návrh Gitu byl ovlivněn projekty BitKeeper a Monotone. Git byl původně zamýšlen jako nízkoúrovňový framework pro tvorbu verzovacích systémů, ale postupem času se z něj stal plnohodnotný verzovací systém obsahující všechny potřebné funkce. 34 KAPITOLA 7. MOŽNOSTI PŘECHODU NA GIT 7.1.1 35 Vnitřní fungování Gitu Git se od SVN neliší pouze architekturou, ale také způsobem ukládání dat. Narozdíl od většiny verzovacích systémů totiž verzuje pouze celé projekty a při commitu neukládá rozdílové soubory (changeset), ale vytvoří snímek (snapshot) celého projektu. To znamená, že při každé změně souboru se do repozitáře vždy uloží celý jeho obsah. Tento způsob ukládání se nazývá DAG storage[9], zatímco SVN používá delta storage. Úložiště Gitu je v podstatě orientovaný necyklický graf objektů, kde každý objekt je identifikován SHA-1 otiskem. Objekty mohou být několika typů: commit, tree, blob a tag. Jejich vzájemný vztah ukazuje obrázek 7.1. Obrázek 7.1: Typy objektů a jejich vztah Commit objekt obsahuje informace o autorovi, zprávu, odkaz na tree objekt a odkaz na commit objekt, ze kterého tento vychází. tree 65b70c81bbedd324eb1d79c90a72ea2bddae82b4 parent 4b08bd0f91e2497afaa2c2a2a7db67c321a40d17 author Daniel Milde <[email protected]> 1355068975 +0100 committer Daniel Milde <[email protected]> 1355068975 +0100 initial Tree objekt je analogií adresáře v unixových systémech a obsahuje seznam názvů souborů, jejich práv a odkazů na blob objekty. 100644 blob 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 a.txt 100644 blob f761ec192d9f0dca3329044b96ebdb12839dbff6 b.txt Blob objekt obsahuje samotný obsah souboru. Když vytváříme nový commit, vytvoří se vždy nový commit objekt. Pokud měníme nějaký soubor (můžeme totiž měnit jen commit zprávu), vytvoří se také nový tree objekt. Nový tree objekt pak odkazuje na původní blob objekty souborů, u kterých nedošlo ke změně, a na nové blob objekty souborů, které se změnily. Celou situaci zachycuje obrázek 7.2. KAPITOLA 7. MOŽNOSTI PŘECHODU NA GIT 36 Obrázek 7.2: Ukázka DAG storage grafu 7.1.2 Odlišnosti v práci oproti SVN Kvůli svojí decentralizované podobě obsahuje Git navíc příkazy pull a push, které získávají a odesílají data ze/do vzdálených repozitářů. Git obsahuje koncept takzvané staging area neboli indexu, který slouží jako místo pro pohodlné vytváření commitu. Při zavolání příkazu commit se necommitují všechny změny, ale pouze ty, které byly přidány do indexu. Přidání změny do indexu se provádí příkazem add. 7.1.3 7.1.3.1 Změny v knihovně potřebné pro přechod na Git Změny v první vrstvě První vrstva, která zajišťuje volání příkazů verzovacího systému a zpracování výstupu z těchto volání, by musela být z převážné části přepsána, protože Git má velmi odlišné konzolové rozhraní a také značně odlišný formát výstupu. Způsob volání příkazů i zpracování výstupu pomocí parseru založeného na rekurzivním sestupu by mohl zůstat totožný. 7.1.3.2 Změny v druhé vrstvě Nejzásadnější změny v druhé vrstvě knihovny by způsobila skutečnost, že v Gitu figuruje working copy pouze jako integrální součást repozitáře. Dalo by se říci, že repozitář v Gitu odpovídá ve světě SVN working copy s repozitářem na stejném stroji, zatímco v Gitu vzdálený repozitář odpovídá v SVN repozitáři na jiném stroji. Nejlépe se proto jeví možnost třídu WorkingCopy zcela zrušit a její metody sloučit do třídy Repository. V Gitu je použita odlišná terminologie než u SVN, takže například třídu Revision by bylo potřeba přejmenovat na Commit. Vzhledem k internímu grafovému úložišti Gitu by bylo také vhodné místo třídy RevisionEntry použít třídy Tree a Blob. Možný návrh druhé vrstvy při použití Gitu je znázorněn na obrázku 7.3. KAPITOLA 7. MOŽNOSTI PŘECHODU NA GIT Obrázek 7.3: Class diagram druhé vrstvy knihovny při použití Gitu 37 Kapitola 8 Závěr 8.1 Zhodnocení splnění cílů Cílem práce byla navrhnout a implementovat knihovnu pro práci se SubVersion v prostředí Smalltalk/X. Zvláštní důraz měl být přitom kladen na návrh snadno použitelného API a pokrytí kódu testy. Tyto cíle byly realizovány důkladným návrhem knihovny a následnou implementací s využitím metodiky Test Driven Development, která pomohla doladit praktické detaily rozhraní knihovny. Knihovna byla implementována pomocí dvou vrstev, kdy první vrstva spouští příkazy SubVersion a zpracovává jejich výstup pomocí parseru založeného na rekurzivním sestupu s využitím knihovny PetitParser[7]. Druhá vrstva pak reprezentuje metamodel SubVersion a poskytuje vyšším vrstvám (nebo přímo uživatelskému kódu) komfortní rozhraní pro práci se SubVersion pomocí konstruktů jazyka Smalltalk. Během vývoje projektu se pozornost programátorské komunity stále více zaměřovala také na decentralizované verzovací systémy, a proto byla doplněna také kapitola zabývající se možnostmi přechodu na jeden z nejznámějších zástupců DVCS - Git. 8.2 Další pokračování projektu K úspěšnému završení tohoto projektu je třeba implementovat další dvě vrstvy knihovny, a to vrstvu umožňující verzování smalltalkového kódu a vrstvu doplňující do prostředí Smalltalk/X grafické rozhraní pro verzování. Po úspěšné implementaci těchto dvou vrstev přijde na řadu testování celé knihovny v praxi - denním používáním. Pokud se knihovna osvědčí a bude shledána dostatečně zralou, bude možná její integrace přímo do distribučního balíku prostředí Smalltalk/X, který vytváří společnost eXept Software. 38 Literatura [1] BLACK, A. P. et al. Pharo by Example. Square Bracket Associates, 2009. [2] FOWLER, M. Mocks Aren’t Stubs [online]. 2007. [cit. 22. 4. 2012]. Dostupné z: <http: //martinfowler.com/articles/mocksArentStubs.html>. [3] KANER, C. – FALK, J. – NGUYEN, H. Q. Testing Computer Software. London : International Thomson Computer Press, 1993. [4] Monticello [online]. 2012. [cit. 18. 12. 2012]. monticello/>. Dostupné z: <http://wiresong.ca/ [5] PILATO, C. M. – COLLINS-SUSSMAN, B. – FITZPATRICK, B. W. Version Control with Subversion. 1005 Gravenstein Highway North, Sebastopol : O’Reilly Media, Inc., 2008. [6] RAYMOND, E. Understanding Version Control Systems [online]. 2012. [cit. 18. 12. 2012]. Dostupné z: <http://www.catb.org/~esr/writings/ version-control/version-control.html>. [7] RENGGLI, L. Dynamic Language Embedding. 2010. Dostupné z: <http://scg.unibe. ch/archive/phd/renggli-phd.pdf>. [8] SHARP, A. Smalltalk by Example. Berne : The University of Berne, 1997. [9] VIRTANEN, T. Git for Computer Scientists [online]. 2012. [cit. 9. 12. 2012]. Dostupné z: <http://eagain.net/articles/git-for-computer-scientists/>. [10] Wikipedia participants. Smalltalk [online]. 2012. [cit. 17. 12. 2012]. Dostupné z: <http: //en.wikipedia.org/wiki/Smalltalk>. 39 Příloha A Seznam použitých zkratek API Application programming interface CDN Content Delivery Network CLI Command line interface DAG Directed Acyclic Graph DVCS Distributed version control system GNU GNU’s Not Unix GUI Graphical user interface IDE Integrated development environment ISO International Organization for Standardization OOP Object-oriented Programming OSI Open Systems Interconnection SHA Secure Hash Algorithm SQL Structured Query Language TDD Test Driven Development VCS Version-control system .. . 40
Podobné dokumenty
MapInfo Professional Printing Guide
Technologie OSGeo FDO Data Access ukládá data o geometrii do databáze SQLite database jako FGF objekty
(Feature Geometry Format). Pro informace o tom, jak jsou MapInfo objekty mapovány do FGF objek...
Skripta OMP 1535KB 4.12. 2004 05:44:10
jakékoliv dřívější zařízení. Tyto počítače umožňovaly spouštění větších a komplexnějších
programů a kombinovat operace, které byly dříve rozděleny mezi několik jednodušších strojů,
do jednoho kompl...
Objektově orientovaná tvorba softwaru
1.8.8 Delegování, alternativní aktorový model ............................................................................. 38
1.8.9 Závislost mezi objekty ...........................................