Skip to content

reversTeam/jobuzzle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

  1. Installation
  2. Configuration
    1. Langues
    2. Modules
    3. Routeur
    4. Service Locator
    5. Environnement
    6. Traduction
  3. Autoload
  4. Dispatcher
  5. Factory
  6. Initializer
  7. Manager
    1. Service Manager
    2. Route Manager
    3. Template Manager
    4. Model Manager
    5. Entity Manager
    6. Controller Manager
  8. Route
  9. Service
  10. Routeur
  11. ServiceLocator
  12. Translator
  13. Utility
  14. Modules
    1. Déclaration
    2. Nomenclature

<a name="installation>Installation

Clone du dépot

git clone git@gitlab.intra.jobuzzle.com:Jobuzzle/Framework-Front.git --recursive
cd Framework-Front
git submodule foreach --recursive git checkout -b g_remote origin/g_remote
git submodule foreach --recursive git checkout master
git checkout -b g_remote origin/g_remote
git checkout -b front_remote origin/front_remote
git remote add generic git@gitlab.intra.jobuzzle.com:Generic/Framework.git
git remote add front git@gitlab.intra.jobuzzle.com:Front/Framework-Front.git
git submodule foreach --recursive npm install

La configuration du framework et de ses modules sont disponibles via l'objet Config, c'est un objet global. Les dossiers de configurations sont disponibles dans chacuns de vos modules et sont totalements flexibles, elles suivent l'architecture de votre dossier Config à quelques exeptions près, en effet un lien est créé entre les configurations de vos modules et les configurations du framework pour les rendres accessible via Config['modules']['nom_du_module']. Dans le dossier Config de votre framework se trouve deux dossiers, dev et prod. C'est dans ce dossier que vous mettrez les configurations propres au différents environnements. Pour changer d'environnement, il vous suffit de modifier env dans le fichier global.yml qui se trouve dans le dossier de configuration de votre framework.

Le fichier language.yml disponible dans les configurations générales vous permettra de définir la langue par défaut de votre application, ainsi que les différentes langues disponibles. Exemple :

all:
	- EN_us
	- FR_fr
default : FR_fr

La variable default permet de définir une langue par défaut pour votre application, le Translator se référera à la langue par défaut si une traduction était indisponible.

Les configurations des modules servent à déclarer un module comme actif, ou alors à le désactiver. Si le module est désactivé, il ne sera pas chargé, et ses configurations ne seront pas accessible par le reste de votre application. Exemple :

Example: true
ModuleName: true
ModuleDisable: false

Le routeur s'occupe de construire ou de résoudre les routes qui lui sont envoyées. Ses configurations permettent de définir un controlleur et une méthode par défaut qui sera appellée lorsque le routeur cherchera à résoudre la route /. Nous déclarons aussi au routeur ce qu'il doit faire si une route est inconue ou alors si une erreur survenait. Exemple:

default: 
    controller : ExampleController
    method : home
404:
    contoller: ErrorController
    method : error404
403:
    controller: ErrorController
    method: error403

Le service locator est un objet, il est disponible partout dans votre application, le fichier de configuration détermine où se trouve la classe qu'il doit instancier et quel est le nom par lequel vous y ferez référence. On y distingue deux types d'objet, les invokables qui seront instanciés directement, et les factories, la factory est un objet intermédiaire qui vous permettra de configurer un objet avant utilisation. Exemple:

factories:
    Test : Path/To/TestFactory

invokables:
	Translator : Kernel/Translator
    RouteManager: Kernel/Managers/RouteManager
    ServiceManager: Kernel/Managers/ServiceManager
    ModuleInitializer: Kernel/Initializer/ModuleInitializer
    Router : Kernel/Router
    Dispatcher : Kernel/Dispatcher/DispatcherBase

Les configuration d'environnement vous permettrons d'intéragir différemment avec votre application en fonction de votre environnement de développement (dev) ou celui de production (prod), vous êtes alors libre de créer votre propre arboréscence de dossier qui définira alors votre tableau de configuration. L'environnement actif (Config['global']['env']) est disponible directement via l'objet de configuration sur la référence Config['env']. Par exemple si vos configurations sont semblables à celle ci : Conf/dev/server.yml

