V mnoha případech se vyplatí doplnit vytvořenou aplikaci o konfigurační systém. Zvláště výhodné to je, pokud zpracováváte velmi podobné projekty pro více zákazníků nebo pokud chcete zákazníkovi dát možnost některá nastavení snadno modifikovat.
Typicky se pak konfigurace používá v situaci, kdy máte v jedné image více téměř stejných aplikací a chcete se vyhnout vytváření dalších zbytečných tříd pro každou z nich. Žádná databáze vám v tomto případě nepomůže, protože i tu musíte nějak každé z aplikací identifikovat zvlášť.
Konfigurace je sada atributů různých typů, které určují chování či vzhled aplikace. Vytvářejí se jako třídy odvozené od třídy WAConfiguration. Jednotlivé konfigurace mohou tvořit hierarchickou strukturu, kdy odvozená konfigurace může atributy doplňovat nebo přetěžovat jejich původní hodnoty. Na rozdíl od běžné třídní hierarchie však konfigurace mohou využívat i násobnou dědičnost.
Seaside má tři úrovně konfigurací:
- systémová konfigurace
- lokální konfigurace
- uživatelská konfigurace
Systémová konfigurace
Systémová konfigurace (WASystemConfiguration) poskytuje konkrétní sadu atributů, u nichž by měla být vždy deklarována přednastavená hodnota. Množina odvozených konfigurací v nich musí být definován ručně v metodě ancestors (standardně vrací prázdnou množinu), takže tvoří přesně definovanou hierarchii. Ta je v Seaside předdefinována takto:
WAAuthConfiguration (volitelně)
WARenderLoopConfiguration
WASessionConfiguration
WAGlobalConfiguration
Systémové konfigurace se tvoří jako singletony, takže všechny aplikace nastavené atributy sdílí, pokud jsou na ně tyto konfigurace aplikovány. Atributy systémových konfigurací nelze měnit přes webové rozhraní. Definují se pomocí instanční metody attributes.
Lokální konfigurace
Lokální konfigurace slouží k předefinovávaní hodnot systémových konfigurací, a to i pomocí webového rozhraní. Každá lokální konfigurace je vázána na jednu systémovou konfiguraci, k níž doplňuje slovník předefinovaných atributů.
Uživatelská konfigurace
Uživatelská konfigurace je obdoba lokální. Na rozdíl od ní však umožňuje za běhu uživatelem editovat kromě změn v atributech i seznam dceřiných konfigurací. Každá aplikace má standardně vytvořenu jednu uživatelskou konfiguraci.
Například výsledná hierarchie konfigurací pro jednu aplikaci obsahující násobnou dědičnost může vypadat takto:
user configuration
- login: 'squeak'
- password: 'squeak'
- rootComponet: WADispatcherEditor
- local configuration (#1)
- deploymentMode: true
- system configuration WARenderLoopConfiguration
- local configuration
- sessionExpirySeconds: 1000
- system configuration WASessionConfiguration
- local configuration
- system configuration WAGlobalConfiguration
- local configuration
- system configuration WAAuthConfiguration
- local configuration (#1)
Lokální konfigurace označené (#1) jsou stejné objekty, takže pevná hierarchie definovaná v instančních metodách ancestors systémových konfigurací zůstává zachována.
V konfigurační aplikaci jsou lokální konfigurace zobrazeny jako odkazy vedoucí na editovatelné formuláře. Naproti tomu systémové konfigurace mají přídomek defaults a slouží jako zdroje implicitních hodnot pro jednotlivé atributy. Tyto implicitní hodnoty lze modifikovat v konfigurační aplikaci Seaside v sekci Edit Configurations.
Vlastní konfigurace
Pokud si chceme vytvořit vlastní konfiguraci, napíšeme si vlastní třídu odvozenou od WASystemConfiguration. Protože není potřeba, aby byla zapojena do standardní hierarchie konfigurací, nebudeme u ní přetěžovat metodu ancestors, a necháme ji tak vracet prázdnou kolekci.
WASystemConfiguration subclass: #StoreConfiguration
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Store'
Poté u ní nadefinujeme, které atributy bude specifikovat. U nás to bude jméno obchodu a e-mailová adresa. U každého atributu se určuje jméno představující selektor, pod kterým se bude atribut v konfigurační třídě hledat. Dále se atributům přiřadí jméno skupiny, které slouží k seskupování atributů v konfigurační aplikaci.
attributes
^ {
WAStringAttribute key: #jmeno group: #'Atributy obchodu'.
WAStringAttribute key: #adresa group: #'Atributy obchodu'.
}.
Implicitní hodnoty atributů se definují pomocí stejně pojmenovaných instančních metod.
jmeno
^ 'Obchod'
adresa
^ 'neuvedeno'
Nyní již můžeme naši konfiguraci přidat k některé aplikaci. Buď tak učiníme v
hlavní konfigurační aplikaci Seaside v sekci Configuration Ancestors, nebo
programově, například v inicializační metodě kořenové komponenty.
initialize
| app |
app := self registerAsApplication: 'Obchod'.
app configuration addAncestor: StoreConfiguration localConfiguration.
Přístup k nakonfigurovaným atributům z komponent je pak jednoduchý:
renderContentOn: html
html heading: (self session application preferenceAt: #jmeno).
html anchorWithMailto: (self session application preferenceAt: #adresa).
Pokud atributy nemají definovanou hodnotu nebo pokud takový atribut neexistuje, vrací nil.
Atributy
Seaside podporuje několik druhů atributů a lze samozřejmě vytvářet i vlastní. Standardní jsou WAStringAttribute pro vstup řetězců formou textového pole, WAPasswordAttribute, který skrývá psaný obsah, WANumberAttribute pro vstup čísel (i reálných), WABooloeanAttribute pro výběr mezi volbami ano/ne a WAListAttribute pro výběr z více položek.
U atributů definovaných jako WAListAttribute se kolekce hodnot k výběru definuje pomocí zprávy options:. Hodnota atributu pak odpovídá přímo vybranému objektu a nikoliv pouze jeho textovému popisu.
(WAListAttribute key: #dataClass group: #'Atributy obchodu')
options: (Smalltalk allClasses).
Pokud si chcete vytvořit vlastní typ atributu, pro jehož vizualizaci si již nevystačíte se základními elementy, pak si musíte naprogramovat vlastní třídu atributu odvozenou od WAConfigurationAttribute, přičemž se můžete směle inspirovat existujícím kódem. Například třída atributu pro vkládání IP adresy pomocí čtyř textových polí by mohla vypadat nějak takto:
WAConfigurationAttribute subclass: #IPAddressAttribute
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Seaside-Configuration'
accept: editor with: html
1 to: 4 do: [ :i |
self renderBlock: i editor: editor with: html ].
renderBlock: index editor: editor with: html
html attributes size: 3.
html textInputWithValue: ((self originalValueIn: editor) at: index)
callback: [:v |
| ip |
(v asInteger between: 0 and: 255) ifFalse: [ self error: 'Wrong IP address' ].
ip := self originalValueIn: editor.
ip at: index put: v asInteger.
editor configuration takeValue: ip forAttribute: self ] .
originalValueIn: editor
^ (editor configuration valueForAttribute: self) ifNil: [ ByteArray new: 4 ].
Hodnota atributu je tvořena čtyřmi byty, přičemž každé editační pole modifikuje jeden z nich.
Pro pozorné čtenáře minulého dílu by nemusel být problém uhodnout, proč tyto tři metody nelze sloučit do jedné bez toho, aby se v ní kód pro vykreslení editačního pole musel otrocky uvést čtyřikrát.