Concepte avansate JUL – partea a 2-a

jÎn articolul anterior am văzut că JUL poate genera arbori de componente și poate gestiona lucrul cu obiecte de configurare.
Dar JUL are nai mulți ași în mânecă atunci când se discută de spațiile de nume de configurare.

Spații de nume – utilitate și relativitate

Un spațiu de nume în JavaScript e un obiect global care stochează diverse alte obiecte și funcții. Istoric, spațiile numea au fost folosite pentru a permite existența non-conflict a mai multor biblioteci de unelte JavaScript în același mediu runtime (pagină web). Prin folosirea spațiilor de nume, autorii au obținut nu doar utilizarea clară bon-conflictuală a scopului global, dar și organizarea structurală inerentă a utilitarelor lor. Mai recent, rolul spațiilor de nume de a încărca și de a utiliza bibliotecile JavaScript într-un mediu de lucru a fost luat de către module, exemplul cel mai relevant fiind CommonJS în Node. Unii spun că, odată cu consacrarea modelului modulelor, spațiile de nume sunt învechite, dar spații de nume înseamnă structură, iar structura nu e niciodată învechită.
JUL oferă o serie de utilitare pentru lucrul cu spații de nume: JUL.ns(), JUL.get(), JUL.Instance().
Dacă doriți să creați un obiect într-un spațiu de nume dat, chiar dacă spațiul de nume nu există, puteți folosi JUL.ns().
Dacă doriți să regăsiți un obiect de la calea sa în scopul global (succesiunea de nume de proprietăți separate prin punct), puteți folosi JUL.get().

// crearea unui spațiu de nume
var oNS = JUL.ns('@.non-standard.tools & libs');
JUL.apply(oNS, {
	setting1: 'Custom NS',
	setting2: ['first', 'second', 'third'],
	method1: function() {
		console.log('Method 1');
	}
});
// obținerea unui membru al spațiului de nume
console.log(JUL.get('@.non-standard.tools & libs.setting2.2'));

Dar spațiile de nume nu sunt neapărat legate de scopul global (absolute), ele putând fi folosite relativ la un scop local.
Cea mai puternică utilizare în scop a spațiilor de nume este dată de clasa JUL.Instance(), care face toate operațiile de spațiu de nume, JUL.get() sau JUL.ns(), relative la un spațiu de nume de bază (rădăcină). Pentru orice parser-i și referințe create de JUL, se poate folosi utilitarul JUL.getInstance() pentru a obține instanța originală ce a creat obiectul.

var sNS = '#.non-standard.~tools~';
var oLocal = {};
// crearea unui spațiu de nume relativ
var oNS = JUL.ns(sNS, {}, oLocal);
JUL.apply(oNS, {
	property1: {x: 5, y: 3},
	property2: /^new/i,
	'a-method': function() {
		console.log('A method');
	}
});
// obținerea unui membru al spațiului de nume relativ
var fCall = JUL.get(sNS + '.a-method', oLocal);
fCall();
// crearea unei instanțe JUL atașată la un spațiu de nume
var jul = new JUL.Instance({nsRoot: oNS});
console.log(jul.get('property1.x'));
// obținerea unei instanțe implicite
var oParser = new JUL.UI.Parser({
	nsRoot: oNS,
	yopDown: true,
	useTags: true
});
console.log(JUL.getInstance(oParser).get('property2'));

Preluarea și punerea în scop a metodelor

Mai multe utilitare JUL precum JUL.UI.factory() sau JUL.UI.createDom() acceptă o cale punctată acolo unde se așteaptă o funcție / un callback. Intern, această cale e rezolvată la obiectul concret utilizând JUL.get().

var oTree = {
	tag: 'div',
	css: 'wrapper',
	children: [
		{tag: 'input', id: 'input-name', value: 'A text', listeners: {change: 'NS.lib.defaultChange'}},
		{tag: 'button', id: 'button-go', html: 'Go', listeners: {click: 'NS.lib.defaultClick'}}
	]
};
var oParser = new JUL.UI.Parser({
	defaultClass: 'html',
	customFactory: 'JUL.UI.createDom',
	topDpwm: true,
	useTags: true
});
var oNS = JUL.ns('NS.lib');
JUL.apply(oNS, {
	defaultClick: function() {
		console.log('Button clicked');
	},
	defaultChange: function() {
		console.log('Text changed');
	}
});
oParser.create(oTree, null, window.document.body);