host: v1.api.local.jobuzzle.dev

Conf/prod/server.yml

host: v1.api.jobuzzle.com

Config/global.yml

env: dev

Config.env.server.host est égal à v1.api.local.jobuzzle.dev ce qui aurait été différent si vous aviez eu la configuration env: prod, dans ce cas Config.env.server.host aurait été v1.api.jobuzzle.com

Les configurations de traductions se trouve dans Config/translation/.... Vous avez un dossier pour chaque langue que vous avez configurer dans votre fichier language.yml. Dans ces dossiers se trouve un fichier routes.yml, qui sera utilisé seulement pour la traduction des routes de votre application. Vous avez donc par exemple dans ce fichiers routes.yml

create: creer
delete: supprimer
update: editer

Seul les routes ont un comportement un peu particulier car elles sont utilisées tel quel par le routeur au moment du match. Le reste des traductions aura le même comportement, elles seront toutes disponibles via un system de key, en chaine de caractères avec comme caractères séparateur .. C'est à dire que si vous créer un fichier header.yml de ce type :

menu:
    messages: Messages
    user_name: {{last_name}} {{first_name}}
    disconnect: Deconnexion

Vous utiliserez alors le translator de la facon suivante pour acceder aux différentes sections:

var t = serviceLocator.get('Translator');
console.log(t.translate('header.menu.messages'));
console.log(t.translate('header.menu.user_name', {
    "last_name" : "Doe",
    "first_name" : "John"
}));
console.log(t.translate('header.menu.disconnect'));

Comme nous venons de le voir, nous avons la capacité de faire correspondre des segments de texte avec des variables passées en paramètres à la methode translate de notre Translator, pour cela il faut que la variable soit renseignée par {{ }}

L'objet Autoload charge les modules, chaque modules hérite de ce MasterAutoload, à son intialisation, il vient charger les Traduction, les Route et les Service du module qui est en train d'être instancié. La methode qui sera appellée juste apres le constructeur, sera la méthode onBootstrap qui ne fait rien dans le parent, elle permettra aux enfants d'effectuer des actions supplémentaires si elle le désire.

Voici la déclaration basique d'un Autoload :

#import (Config.path +'/Kernel/Autoload/MasterAutoload') = MasterAutoload;

class ExampleAutoload extends MasterAutoload {

    confModuleName = 'Example';

}

#export ExampleAutoload;

L'objet Dispatcher gère les évenements, sont role permet à votre application d'avoir des points de branchement, afin de pouvoir plugger des actions à certaines étapes de l'éxecution de votre code. Pour ce faire, il met à votre disposion trois méthodes : 1. trigger : Qui permet d'émettre un événement 2. listen : Qui permet d'écouter un évenement 3. unlisten : Qui permet de se déconnecter d'un événement

Lorsque l'on écoute un événement, trois scopes s'offrent à nous séparés par des : : 1. Module : C'est le module qui lance l'événement 2. Class : C'est la classe qui a send cet événement 3. Method : C'est la méthode dans la quel l'événement a été lancé

Il est possible de définir les scopes a * afin de : Example:Test:* : Je veux listen tous les events de la classe Test du module Example *:Test:init : Je veux listen tous les events de toutes les méthodes init de toutes les classes Test de tous les modules Exemple:*:init : Je veux listen tous les init de toutes les classe du module Example *:*:* : Je veux tout listen ...

Il est également possible de transmettre les paramêtre passés au trigger à ceux qui listen :

var dispatcher = serviceLocator.get('Dispatcher');
dispatcher.trigger('Example:Test:init', 42);

Il sera ensuite catcher par une méthode de ce type :

var dispatcher = serviceLocator.get('Dispatcher');

class obj {
    initialize: function () {
        dispatcher.listen('Example:Test:init', this, this.itsInit);
    }

    itsInit: function (name, id) {
        console.log('name: ' + name + ' - id: ' + id);
        if (id == 42)
            dispatcher.unlisten('Example:Test:init', this);
    }
}

var o = new obj();

Les Factory, permettent de construire des objets et de set les attibuts de l'objet afin qu'il soit tout prêt à l'usage, vous pouvez créer plusieurs factory pour une même invokables. Afin d'avoir deux configurations d'objet pour deux contextes différents. Dans notre exemple nous souhaitont avoir un objet qui sera instancié et qui aura dans ses attributs le Translator et le Router.

