Předávání otagovaných služeb jiným službám – „agregátorům“

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Filip Procházka
Moderator | 4668
+
0
-

Když už máme ty tagy, občas je potřeba otagované služby někam předat. Dejme tomu, že mám otagované komponenty, nebo filtry pro Assetic, to je jedno. Každopádně, teď mám dvě možnosti, jak naložit s hromádkou služeb, které mají nějaké tagy.

Jak to funguje teď?

1) Compile-time předání natvrdo

Vytvořím si vlastní CompilerExtension a v něm si všechny služby najdu

$components = $buider->findByTag('component'); // callbacky na továrničky (shared = false)
$filters = $buider->findByTag('filter'); // služby (shared = true)

a předám si je kam potřebuji. Čisté, jednoduché, funkční. Ale není to lazy. Co si budem povídat, předat někam 20 objektů, které můžou být klidně i pořádné molochy a pak z toho použiju jenom třeba 2, je zbytečné.

2) Run-time lazy accessor

Příklad implementace. Je to podle mě relativně čisté, udělal jsem malý ústupek v jedné třídě, aby zbytek aplikace neviděl na Container a díky tomu mám všechny potřebné služby lazy.

Jak to vylepšit?

První krok je odložit Container někam, kde na něj bude ještě méně vidět. Třeba do samostatné třídy, která
mi bude umět podle tagu vrátit služby.

services:
	componentsFactory:
		class: Kdyby\ComponentsFactory
		arguments: [Kdyby\Config\TaggedServices(component)]

Kdyby\Config\TaggedServices mi přijímá v druhém argumentu Container, ale to mě nemusí zajímat, protože se tam doplní díky autowiringu (cool magie!). Ve službě componentsFactory mám teď přístupné všechny továrničky, které potřebuji. Nemusím sahat na Container a jejich vytváření je maximálně líné.

Jak to ještě vylepšit?

Co takhle, kdyby stačilo zapsat