Aceasta îi permite unui obiect de configurare să stocheze callback-uri nerezolvate la momentul încărcării lor. Dar, dacă vreți ca aceste callback-uri să fie apelate într-un scop dat, puteți utiliza JUL.makeCaller() pentru a crea un wrapper cu cache și cu scop fixat pentru o anumită funcție (similar la ES5 Function.prototype.bind()). Când se atașează listener-i la un element DOM, JUL.UI.createDom() face legarea automat dacă furnizați o proprietate scope în obiectul de configurare listeners.

var oTree = {
	tag: 'div',
	css: 'wrapper',
	children: [
		{tag: 'input', id: 'input-name', value: 'A text', listeners: {scope: 'NS.lib', change: 'NS.lib.defaultChange'}},
		{tag: 'button', id: 'button-go', html: 'Go', listeners: {scope: 'NS.lib', click: 'NS.lib.defaultClick'}}
	]
};
var oParser = new JUL.UI.Parser({
	defaultClass: 'html',
	customFactory: 'JUL.UI.createDom',
	topDpwm: true,
	useTags: true
});
var oNS = JUL.ns('NS.lib');
JUL.apply(oNS, {
	clickMsg: 'Button clicked',
	changeMsg: 'Text changed',
	defaultClick: function() {
		console.log(this.clickMsg);
	},
	defaultChange: function() {
		console.log(this.changeMsg);
	}
});
oParser.create(oTree, null, window.document.body);

Escaping cale punctată

La utilizarea șirului de caractere cale spațiu de nume (cale punctată) pentru preluarea unui obiect, există o serie de aspecte la care trebuie avut grijă.
Fiecare segment descrie un șir proprietate de obiect, astfel că poate avea un format mult mai larg decât șirurile nume de variabilă sau indecși de vector. Va fi folosit un singur separator (aducă un punct) pentru a separa două segmente ale căii, iar calea poate fi folosită pentru a traversa orice obiecte de configurare, inclusiv vectori.
Pentru a acoperi toate cazurile de utilizare, JUL oferă o sintaxă escape pentru o cale punctată, în care toate punctele prezente într-un șir proprietate sunt precedate de un backslash atunci când sunt utilizate înăuntrul căii.
Toate rutinele JUL care folosesc căi punctate, cum ar fi JUL.ns() sau JUL.get(), suportă această sintaxă.

var oNS = JUL.ns('NS.dotted\\.segment');
JUL.apply(oNS, {
	property: {a: 'A', b: 'B', c: 'C'},
	'dotted.method': function() {
		console.log('Dotted access');
	}
});
var fCall = JUL.get('NS.dotted\\.segment.dotted\\.method');
fCall();

Replacer-i JSON și prefixe JSON

Unul din scopurile JUL este serializarea consistentă a obiectelor de configurare. Pentru a realiza aceasta, JUL le convertește într-un format intermediar JSON extins înainte de generarea șirului de cod JavaScript final.
Locul unde se poate controla cum arată JSON-ul generat este callback-ul JUL.UI._jsonReplacer, ce joacă rolul celui de-al doilea parametru al JSON.stringify(). În interiorul acestuia, utilizatorul poate schimba ce obiecte devin șiruri de caractere și cum vor arăta ele. Implicit, în afară de tipurile standard JSON, JUL e capabil să serializeze: funcții, obiecte regex, obiecte dată. Adițional, este ușor să se obțină la ieșire rezultat pentru orice tip cunoscut de obiecte pe cază de constructor prin prefixarea apelului lor cu cuvântul cheie ‘new’.

// un constructor ce stochează o copie a unui obiect
var Store = function(oConfig) {
	this._config = JUL.aply({}, oConfig);
};
Store.prototype.serialize = function() {
	return JUL.UI.obj2str(this._config);
};
// un parser personalizat ce poate serializa obiecte Store
var oParser = new JUL.UI.Parser({
	_jsonReplacer: function(sKey, oValue) {
		if (oValue instanceof Store) {
			return 'new Store(' + oValue.serialize() + ')';
		}
		else {
			return JUL.UI._jsonReplacer(sKey, oValue);
		}
	}
});
// producerea formei JSON pentru un obiect de configurare mixt
var oConfig = {
	dates: [new Date(), new Date() + 144000e3, new Date() + 288000e3],
	regexs: {begin: /^\s+/, end: /\s+$/},
	'a-method': function() {
		console.log('A method');
	},
	'a-store': new Store({
		item1: 'An item',
		item2: [0, 2, 4],
		item3: /(red|green|blue)/
	})
};
console.log(oParser.obj2str(oConfig, true));