Pour ce faire nous créeons notre objet invokable comme ceci :

class ExampleBookMainService {
    
    _translator = {};
    _router = {};

    #default get, set for _translator;
    #default get, set for _router;

}

#export ExampleBookMainService;

Les noms des modules doivent suivre cette convention, ainsi que les déclarations dans les managers afin de ne jamais écraser la classe d'un autre module: ModuleName``BundleName``Directory``NomDeClass``CurrentDir ./Modules/Example/Bundle/Book/Service/ExampleBookMainService

Maintenant nous lui créeons une factory :

#import (Config.path +'/Kernel/Factory/MasterFactory') = MasterFactory;

class ExampleBookServiceMainFactory extends MasterFactory {

    // Le nom de la Factory ajouter dans le manager
    invokableClassName = 'example_book_main_service';

    create : function (serviceManager) {
        // Dans obj nous avons notre invokable car c'est le parent qui se charge de nous instancié l'objet
        var obj = _super(serviceManager);

        obj.setTranslator(serviceLocator.get('Translator'));
        obj.setRouter(serviceLocator.get('Router'));

        return obj;
    }

}

#export ExampleBookServiceMainFactory;

L'objet Initializer permet l'instanciation de différents composants du Framework, tels que les Modules ou encore les Mediators. Actuellement nous avons le ModuleIntializer, c'est l'objet qui s'occupe de prendre les modules actifs et de charger chacunes des classes Autoload des modules, une fois l'objet instancié, il appelle directement la méthode onBootstrap de l'objet, comme nous le montre son code :

    loadModules : function () {
        var basePath = Config['path'] +'/Modules';
        var req = {};
        var obj = {};

        for (var i in Config['modules']) {
            req = Utility.require(basePath +'/'+ i +'/'+ i + 'Autoload');
            obj = new req();
            obj.onBootstrap();
        }
    }

Le design Manager est un objet qui permet de stocker des collections d'objets, il fonctionne un peu comme le serviceLocator, sauf qu'il n'est pas accessible dans le contexte global, mais aussi qu'il permet via la configuration shared de définir si l'objet qui sera retourné sera une nouvelle instance ou alors un singleton.

Nous avons actuellement deux type de manager RouteManager et ServiceManager. Chaque module intégre ses propres Service et ses propre routes, lorsque l'<a name"#initialize">Initializer vient charger le module, le module injecte ses Manager au sein des différents managers disponibles dans le ServiceLocator. Pour ce faire il faut respecter la déclaration de ses composants, dans le dossier Config de votre module vous avez un fichier qui sera en liaison avec le manager en relation : route_manager.yml c'est ici que l'on déclare les RouteManager de notre module afin qu'il soit injecté dans le manager principal :

invokables:
    ExampleRoute: ExampleService

service_manager.yml c'est ici que l'on déclare les ServiceManager de notre module afin qu'il soit injecté dans le manager principal :

factories:
    ExampleService: Factory/ExampleServiceFactory
invokables:
    ExampleService: ExampleService
shared :
    ExampleService : true

Le design Route permet la déclaration des routes sous forme d'objet avec la notions d'héritage, ainsi une Route instanciée par cet objet, aura directement les routes suivantes :

/{name_route}
/{name_route}/{create}
/{name_route}/:id_name_route
/{name_route}/:id_name_route/{update}
/{name_route}/:id_name_route/{delete}

Nous distinguons dans cette exemple deux des trois types de routes qui sont à notre disposition : 1. {name_route} : C'est une route de type Translation ce qui signifie que l'identifiant de la traduction est name_route et donc qu'il faut que vous ayez dans votre fichier [Modules/ModulesName/]Config/translation/route.yml la traduction correspondante. 2. :id_name_route : C'est une route de type Parameters ce qui signifie que la valeur de ce paramètre sera régi par une regex et donc qu'il devra répondre à un type. Dans notre exemple il semble logique que le pattern correspond a ceci [0-9] 3. name_route : Ce dernier type que nous n'avons pas vu dans notre exemple est un type Litteral c'est a dire qu'aucune action ne sera effectuée sur ce type de chaine, elle matchera seulement si la chaine est exactement la même. Le fait d'utiliser des objets pour effectuer la création des routes va nous permettre de nous abstenir des choses rebarbatives tel que la redéclaration dans leurs intégralité si celle-ci sont plus ou moins similaires.

