Guide de développement
Nous allons utiliser la version 1.0.0 de DemocracyOS.
Stack
Node.js et NPM
Vous avez besoin de Node.js v4.0.0 ou supérieure pour exécuter DemocracyOS.
Il y a plusieurs options disponibles pour l’installer :
-
Site officiel: aussi bien Node.js que NPM sont intégrés à cette installation.
-
NVM: Node Version Manager - Script bash simple pour gérer de multiples versions actives de node.js
-
tj/n: un autre -en fait plus simple- gestionnaire de versions node
MongoDB
Nous prenons aussi en charge la dernière version de MongoDB.
Donc téléchargez la version correspondant à votre système et suivez les instructions d’installation.
Remarque : nous envisageons d’utiliser Docker à la fois pour le développement et la production afin que commencer à développer sur une nouvelle machine soit très facile et de pouvoir exécuter l’application dans le même environnement qu’en production.
</i>
Browserify
Nous utilisons Browserify pour lier le code côté client, mais nous l’utilisons comme une dépendance donc vous n’avez pas besoin d’installer quoi que ce soit.
Système d’exploitation
Actuellement nous ne prenons pas Windows en charge, donc si vous l’utilisez, vous devez installer VirtualBox avec une distribution Linux. Nous préférons Ubuntu parce que nous savons que DemocracyOS s’exécute dessus.
Configuration
Toutes les variables de configuration sont dans le fichier /config/defaults.json
. Si vous souhaitez redéfinir l’une d’entre elles, vous devez créer un nouveau fichier appelé development.json
dans le même répertoire et réécrire le nom et la valeur que vous voulez.
Également, pour passer les variables de configuration au client, nous utilisons la clé client
qui accepte une matrice des autres noms de variables de configuration.
Donc si vous souhaitez ajouter une variable appelée exemple
et la passer au client, les étapes sont :
- Ajouter la paire de valeurs de clé au fichier
defaults.json
, la clé doit êtreexemple
et la valeur ce que vous voulez. Elle doit avoir une valeur par défaut, même si c’est une chaîne vide. - Si vous voulez passer cette variable au client, ajoutez alors
'exemple'
à la matrice cliente dans le même fichier. - Si vous voulez tester une valeur différente avec la variable
exemple
, créez une fichierdevelopment.json
dans le même répertoire et écrasez la valeur par défaut.
Les noms des variables de configuration doivent être camelCased
puisqu’en production, nous faisons correspondre les Variables d’Environnement avec les Variables de Configuration de cette façon :
Dans defaults.json:
{
...
maVariable: 'par_defaut',
monAutreVariable: {
unePropriété: 10
}
...
}
En production, ces variables d’environnement doivent être:
Ma_VARIABLE="pas par défaut"
Mon_AUTRE_VARIABLE_UNE_PROPRIETE=20
Comme vous le voyez nous faisons correspondre camelCase et les objets imbriqués avec des tirets de soulignement et nous déduisons le type en utilisant le fichier defaults.json
(maVariable
est une chaîne
et unePropriété
est un nombre dans ce cas).
Exécuter l’application
DemocracyOS peut être exécuté de différentes manières, mais nous préférons juste utiliser make
dans le dossier root.
Une autre façon est d’exécuter gulp bws
pour le développement, cela attendra des modification dans les fichiers projet pour recompiler à chaque fois. Pour cela nous devons avoir gulp
installé :
npm install -g gulp
Modèles
Toutes nos données sont stockées dans MongoDB et pour s’y connecter, nous utilisons mongoose, un modelage d’objet mongodb élégant pour node.js.
Les modèles sont stockés dans le répertoire lib/models
. Si vous souhaitez ajouter un nouveau modèle, alors faites-le dans un nouveau fichier dans ce répertoire en suivant une structure similaire aux modèles que nous avons.
Après cela appelez
le dans lib/models/index.js
.
Database-API
Pour créer, lire, mettre à jour et supprimer les modèles, nous avons une couche de données qui interagit avec les modèles. Elle est appelée db-api
et est située sous lib/db-api
.
Après avoir créé un nouveau modèle, vous devez inclure un nouveau fichier dans le répertoire lib/db-api
avec son nom et et des méthodes d’export
pour :
- create
- search
- all
- findOne
- destroy
- update
- custom operation
Vous pouvez voir des exemples dans ce répertoire pour voir les db-api
s que nous avons actuellement.
Web API
Nous exposons ces appels de l’api de la base de données via une api restful. Chaque modèle
a un fichier différent qui suit la convention lib/${modelName}-api/index.js
, par exemple : lib/topic-api/index.js
.
Ces modules sont ajoutés à l’application dans lib/boot/index.js
et vous devez les ajouter comme :
app.use('/api', require('lib/${modelName}-api'));
De façon à ce qu’il puisse être accédé sur le client par l’URL : /api/modelName
.
Routing
Côté serveur
Si vous souhaitez ajouter une nouvelle route à DemocracyOS, la première chose à faire est de lui créer un nouveau module sous lib
et d’exposer une app
(une instance express
) qui nécessite lib/layout
. Par exemple :
Dans lib/mypage/index.js
/**
* Module dependencies.
*/
var express = require('express');
var app = module.exports = express();
app.get('/mypage', require('lib/layout'));
Et à la fin de lib/boot/index.js
app.use(require('lib/mypage'));
Côté client
Pour le routage côté client, nous utilisons page.js,
un micro routeur côté client inspiré par le routeu Express.
Vous devez créer un nouveau fichier dans lib/mypage/mypage.js
et inclure les lignes suivantes :
import page from 'page';
page('/mypage', (ctx, next) => {
// your page logic goes here
});
Si vous créez une page pour afficher du contenu (et non des sujets) vous devez d’abord vider et ensuite tout restituer dans l’élément #content
.
Vous pouvez faire cela simplement avec component-dom:
import o from 'component-dom';
import page from 'page';
import MyPageView from './view';
page('/mypage', (ctx, next) => {
let myPage = new MyPageView();
let el = o('#content');
myPage.appendTo(el[0]);
});
Ce qui nous conduit à… affichages.
Views
Pour créer de nouveaux affichages (le HTML que vous allez afficher pour l’utilisateur final) commencez par créer un nouveau fichier dans lib/mypage
appelé view.js
. Ceci si vous avez une route appelée de cette façon, mais si vous n’en avez pas, vous pouvez juste le créer dans lib/my-view/view.js
.
Nous avons notre propre bibliothèque Affichage
dans lib/view/view.js
qui fournit des méthodes pour :
constructor
: passe un modèle et les locales pour ce modèleswitchOn
: fonction appelée que les modèle est restitué dans le DOMswitchOff
: fonction appelée que le modèle est supprimé du DOMappendTo
: joint l’élément créé à l’intérieur de l’élément passébind
: utilisé pour lié des gestionnaires d’événement- vous pouvez en voir d’autres dans
lib/view/view.js
Vous ne devriez pas toucher à ce fichier mais plutôt l’étendre avec vos propres affichages :
import View from '../view/view.js';
import template from './template.jade';
export default class MyView extends View {
constructor (topic) {
super(template, { topic });
}
}
Modèles
Dans l’exemple précédent, nous avons utilisé un modèle. Ce fichier est écrit dans le format jade et il ne doit avoir qu’un et un seul élément conteneur. Si vous écrivez quelque chose comme :
.topic-container
.topic-body
// ...
.comments
.comment-form
// ...
Vous avez deux conteneurs racine (topic-container
et .comments
) et vous aurez des problèmes avec ça. Les modèles peuvent être passés en données dans les objets locales
: dans l’affichage lorsque vous appelez super(template, locals)
.
Stores
Les stores sont responsables de la communication avec l’API côté serveur afin d’obtenir les données nécessaires. Un store doit avoir une fonction name
définie et renvoyant le nom du modèle qui lui est relatif, parce qu’elle est utilisées pour construire l’URL de l’API.
Pour faire des requêtes sur le serveur nous utilisons notre propre mode sur lib/request/request.js
qui utilise superagent.
Comme pour les Affichages, si vous créez une nouveau store, vous devez étendre lib/store/store.js
.
import Store from '../store/store';
class MyModelStore extends Store {
name() {
return 'myModel'; // calls will be made to '/api/myModel'
}
}
Ce store est déjà lié avec des méthodes domme :
- findOne
- findAll
- destroy
Le store utilises Promises pour faire des appels asynchrones, attrapant les ‘promises’ dans une variable privée appelée _fetches
.
Pour voir un exemple complet d’un store personnalisé, vous pouvez regarder lib/topic-store/topic-store.js
.
Middlewares
Vous allez sans doute avoir besoin de données sur vos pages, pour cela vous allez créer une fonction personnalisée middleware.
Pour garder le tout organisé, nous avons créé quelques modules comme lib/topic-middlewares/topic-middlewares.js
.
Sous les routes qui nécessitent des données, vous devez inclure ces middlewares et vous devez également utiliser un store pour faire ces appels.
Quand les données sont prêtes, vous devez alors appeler next
sur ce middleware, par exemple :
export function findMyModel(ctx, next) {
myModelStore
.findOne(ctx.params.id)
.then(model => {
ctx.model = model;
next();
})
.catch(err => {
if (404 !== err.status) throw err;
log(`Unable to load model for ${ctx.params.id}`);
});
}