Deși șirurile generate au un format corelat cu tipurile lor, nu se poate deduce ne-echivoc aceea că un șir cu același format este obținut dintr-un obiect serializat. Pentru cazuri mai avansate, când controlul asupra codului generat este mai strict, JUL oferă conceptul prefixelor JSON.
Un prefix JSON este un șir unic ce precedă șirul serializat al unui anume tip de obiect.
Prefixele JSON sunt activate de către proprietatea JUL.UI._usePrefuxes și sunt stocate în hash-ul (obiectul) JUL.UI._jsonPrefixes. Predefinit, acestea sunt: prefixul pentru funcții, prefixul pentru sintaxa regex, prefixul pentru construcția ‘new’, dar ele pot fi adăugate sau modificate după necesități. Prefixele apar doar la forma serializată JSON a arborelui de configurare, dar nu și în forma serializată JavaScript, la care codul este direct rulabil.

var oConfig = {
	xclass: 'FWK.Dialog',
	autoCenter: true,
	avatar: /(hat|suit|shirt)/i,
	validity: [new Date(Date.UTC(2017, 10, 3)), new Date(Date.UTC(2018, 10, 3))],
	listeners: {
		done: function(bQuit) {
			if (bQuit) {
				this.close();
			}
		},
		show: function(nLeft, nTop) {
			this.drawAt(nLeft, nTop);
		}
	}
};
var oParser = new JUL.UI.Parser({
	_usePrefixes: true,
	_tabString: '    '
});
console.log(oParser.obj2str(oConfig, true));

Referințe de cod și decoratori

Există cazuri în care este necesar să generăm o secțiune specifică de cod ce nu poate fi stocată ca obiect de configurare. Un exemplu recurent este generarea codului altor unelte sau biblioteci JavaScript cu spațiu de nume global care sunt încărcate înainte de încărcarea configurației.
Utilitara de serializare JUL.UI.obj2str() identifică aceste șiruri de cod folosind JUL.UI.referencePrefix și le generează fără ghilimele la ieșire indiferent de replacer-ul JSON și de șirurile de prefixe JSON.

var oConfig = {
	tag: 'div',
	id: 'panel-tools',
	css: 'blue-panel',
	'data-control': 'Test.UI.Panel',
	'data-options': {
		instantiator: '=ref: Test.ContainerBase',
		bounding: [0, 0, 400, 200]
	}
};
var oParser = new JUL.UI.Parser({
	defaultClass: 'html',
	customFactory: 'JUL.UI.createDom',
	topDown: true,
	useTags: true
});
console.log(oParser.obj2str(oConfig));
// JUL.UI.createDom() respectă de asemenea referințele de cod din atributele elementelor
oParser.create(oConfig, null, window.document.body);

La o privire mai atentă asupra metodei JUL.UI.obj2str(), observăm că putem schimba codul JavaScript rezultant pe măsură ce traversăm șirul JSON. JUL folosește aceste puncte de acces pentru a insera cod personalizat înaintea cheilor obiectului serializat. Primul caz de utilizare al acestor segmente de cod, denumite decoratori de cod, este inserarea comentariilor bloc pentru documentare, cum o face de ex. JCS (JUL Comment System).

var oConfig = {
	property1: 'First name',
	property2: ['a', 'b', 'c'],
	metod1: function() {
		console.log('Method 1');
	}
};
var fDecorator = function(sContent, sPath, sIndent) {
		if (!sPath) { return sContent; }
		var nParts = sPath.split('.').length;
		if (nParts > 1) { return sContent; }
		var oRef = JUL.get(sPath, this);
		var sText = '/**' + '\n' + sIndent + ' * ';
		switch (typeof oRef) {
		case 'function':
			sText = sText + 'Method description';
		break;
		default:
			sText = sText + 'Property description';
		}
		sText = sText + '\n' + sIndent + ' */';
		sText = sText + '\n' + sIndent + sContent;
		return sText;
};
var oParser = new JUL.UI.Parser();
console.log(oParser.obj2str(oConfig, false, fDecorator));