services:
	componentsFactory:
		class: ComponentsFactory
		arguments: [@#component]

Otázky

  • Myslíte si, že by podobná třída jako je moje TaggedServices, měla v Nette místo? Jestli ne, tak proč ne? Výhody jsou zřejmé.
  • Myslíte si, že by dávala smysl úprava neon syntaxe?
    • mřížka je průser, protože je to prostě komentář a blbě se to rozlišuje, napadá vás lepší znak?
Jan Tvrdík
Nette guru | 2595
+
0
-

Problém s mřížkou řeší uvozovky ["@#component"]. Vzhledem k tomu, že zatím nikde nepoužívám tagy, tag to ale nepotřebuji.

Filip Procházka
Moderator | 4668
+
0
-

Pak to ale ztrácí speciální význam pro Neon. Bez uvozovek by se to chovalo jako NeonEntity, což se pak lépe zpracovává než string a Nette už to používá (anonmyní služby).

Editoval HosipLan (4. 4. 2012 13:22)

David Grudl
Nette Core | 8218
+
0
-

@#component by mělo fungovat, obarvovač kecá.

Asi by to chtělo use-case, protože moc nechápu, o co ti jde. Nerozumím ani „odložení Container někam, kde na něj bude ještě méně vidět.“

Filip Procházka
Moderator | 4668
+
0
-

odložení Container někam, kde na něj bude ještě méně vidět.

Chci tím říct, že ten Container je tam prostě potřeba, jinak to z principu nemůže být lazy. Ale nechci aby s Containerem pracoval můj kód, když to může být schované za třídou frameworku (jako je například NestedAccessor)

Tak tedy rozvedu příklady z prvního příspěvku.

Assetic

Zaregistruju si služby

services:
	lessFilter:
		class: Assetic\Filter\StylusFilter
		tags: {filter: less}

	coffeeFilter: Assetic\Filter\CoffeeScriptFilter
		tags: {filter: coffee}

Teď ty služby potřebuju předat do FilterManageru a pokud možno co nejvíc jednoduše.

services:
	filterManager: Kdyby\FilterManager(TaggedServices(filter))

TaggedServices dostane jméno tagu a Container, vytáhne si jména služeb a když jsou potřeba, tak je předává. Do třídy se mi dostane accessor pro všechny služby, které mají tag „filter“

class FilterManager
{
	// předal jsem filtry, ale žádný se nevytvořil
	function __construct($filters) { $this->filters = $filters; }
}

Pokud teď posílám requesty na ty jednotlivé soubory, tak se mi budou buildovat podle toho, který filtr bude potřeba. Na Less se mi použije jeden na Coffee druhý.

Komponenty

Tohle nemám ještě úplně domyšlené, ale chci mít jeden kontejner, který bude vědět jaké služby jsou továrničky na komponenty a tento se předá do presenteru. Chtěl bych tedy něco jako

services:
	componentsFactory:
		class Kdyby\ComponentsFactory(TaggedServices(component))

	myPresenter:
		class: Kdyby\MyPresenter(@componentsFactory)

TaggedServices dostane jméno tagu a Container, vytáhne si jména služeb a když jsou potřeba, tak je předává. A v presenteru

class MyPresenter
{
	function __construct($factory) { $this->factory = $factory; }

	function actionDefault($page)
	{
		$components = $this->model->findComponentsForPage($page);
		foreach ($components as $name => $component) {
			$this[$name] = $this->factory->create($component);
		}
	}
}

Chci mít zkrátka službu, která bude mít přístup k podmnožině služeb containeru, ale bez toho, abych musel předávat container do svých tříd.

A myslím si, že by se k tomu daly využít tagy. Implementace na ukázku

Editoval HosipLan (4. 4. 2012 14:46)

awsickness
Člen | 98
+
0
-

jako asi to je dobre jesete by se to dalo trosku upravit ten zapis na

services:
        componentsFactory:
                class Kdyby\ComponentsFactory(TaggedServices(component))

        myPresenter:
                class: Kdyby\MyPresenter(@#tags)

kde by se o naloudovani postarala ta sluzba sama sice nevim ted jestli by to slo nejak rozpoznat ze je to tag ale takhe by to bylo jeste lepsi.

a pak by se jen delalo

function actionDefault($page)
      {

              foreach ($components as $name => $component) {
                      $this[$name] = $this->create($component);
              }
      }

ale mozna taky blabolim.

Filip Procházka
Moderator | 4668
+
0
-

awsickness napsal(a):

ale mozna taky blabolim.

Trošku ano. Služba sama se o naroubování starat nemůže, to musí dělat DIC nebo CompilerExtension. Tedy tohle je hloupost

services:
        componentsFactory:
                class Kdyby\ComponentsFactory(TaggedServices(component))

        myPresenter:
                class: Kdyby\MyPresenter(@#tags)

Tohle by už dávalo smysl

services:
        componentsFactory:
                class Kdyby\ComponentsFactory(@#component)

        myPresenter:
                class: Kdyby\MyPresenter(@componentsFactory)

Ale to je zatím nepodstatný rozdíl a je jenom krůček k tomu, aby to fungovalo. Co se týče

$this->create($component);

tak to je úplně zcestné, protože přidáváš presenteru další odpovědnosti, které se ho vůbec netýkají.

Ale o tom tato diskuze není. Diskuze je o tom, jestli má smysl, implementovat podporu pro předávání X služeb přes lazy accessor.

Honza Marek
Člen | 1664
+
0
-

Přijde mi to jako těžko pochopitelnej okrajovej případ. Což je pro mě kombinace, která nepatří do frameworku :)

Ondřej Brejla
Člen | 746
+
0
-

David Grudl napsal(a):

@#component by mělo fungovat, obarvovač kecá.

Jaké tedy máš pro commenty pravidlo, pokud # předchází cosi (třeba referenční zavináč), pak je vše ok? Pokud ne, tak je to comment? Ze zdrojáků se mi to luštit nechce (ani chuť ani čas ;). Díky.

Filip Procházka
Moderator | 4668
+
0
-

<ot>@Ondřej Brejla spíš si myslím, že jde o kontext a tohle má větší přednost</ot>

Patrik Votoček
Člen | 2221
+
0
-
  1. @#tag co to provede s dodatečnejma informacema pro tag (ve tvém případě je to ‚less‘ a ‚coffee‘)?
  2. vytváří se kolekce accessorů což je tak trochu wtf (nehledě na to že Accessor jako takový v Nette (ještě) není)

Honza Marek napsal(a):

Přijde mi to jako těžko pochopitelnej okrajovej případ. Což je pro mě kombinace, která nepatří do frameworku :)

tak nějak souhlasím

Filip Procházka
Moderator | 4668
+
0
-

Patrik Votoček napsal(a):

@#tag co to provede s dodatečnejma informacema pro tag (ve tvém případě je to ‚less‘ a ‚coffee‘)?

To je naznačené v mé implementaci. Tam předám objektu TaggedServices jméno tagu a Container. Získá si seznam služeb s tagem a pak je zpřístupní. Mému use-case to stačí, navíc tam mám tuto metodu, podle které si můžu získat ještě i službu s konkrétními daty v tagu, takže například

$lessFilter = $tagged->findOneByMeta('less');

vytváří se kolekce accessorů což je tak trochu wtf (nehledě na to že Accessor jako takový v Nette (ještě) není)

To není kolekce accesorů to je accesor na podmnožinu služeb Containeru.

Honza Marek napsal(a):

Přijde mi to jako těžko pochopitelnej okrajovej případ.

Jak říká jeden můj kamarád, lepší 10 nápadů zavrhnout, než jeden geniální neříct ;)

David Grudl
Nette Core | 8218
+
0
-

Ondřej Brejla napsal(a):

Jaké tedy máš pro commenty pravidlo, pokud # předchází cosi (třeba referenční zavináč), pak je vše ok?

Všechny pravidla jsou vlastně dány těmito regexpy. Pokud token začíná #, tak je to komentář. Nicméně znak # může být použit uprostřed literalu. Takže [@#component] je ok, zatím co [#@component] je otevřená hranatá závorka a komentář.

David Grudl
Nette Core | 8218
+
0
-

Položím důležitou otázku: lze vytořit funkční presenter (tj. předat filtry atd.) i bez existence kontejneru?

Pokud provázání tříd povede k tomu, že musíš mít nějaký kontejner, není návrh dobrý. Řešení by se mělo odvíjet od opačného konce, tady máš něco, co lze udělat bez kontejneru, a poté teprve doplníš kontejner.

Ondřej Brejla
Člen | 746
+
0
-

David Grudl napsal(a):

Jasný, to jsem přesně předpokládal a obarvovátko jsem upravil. Díky.

David Grudl
Nette Core | 8218
+
0
-

Aby nedošlo k nedorozumění, psal jsem o obarvovátku zde na fóru, které je skutečně nespolehlivé.

Ondřej Brejla
Člen | 746
+
0
-

To mi došlo ;-) Nicméně jsem zmíněnou syntax vyzkoušel v Neon colorovátku pro NetBeans a taktéž propadlo :-) Ale už zase stojí na nohou a vesele barví.