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

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...

Více

Skripta OMP 1535KB 4.12. 2004 05:44:10

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...

Více

Objektově orientovaná tvorba softwaru

Objektově orientovaná tvorba softwaru 1.8.8 Delegování, alternativní aktorový model ............................................................................. 38 1.8.9 Závislost mezi objekty ...........................................

Více

xtomj107_DP

xtomj107_DP Česká zemědělská univerzita v Praze Provozně ekonomická fakulta

Více