Extensibilitatea colecțiilor membri

Pentru a parsa un arbore de configurare, JUL trebuie să știe ce membri ai obiectului de configurare reprezintă configurații de componente copil. Acest tip de informație este dată de JUL.UI.childrenProperty și de JUL.UI.membersProperties ale parser-ului. JUL.UI.childrenProperty este proprietatea principală, în timp ce vectorul JUL.UI.membersProperties poate conține nume suplimentare ale altor membri descendenți ai obiectului, iar acești membri pot fi reduși la o formă unificată folosind JUL.UI.expand(). După expandare, singurul membru copii va fi acela cu valoarea proprietății JUL.UI.childrenProperty.
Dar, există situații în care proprietățile membri / copii ale unui obiect de configurare depind de clasa componentei acelei configurații. Pentru aceste cazuri JUL oferă JUL.UI.membersMappings care asociază vectorii membri la nume de clase. Mai mult, dacă un obiect de configurarea are o proprietate specială dată de valoarea lui JUL.UI.instantiateProperty, parser-ul ține cont de această proprietate membri atunci când parsează obiectul respectiv.

var oTree = {
	xclass: 'Dialog',
	title: 'Title',
	content: [
		{xclass: 'BorderLayout', top: [
			{xclass: 'BorderLayoutArea', content: [
				{xclass: 'MenuBar', items: [
					{xclass: 'MenuItem', text: 'Menu 1', submenu: [
						{xclass: 'Menu', items: [
							{xclass: 'MenuItem', text: 'Submenu 1'}
						]}
					]},
					{xclass: 'MenuItem', text: 'Menu 2', submenu: [
						{xclass: 'Menu', items: [
							{xclass: 'MenuItem', enabled: false, text: 'Submenu 2'}
						]}
					]}
				]},
				{xclass: 'Toolbar', items: [
					{xclass: 'TextField', value: 'Value'},
					{xclass: 'Button', text: 'Button'}
				]}
			]}
		],
		begin: {
			xclass: 'BorderLayoutArea', content: [
				{xclass: 'ListBox'}
			]
		},
		center: {
			xclass: 'BorderLayoutArea', content: [
				{xclass: 'HTML', content: 'Content'}
			]
		}}
	]
};
var oParser = new JUL.UI.Parser({
	childrenProperty: 'items',
	membersMappings: {
		BorderLayout: ['top', 'bottom', 'begin', 'end', 'center'],
		BorderLayoutArea: 'content',
		Dialog: ['content', 'buttons'],
		MenuItem: 'submenu'
	},
	customFactory: function(oConfig) {
		console.log('Creating ' + oConfig.xclass);
		return oConfig;
	}
});
oParser.create(oTree);

JUL are de asemenea un mod ‘sparse’, în care parser-ul încearcă să traverseze orice obiect copul al configurației curente pentru a căuta configurații descendente de instanțiat. În acest mod, care este declanșat de al patrulea argument al metodei JUL.UI.create(), nu este presupusă nici o clasă implicită pentru un obiect ce nu are setată proprietate clasă. În modul sparse, orice obiecte ce nu sunt configurații de componente vor fi copiate la obiectele / instanțele de ieșire cu aceeași structură de apartenentă.

Concluzie

Folosind JUL puteți controla multe aspecte ale arborilor de configurare și ale instanțierii lor în componente. Aceasta se aplic sarcinilor de creare, dar nu numai acestora. De fapt, JUL urmărește să fie o bază pentru dezvoltarea rapidă a unei aplicații web.
Filozofia care stă în spatele JUL și a setului său de unelte este aceea că o persoană nu trebuie să plătească pentru a-și putea exprima creativitatea sa pozitivă.
Platforma ce oferă acces gratuit și public la o astfel de dezvoltare este limbajul JavaScript. Acesta, împreună cu utilitarele sale client și server, este cu adevărat un limbaj de programare universal, depășind obstacolele întâmpinate de alte limbaje gratuite când au construit pe baza unui cod proprietar.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Copyright © 2012 - ZB Constructorul Zonal
Frontier Theme