Voici un bref tableau d'ensemble, je parlerai de certains éléments plus loin.
+-----------------------+------------------------------+------------------------------------+
| | RailwayJS | Tower.js |
+-----------------------+------------------------------+------------------------------------+
| First commit | Jan 2011 | Oct 2011 |
| Rails | 2.3.x | 3.x |
| Node.js | >= 0.4.x | >= 0.4.x |
| Server | ✓ | ✓ |
| Client | | ✓ |
| Template agnostic | ✓ | ✓ |
| Default engine | EJS | CoffeeKup |
| Database agnostic | ✓ | ✓ |
| Default datastore | MongoDB | MongoDB |
| Model validations | validatesPresenceOf('email') | validates('email', presence: true) |
| Query scopes | ✓ | ✓ |
| Chainable scopes | | ✓ |
| Param parsing | | ✓ |
| Controllers | ✓ | ✓ |
| Resource controllers | | ✓ |
| File naming | users\_controller.js | usersController.coffee |
| vm.runInCustomContext | ✓ | |
| Asset pipeline | | ✓ |
| Asset compression | | ✓ |
| Routing | map.resources('posts') | @resources 'posts' |
| Nested routes | ✓ | ✓ |
| Generated url helpers | ✓ | |
| Generators | ✓ | ✓ |
| Command-line api | ✓ | ✓ |
| REPL (console) | ✓ | ✓ |
| CoffeeScript console | | ✓ |
| Asset cache method | timestamp | md5 hash |
| Production asset path | /app.css?123123123 | /app-859c828c89288hc8918741.css |
| Preferred Language | JavaScript | CoffeeScript |
| CoffeeScript support | ✓ | ✓ |
| Internationalization | ✓ | ✓ |
| Heroku support | ✓ | ✓ |
| String case | snake\_case | camelCase |
| Form builder | ✓ | ✓ |
| Semantic form builder | | ✓ |
| Table builer | | ✓ |
| File watcher API | | ✓ |
| Live-reload assets | | ✓ |
| Test suite | | ✓ |
| Generators for tests | | ✓ |
| Twitter Bootstrap | ✓ | ✓ |
| HTML5 Boilerplate | | ✓ |
+-----------------------+------------------------------+------------------------------------+
J'ai créé Tower.js pour atteindre plusieurs objectifs qu'aucun des frameworks existants ne permettait de réaliser de manière adéquate. Voici quelques-uns de ces objectifs.
1. Même code sur le client et le serveur
Depuis que Node.js a rendu JavaScript possible sur le serveur, il n'y a aucune raison d'écrire une partie de l'application en Rails, et l'autre en Backbone. C'est tout sauf DRY. Vous devriez pouvoir définir les modèles une fois et les utiliser à la fois sur le client et le serveur.
RailwayJS ne fonctionne que sur le serveur car il a été construit autour d'express. Tower.js est également construit autour d'express mais d'une manière qui le fait fonctionner à la fois pour le client et le serveur. Tower.js fournit exactement la même API pour le client et le serveur. Cela signifie que j'ai dû réécrire certaines choses, comme le routeur, pour qu'il fonctionne de la même manière sur le client et sur le serveur (en plus, il vous permet de faire des choses comme history.pushState
avec le #
de secours, en utilisant le même ensemble de routes).
2. Mêmes "vues" sur le client et le serveur
J'ai passé beaucoup de temps dans Rails et à écrire des templates Haml. Parallèlement, j'écrivais des interfaces JavaScript pour le web et les mobiles en utilisant des langages de template comme Mustache. Il s'agit d'une duplication de code supplémentaire Vous devriez être en mesure d'utiliser le même ensemble de vues/templates à la fois sur le client (en tant que templates JavaScript) et sur le serveur (en rendant du HTML statique).
Comme Haml était assez génial (super propre, permettait d'exécuter des rubis arbitraires, intégrait la fonction de pretty-printing, etc.), l'alternative JavaScript la plus proche était CoffeeKup . Et il fonctionne aussi bien sur le client que sur le serveur. CoffeeKup vous permet d'écrire des modèles avec toute la puissance de JavaScript, vous n'avez donc aucune limite. Construire un FormBuilder dans Mustache va demander beaucoup de travail ou beaucoup de code, ou les deux.
Notez cependant que vous êtes libre de changer de moteur de template et d'utiliser Jade, Mustache, Handlebars, etc. pour le client ou le serveur. CoffeeKup est simplement une solution par défaut propre et puissante.
3. API de modèle de qualité Rail sur le client et le serveur
ActiveModel (implémenté par ActiveRecord pour SQL et Mongoid pour MongoDB pour Rails) est une API très complète et bien testée permettant aux développeurs de définir et d'interagir avec les données. Elle est à la fois puissante et agréable. Toutes les implémentations JavaScript précédentes (et actuelles) n'ont jamais été aussi robustes et bien conçues, et je ne vois rien de tel dans un avenir proche.
Si tu peux écrire ça en Rails :
User.where(:email => /[a-z/).page(2).limit(20)
Vous devriez être en mesure de le faire en JavaScript :
App.User.where(email: /[a-z/).page(2).limit(20)
Tower.js est livré avec des "scopes chaînables", c'est-à-dire des requêtes en dur + une pagination. Il est calqué sur le modèle API de requête MongoDB mais cette "entrée" API est convertie en commandes de base de données appropriées pour les différents magasins de données.
4. Interface uniforme avec les datastores SQL et NoSQL
Tower.js dispose actuellement d'un magasin MongoDB et Memory (dans le navigateur), et vise à fournir une interface uniforme pour le reste des bases de données populaires (CouchDB, Neo4j, PostGreSQL, MySQL, SQLite, Cassandra, etc.)
RailwayJS semble faire de même via JugglingDB, et cela semble être un bon début. Mais j'ai choisi de ne pas l'utiliser pour plusieurs raisons. Premièrement, il semble qu'il soit construit autour de l'API Rails 2.x ( User.validatesUniquenessOf "email"
vs. User.validates "email", presence: true
). Deuxièmement, il n'a pas la richesse des requêtes chaînables que possède Rails 3. Troisièmement, je veux pouvoir ajouter du code à la base de code rapidement, et comme je suis très pointilleux, je finirais probablement par remanier l'ensemble pour utiliser CoffeeScript, haha. Et je ne veux pas construire une couche autour de ça parce que ça doit fonctionner sur le client aussi, donc garder l'architecture de la bibliothèque aussi minimale que possible est une priorité élevée.
5. Contrôleurs débrouillards
El ressources héritées Ruby gem a supprimé environ 90 % du code de mes contrôleurs Rails. Elle a trouvé un ensemble de conventions pour implémenter les 7 actions de base du contrôleur. Tower.js inclut quelque chose comme ça, donc par défaut vous n'avez pas à écrire de code dans vos contrôleurs, ils répondront toujours avec JSON et HTML. Il permet également de définir des routes imbriquées.
6. Analyseur automatique de requêtes entre URL et base de données
Dans Tower.js, vous pouvez indiquer à un contrôleur de surveiller des paramètres spécifiques dans l'url et il les convertira en un hachage prêt à être appliqué à une requête de modèle.
class App.UsersController extends App.ApplicationController
@param "email"
index: ->
App.User.where(@criteria()).all (error, users) =>
@respondTo (format) =>
format.json => @render json: users
format.html => @render "index", locals: {users}
Étant donné une url qui ressemble à /users?email=abc&something=random
alors @criteria()
vous donnera un hash {email: /abc/}
.
Ce n'est pas dans Rails, mais j'aimerais que ce soit le cas.
7. Formes sémantiques
Je suis à fond dans le HTML sémantique. Le constructeur de formulaires de Rails génère un HTML plutôt laid, si bien que de nombreuses personnes, ainsi que moi-même, ont utilisé Formtastic qui génère des formes plus sémantiques. Tower.js utilise à peu près la même API que Formtastic. Il dispose également d'un générateur de tableaux sémantiques, qui permet de créer facilement des tableaux interrogeables/triables pour les vues d'administration.
8. Pipeline d'actifs
Rails 3 disposait d'un formidable pipeline d'actifs, dans lequel vous pouviez écrire votre JavaScript en CoffeeScript, votre CSS en SCSS, et il se recompilait automatiquement. Puis rake assets:precompile
vos ressources et vous obtiendrez des ressources gzippées et hachées en md5, prêtes pour S3. C'est assez difficile à construire soi-même, et je n'ai vu personne travailler sur ce sujet pour Node.js.
RailwayJS utilise la méthode Rails 2 d'horodatage du chemin d'accès au bien, donc au lieu de cette version md5-hashed :
/stylesheets/application-51e687ad72175b5629f3b1538b65ea2c.css
Tu obtiendrais quelque chose comme ça :
/stylesheets/application.css?1306993455524
C'est un problème pour quelques raisons importantes. Le site Guide Rails Asset Pipeline a les détails, mais la grande chose est que S3 ne reconnaît pas l'horodatage, donc il lit /stylesheets/application.css, et si vous définissez un futur lointain Expires
et que vous avez modifié votre CSS, toute personne ayant visité votre site auparavant devra purger son cache ou rafraîchir votre page pour voir les mises à jour.
RailwayJS ne dispose pas non plus du pipeline de compilation des actifs intégré (du moins à ma connaissance).
9. Le fichier de veille
Garde a été un énorme accélérateur de productivité dans Rails. Il vous permettait d'écrire des "tâches de surveillance" rapides, essentiellement comme des tâches rake/cake, qui s'exécutaient lorsqu'un fichier correspondant à un modèle était créé/mise à jour/supprimé.
Tower intègre cette fonction (en utilisant design.io ). C'est en fait ce qui indique aux ressources CoffeeScript et Stylus de compiler en JavaScript et CSS. Mais vous pouvez faire des choses très puissantes avec cette fonctionnalité, voir https://github.com/guard/guard/wiki/List-of-available-Guards par exemple.
10. CoffeeScript
Grand fan de CoffeeScript.
CoffeeScript réduit de moitié la quantité de JavaScript que vous devez écrire ( 6 501 ajouts, 15 896 suppressions en convertissant toute la bibliothèque Node.js en CoffeeScript). Et cela rend le codage beaucoup plus rapide et facile.
En outre, CoffeeScript est le seul moyen de conserver cette expérience de codage productive et agréable que Rails a fait découvrir au monde. JavaScript ne fait pas ça.
Les petites choses
Je suis un fan des standards. RailwayJS s'en tenait à la convention Ruby d'utiliser snake_case, et je voulais faire de même, mais la communauté JavaScript utilise camelCase, et Tower s'en est donc tenue à cela. CamelCase présente également quelques avantages supplémentaires, comme le fait qu'il n'est pas nécessaire de convertir le snake_case de Rails côté serveur en camelCase pour le client, et que la suppression de ce caractère supplémentaire permet de réduire la taille du fichier.
Je suis également amoureux du code super propre. Avant d'envisager de contribuer à un projet, je lis le code source... et s'il est super désordonné, je vais probablement le réécrire.
J'aime aussi optimiser le code. Avec Tower.js, l'un des principaux objectifs est de le structurer de manière à ce qu'il fasse tout ce que fait Rails, en fournissant exactement la même API au client et au serveur, en utilisant le moins de code possible. Il y a cependant un compromis à faire entre la réduction de la taille du code et l'écriture d'un code clair et agréable à utiliser. Je cherche encore des moyens d'obtenir le meilleur des deux mondes.
Je suis définitivement là pour le long terme aussi. C'est la base de notre entreprise, et de tout ce que je construirai personnellement à l'avenir. Je veux arriver au point où vous pouvez produire une application bien conçue, fonctionnelle et hautement optimisée en un jour.
J'espère que cela vous aidera.