Admettons que dans notre application nous ayons besoin de créer des routes pour manipuler des entreprises, que nous les identifions par leurs marque dans nos url, mais que nous devions aussi manipuler des étudiants, mais que pour les étudiant nous préferons utiliser leurs id plutot que leurs nom, login ou prenom pour préserver leurs annonymat. Nous ferons donc deux objets de route :

#import (Config.path +'/Kernel/Route/MasterRoute') = MasterRoute;

class ExampleAuthorMainRoute extends MasterRoute {

    baseName = 'Author';
    baseRoute = '{authors}';
    baseController = 'Author';

    paramName : 'author_url';
    paramRegex : '[a-z0-9-]+';

}

#export ExampleAuthorMainRoute;
#import (Config.path +'/Kernel/Route/MasterRoute') = MasterRoute;

class ExampleBookMainRoute extends MasterRoute {

    baseName = 'Book';
    baseRoute = '{books}';
    baseController = 'Book';

    paramName : 'book_id';

}

#export ExampleBookMainRoute;

Ainsi nous venons donc de créer toutes ses routes :

/{authors}
/{authors}/{create}
/{authors}/:author_url
/{authors}/:author_url/{update}
/{authors}/:author_url/{delete}

/{books}
/{books}/{create}
/{books}/:book_id
/{books}/:book_id/{update}
/{books}/:book_id/{delete}

Le design Service n'à actuellement aucune logique générique. Les Controller et les Mediator intéragirons avec ces objet.

L'objet Router s'occupe de gérer l'ensemble des routes de votre applications, que ce soit au niveau de la construction de l'ensemble des routes, des imbrications, de la résolution ou encore de la création des routes de votre applications, c'est cet objet qui fera votre affaire. Le routeur vous donne la capacité de rajouter et imbriquer vos routes de la facon suivante :

var routeManager = serviceLocator.get('routeManager');
var router = serviceLocator.get('Router');
var authorRoute = routeManager.get('exemple_author_main_route');
var bookRoute = routeManager.get('exemple_book_main_route');
router.addRoute('author', authorRoute);
router.getRoute('author').getRoute('view').addRoute('book', bookRoute);

Dans cet exemple nous venons d'imbriquer les routes de book dans celle de author, nos routes disponibles sont donc :

/{authors}
/{authors}/{create}
/{authors}/:company
/{authors}/:author_url/{update}
/{authors}/:author_url/{delete}
/{authors}/:author_url/{books}
/{authors}/:author_url/{books}/{create}
/{authors}/:author_url/{books}/:book_id
/{authors}/:author_url/{books}/:book_id/{update}
/{authors}/:author_url/{books}/:book_id/{delete}

Le routeur peut maintenant procéder au match des urls entrantes:

// Dans le cadre de la langue FR_fr
router.match('/auteurs/john-doe/livres/42/editer');

// Dans le cadre de la langue EN_us
router.match('/authors/john-doe/books/42/update');

Le serviceLocator est un objet déclaré dans le scope global de votre application, ce qui lui donne la particularitée d'être accessible partout au sein de votre application, il stock des objets de type singleton, c'est à dire que vous intéragirez toujours avec la même instance d'objet, peut importe le nombre de jets que vous effectuerez sur cette meme entitée. Vous pouvez ajouter des objets à votre serviceLocator via son fichier de condiguration disponible à cet endroit Config/servicelocator.yml. On utilise le serviceLocator de cette facon.

var serviceManager = serviceLocator.get('ServiceManager');
var router = serviceLocator.get('Router');

Le serviceLocator vous donne accès aux différents managers ainsi que des composants du Kernel comme le routeur. Pour plus de renseignement sur les capacitées de votre serviceLocator regardez votre configuration Config/servicelocator.yml.

Pour ajouter un service au sein de votre modules, vous devez aller l'ajouter dans le fichier de configuration suivant : Modules/Example/Config/service_manager.yml

