Sails.jsã使ã£ã¦ããã£ãã¨ããã¨ã
ãã®è¨äºã¯Node.js Advent Calendar 2013ã®17æ¥ç®ã®è¨äºã§ãã
å æ¥åå¼·ãã¦ãNode.jsã使ã£ãç°¡åãªWebãã¼ã¸ãä½ã£ã¦ã¿ããã¨ãããã¨ã§ãã¡ããã©è©±é¡ã«ä¸ãã£ã¦ããSails.jsã使ã£ã¦ã¿ã¾ããã
ãã®ä¸ã§æåããããããªãã£ããã¨ãããã£ããã¨ãæ¸ããã¨æãã¾ãã
Sails.jsã«ã¤ãã¦
Sails.jsã¯Ruby on Railsã®MVCã模å£ãã¦ä½ãããNode.jsç¨ã®ãã¬ã¼ã ã¯ã¼ã¯ã§ãã
ãã Railsã®ããã«ãã«ã¹ã¿ãã¯ã¨ããããã§ã¯ãªããModel-View-Controllerãããããã®ã¸ã§ãã¬ã¼ã¿ã«ã¤ãã¦ã¯æ¨æºã§ãµãã¼ãããã¦ãã¾ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ãªã©ã¯æä¾ããã¦ãã¾ããã
å人çã«ã¯ãç¹çãã¹ããã®ã¯ä»¥ä¸ã®2ã¤ãã¨æãã¾ãã
Realitimeæ©è½
Controllersã¨Policiyãæ®éã®HTTPãªã¯ã¨ã¹ãã ãã§ãªããSocket.io / WebSocketãèªåçã«ãã³ãã«å¯è½ã¨ãªã£ã¦ãã¦ããããã£ãã¢ããªéçºã¨è¦ªåæ§ãé«ãã§ãã
i18nãèªè¨¼ãã¢ã¯ã»ã¹ã³ã³ããã¼ã«ããã«ãã¤ã³ã§ãµãã¼ã
ãã®è¾ºã¯ã¨ã³ã¿ã¼ãã©ã¤ãºã£ã½ãå©ç¨ãã¡ããã¨èãã¦ãããªãã¨ããå°è±¡ã
Modelã®ä½¿ãæ¹
æåã«è©°ã¾ã£ãã®ã¯Modelã®ä½¿ãæ¹ã§ããã
Sails.jsã®githubã®wikiã«ããã¥ã¡ã³ããããã¾ãããSails.jsã®ORMé¨åã¯waterlineã¨ããå¥ã¢ã¸ã¥ã¼ã«ã¨ãã¦éçºãããã¦ãã¦ããã¡ãã®æ¹ã®READMEããã¹ããèªãã æ¹ãåèã«ãªãã¾ãã
ã¢ããã¿ã¼ã§MySQLãMongoDBãªã©ãå
±éã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ã§ä½¿ããããã«ãããã¨ããã³ã³ã»ããã¯é¢ç½ãã§ãããã¾ãã¯ãã®å
±éã¤ã³ã¿ã¼ãã§ã¼ã¹ã®å¦ç¿ãããå¿
è¦ãããã¾ãã
DBæä½ç³»ã®APIã«ã¤ãã¦ã¯callbackã¨deferredã¨promiseã¨3ã¤ã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ãæä¾ããã¦ããã®ã§ãããæåããã«æ°ã¥ããã«ããã¥ã¡ã³ããèªãç®æã«ãã£ã¦æ¸ãæ¹ãå¤ãã£ã¦ãã¦æ··ä¹±ããã¦ãã¾ãã¾ããw
ããããããªãã£ãã®ããModelã¸ã®staticã¡ã½ããã¨ã¤ã³ã¹ã¿ã³ã¹ã¡ã½ããã®è¿½å æ¹æ³ã§ããã試ãã¦ã¿ãã¨ããã以ä¸ã®ããã«è¡ãããã§ãã
// api/models/User.js module.exports = { attributes: { _id: 'integer', name: 'string', // ã¤ã³ã¹ã¿ã³ã¹ã¡ã½ãã getNickname: function() { return this.name || 'åç¡ã'; }, }, // staticã¡ã½ãã findNewUsers: function(cb) { this.find({ limit: 20, sort: '_id desc' }, cb); } };
åæã«ã¨ã³ããã¤ã³ãã追å ããªãããã«ããã
CRUDãªã¨ã³ããã¤ã³ããªã©ãèªåã§ä½ã£ã¦ãããã®ã¯ä¾¿å©ãªã®ã§ãããå¤é¨ã«å
¬éããã¢ããªã®å ´åã¯å¿ãã¦ããã¨productionã§ç©´ãéããã¾ã¾ãªãªã¼ã¹ãã¦ãã¾ãå¯è½æ§ãããã®ã§ãã¾ãæåã«ãªãã«ãã¾ããã
config/controllers.jsãéãã¨ããããã®ãªãã·ã§ã³ã«ã¤ãã¦ã³ã¡ã³ãã§è§£èª¬ããã¦ããã®ã§ãå¿
è¦ã®ç¡ããã®ã¯ãªãã«ãã¦ããã¾ãããã
- shortcuts: true, + shortcuts: false, - rest: true, + rest: false, - expectIntegerId: false + expectIntegerId: false
expressã®middlewareã追å ããã
app.jsã§å¤æ´ã§ããã®ã®ãã¨æ¸ãã¦ã¿ãã®ã§ãããåæ ãããã°ã°ã£ã¦ã¿ãã¨ãããconfig/express.jsã¨ãããã¡ã¤ã«ã追å ãã¦ä»¥ä¸ã®ãããªè¨è¿°ããããã¨ã§expressã®middlewareã追å ãããã¨ãã§ãã¾ããã
module.exports.express = { customMiddleware: function (app) { // å§ç¸®ãã¦ã¬ã¹ãã³ã¹ãè¿ã app.use(require('../node_modules/sails/node_modules/express').compress()); } };
productionç°å¢ã¨éçºç°å¢ã§è¨å®ãå¤ããã
è¦ãæãconfig/local.jsã§ãã¼ããªã©ã¯å¤æ´ã§ãããããªã®ã§ãããDBã®æ¥ç¶å
ãªã©ã®å¤æ´æ¹æ³ããªãããã«è¦ãã¾ããã
productionç°å¢ã§ã¯NODE_ENV=productionã«ç°å¢å¤æ°ãè¨å®ããã®ã§ãä¾ãã°config/adapters.jsã以ä¸ã®ããã«åãæ¿ãã¦ãã¾ãã
var dev = { 'default': 'mongo', mongo: {/* development setting */} }; var prod = { 'default': 'mongo', mongo: {/* production setting */} }; module.exports.adapters = (process.env.NODE_ENV == "production") ? prod : dev;
sails-mongoã®ç½
Sails.jsã¯æ¨æºã ã¨MySQLã§ãããsails-mongoã¨ããMongoDBç¨ã®ã¢ããã¿ãæä¾ããã¦ãã¾ããsails-mongoãnpm installãã¦configãæ¸ãæããã°ãMySQLã¨ãå
±éã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ã§ããã£ã¨ä½¿ããã¨ãã§ãã¾ãã
ã§ããã®ã¢ããã¿ã§ä¸ã¤ãããã¾ããã
MySQLã§ã¯ããã©ã«ãã§ã¯ã¢ã«ãã¡ãããã®å¤§æåã¨å°æåãSELECTæã«åºå¥ããªã(case-insentiveã§ãã)ã®ã§ãããMongoDBã¯åºå¥ãããã¾ãããªã®ã§ãä¾ãã°"Hatena"ã¨ä¿åããã¦ããã¨"hatena"ã§MongoDBã§æ¤ç´¢ãã¦ãå¼ã£æããã¾ããã
ç°¡åãªåé¿çã¨ãã¦ã¯ãMongoDBãã¯ã¨ãªã«æ£è¦è¡¨ç¾ã使ããã®ã§ãdb.collection.find({name: /^hatena$/i})ã®ããã«iãªãã·ã§ã³ã使ããã¨ã§ãããã ãiãªãã·ã§ã³ã使ã£ãå ´åã¯ã¤ã³ããã¯ã¹ã使ãããªããªã£ã¦ãã¾ããããããç¨åº¦ã®è¦æ¨¡ã®ãµã¤ããä½ãã®ã§ããã°ãã®ããæ¹ã¯é©å½ã§ã¯ããã¾ããã
ã§ãsails-mongoãªã®ã§ããMySQLã¨ã®äºææ§ãä¿ã¤ããã«ãfindã®æ¡ä»¶ã®å¤ã«ã¹ããªã³ã°ã渡ããã¨ãã«/^hatena$/iãåæã«ãã£ã¦ãããã¨ããç½ ãããã¾ãããMongoDBã§ãããã¡ã¤ãªã³ã°ããã£ã¦ãã¦ãè¦è¦ãã®ãªãã¯ã¨ãªãçºè¡ããã¦ãã¦èª¿ã¹ã¦ãããæ°ã¥ãã¾ãããããããã¨ããããORMã®æãã¨ããã§ããã
対å¦æ³ã¨ãã¦ã¯ä»¥ä¸ã®ããã«èªåã§æ£è¦è¡¨ç¾ã§ã¯ã¨ãªãçºè¡ãã¾ããåæ¹ä¸è´ãã¤case-insentiveã§ãªããã°ã¡ããã¨ã¤ã³ããã¯ã¹ã使ããã¾ãã
User.find({ name: new RegExp('^' + name + '$') }, cb);
ã¦ã¼ã¶ããã®å ¥åãåãä»ããå ´åã¯ä¸è¨ã®ã¾ã¾ã ã¨æ£è¦è¡¨ç¾ã«ä½¿ããªããã®ããã£ããããã®ã§ã以ä¸ã®ãããªæãã§ã¨ã¹ã±ã¼ããããå¿ è¦ãããã¾ãã(replaceã®æ£è¦è¡¨ç¾ã¯sail-mongoã®ãã®ãæµç¨ãã¦ãã¾ã)
User.find({ name: new RegExp('^' + name.replace(/[-[\]{}()+?*.\/,\\^$|#]/g, "\\$&") + '$') }, cb);
使ã£ã¦ã¿ã¦ã®ææ³
Railsã«æ¯ã¹ã¦ãã¾ãã¨è¶³ããªãæ©è½ã¯å¤ãã§ããããªããªãå®æ度ãé«ãã¦ããã£ã¨Webã¢ããªã±ã¼ã·ã§ã³ãä½ãããã¬ã¼ã ã¯ã¼ã¯ã«ãªã£ã¦ãããªãã¨ããå°è±¡ã§ãããã ãè¨å®ãã¢ã¸ã¥ã¼ã«ã®å®è£
æ¹æ³ãªã©ã¯Sails.jsç¬èªã®ãã®ãå¤ãã®ã§ãã¡ããã¨ä½¿ãã«ã¯ãã®è¾ºã®å¦ç¿ãããå¿
è¦ãããã®ã§ãããã¾ã ããã¥ã¡ã³ãä¸è¶³ãªæãã¯å¦ããªãã§ãã
sails-mongoã¨ãããModelã®ã¢ããã¿ã¼ã®èãæ¹ã¯å«ãã§ã¯ãªãã®ã§ãããMongoDBãªãã§ã¯ã®ã¯ã¨ãªãAPIã¯ããããããã®ã§ãã¬ãã§MongoDBã使ããã¨ãèããã¨å°ããã¨ãå¤ããã§ãããªã®ã§ãSails.js+MongoDBã§ã¡ããã¨ä½ããªããèªåã§ORMãã©ããä½ãããMongooseã使ãäºã«ãªãããã§ãã
ä»åã¯Realtimeãªä½¿ãæ¹ã¯ããªãã£ãã®ã§æ¬¡ã®æ©ä¼ã«ã¯è©¦ãã¦ã¿ããã¨æã£ã¦ãã¾ãã
ã¨ãããã¨ã§ããããã使ã人ãç¾å¨ããã£ã¦ãã人ã«ãã®è¨äºãåèã«ãªãã°å¹¸ãã§ãã