factories:
    Example/Service: Factory/ExampleServiceFactory
invokables:
    Example/Service: ExempleService
shared:
    Example/Service : false

Dans le cas présent je déclare un invokable ExampleService qui sera toujours instancié via sa factory, et qui n'est pas partagé, donc qui me renverra toujours une nouvelle instantiation de l'objet.

L'objet Translator est l'objet qui s'occupe du stockage et de la résolution des différentes traductions de votre application, il reprend la nomenclature de vos déclarations des traductions au sein de vos Config/translate. Il remet a plat toutes les traductions de tous vos modules et vous permet donc d'acceder à celles-ci et de les résoudres via une simple chaine de caractère content.title. vous pouvez aussi lui passer un tableau au format JSON, si la traduction que vous désirez résoudre contient des variables. La déclaration d'une variable au sein d'une traduction dois être préfixer de {{ et post fixé par }} ce qui donne : {{ma_var}}

Le translator peut déclarer deux types de translate, celles qui sont communes à votre coeur d'application seront directement dans le dossier : Config/translation Celle qui sont spécifiques à un module : Modules/{MODULE_NAME}/Config/translation Dans ce dossier de translation, vous devez retrouver autant de dossiers, que vous avez déclaré de langues dans votre fichier de configuration : Config/language.yml

all:
    - EN_us
    - FR_fr
default : FR_fr

Dans le cas présent nous avons déclaré une langue EN_us et une langue FR_fr qui se trouve être notre langue par defaut. Dans le cas contraire, une traduction venait à manquer dans la langue courante de l'utilisateur, le Translator ira la chercher dans la langue par defaut, en cas d'echec, il renverra une chaine de ce type : {{ __EMPTY_TRANSLATION [NOM_DE_VARIABLE]__ }}

L'objet Utility est une interface en charge d'unifier les codes qui ont des spécificités, sur leurs cible d'éxécution, par exemple le processus de récupération d'une classe entre le serveur et le browser est différent. Le browser à toutes les déclarations de ses classes accessible directement depuis l'objet window, alors que le serveur utilise le require pour faire référence à l'objet. La surcouche Jpp nous permet via les #targets d'unifier l'usage au seins du Kernel, voicis notre exemple applicatif :

#import ('fs') = fs;

var Utility = {
    'require': function (filename) {
        #target server;
            filename += '.js';
            if (!fs.existsSync(filename))
                throw "Unknow file [" + filename + "]";
            return require(filename);
        #else target;
            filename = filename.split('/');
            filename = filename[filename.length - 1];
            if (!window[filename])
                throw "Unknow class [" + filename + "]";
            return window[filename];
        #end target;
    }
}

#export Utility;

Le design des Modules, sont les composants applicatifs de votre application. Le code spécifique qui va déterminer le fonctionnement de votre application. Il faut pour utiliser un Module, le déclarer dans le fichier Config/modules.yml. Sans cette déclaration il ne sera jamais instancié et donc en fonctionnement. Vous pouvez aussi désactiver un module en passant sa configuration à false dans ce meme fichier de configuration.

Exemple :

Actif : true
Inactif : false
Other : true

Dans ce cas seul les Modules : Actif & Other seront chargés

Les dossiers respectent une certaine nomenclature, afin de préserver l'unicité des classes et la lisibilité des modules, car en effet deux classes du même nom se feraient écraser dans le front vu que tout est ramené à l'objet window. Il est donc très important de réspecter le nommage des fichiers qui doit correspondre à celui des classes, ainsi que le nommage des classes qui doivent être sous cette forme: ModuleName``BundleName``Directory``NomDeClass``CurrentDir ./Modules/Example/Bundle/Book/Service/ExampleBookMainService

    Example/
        Assets/
        Bundle/
            Author/
                Route/
                    ExampleAuthorMainRoute.jpp
                Service/
                    ExampleAuthorMainService.jpp
                [...]
            Book/
                Route/
                    ExampleBookMainRoute.jpp
                Service/
                    Factory/
                        ExampleBookServiceMainFactory.jpp
                    ExampleBookMainService.jpp
                [...]
        Config/
            translation/
                EN_us/
                FR_fr/

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages