NICHOLAS MCCLAYUX Developer
@nickmcclay
MEET YOUR NEIGHBOR
Framework Overview
SAILS HIGHLIGHTS• Robust Node.js MVC web server framework !
• Railsy features - scaffolding, DB agnostic ORM !
• Automatically generated RESTful JSON API !
• Out-of-the-box real-time socket.io integration !
• Role based access policies and ACL !
• JS/CSS asset bundling and minification (Grunt)
QUICK NOTE:Convention > Configuration = Magic
WHAT IS A SINGLE PAGE APPLICATION?
TRADITIONAL WEB APPLICATION
One Kitten Please!
HTTP Request
http://www.cutekitties.com/kitten/1
Response
HTMLJavaScript
CSS
TADA! WEB PAGE!
SINGLE PAGE APPLICATION
One Kitten Please!
HTTP Request
http://www.cutekitties.com#/kitten/1
Response
HTMLJavaScript
CSS
Application
GET /kitten/1
A JAX Request
/kitten/1?format=json
Response
JSON
[{ "_id" : "abc124efg345", "lives" : 9, "curiousity" : "massive", "cuteness" : 9999, "eyes" : "blue", "fur" : "yellow"}]
TADA! WEB APP!
Heading out to sea
SAILS VERSIONS
V0.9 V0.10
• Current NPM default • Reliable starting place • What I’ll be demoing
in this presentation
• beta version (RC3) • Significant Refactors • API changes • Associations • Here be dragons…
https://github.com/balderdashy/sails-docs/blob/master/Migration-Guide.md
Coming
V0.10 Soon
DOCUMENTATION SOURCES
https://github.com/balderdashy/sails-docs/tree/0.10Coming
V0.10 Soon
http://sailsjs.org/#!documentation
Official Website Documentation
GitHub Documentation Repo
0.10.0 NPM INSTALL WEIRDNESS
If you encounter this, try:
npm install -g
SAILS CLI BASICS
sails lift --dev --prod
sails new <appName> --linker
sails version
--verbose
Make a new app Turn on asset linking
Run App Set Env Config Display all logs
Display Sails.js Version
SAILS SCAFFOLDING
sails generate (type) <id>
Generate Scaffolding
model, controller, default is both
sails generate (type) <id>
model, controller, api, blog, user, whatever you want!
Pluggable Generators for ScaffoldingComing
V0.10 Soon
MODELSvar Person = { schema : true, attributes: { firstName: 'string', lastName: 'string', password : { type : 'string', required : true, notEmpty : true }, age : { type: 'integer', max: 150, required: true }, birthDate: 'date', phoneNumber: { type: 'string', defaultsTo: '111-222-3333' }, emailAddress: { type: 'email', // Email type will get validated by the ORM required: true } }}; model.exports = Person;
(Waterline Collection)api/models
BEYOND ATTRIBUTES// Define a custom instance methodfullName: function() { return this.firstName + ' ' + this.lastName; }, // Lifecycle CallbacksbeforeCreate: function(values, next) { bcrypt.hash(values.password, 10, function(err, hash) { if(err) return next(err); values.password = hash; next(); });}, // Override toJSON instance method// to remove phoneNumber valuetoJSON: function() { var obj = this.toObject(); delete obj.password; return obj; }
ORM - WATERLINEhttps://github.com/balderdashy/waterline
• ActiveRecord, Hibernate, and Mongoose
• emphasis on modularity, testability, and consistency across adapters
• Waterline Adapter -> DB Query • Custom Adapter methods
ASSOCIATIONSComing
V0.10 SoonAssociate models across different data stores
var Blog = { attributes : { title : 'string', body : 'string', author : { model : 'person' } }};
• One way • One-to-One • One-to-Many • Many-to-Many
Blog.find({title:'Sails.js Presentation'}).populate('author').exec(console.log);
Relationship TypesDefining Association
Full Sail Ahead!
RESOURCEFUL ROUTING
/:controller/find/:id? /:controller/create /:controller/update/:id /:controller/destroy/:id
get /:controller/:id? post /:controller put /:controller/:id delete /:controller/:id
!To disable set the ‘rest’ flag to false in config/controllers.js
To disable set the ‘shortcuts’ flag to false in config/controllers.js,
CRUD Routes
REST Routes
FREE
FREE
CONTROLLERSapi/controllers
var ChickenController = { // Peck the chicken specified by id (subtract 50 HP) peck: function (req,res) { Chicken.find(req.param('id')).exec(function (err, chicken) { if (err) return res.send(err,500); if (!chicken) return res.send("No other chicken with that id exists!", 404); if (chicken.hp <= 0) return res.send("The other chicken is already dead!", 403); chicken.hp -= 50; // Subtract 50 HP from the chicken chicken.save(function (err) { // Persist the change if (err) return res.send(err,500); // Report back with the new state of the chicken res.json(chicken); }); }); }}; module.exports = ChickenController;
SOCKET.IO
// all controller routes return valid responsessocket.get('/todo/count', function(results) { console.log(results) });// create a new itemsocket.post('/todo', {"title" : "this is from sockets"}, function(err,results) { console.log(err,results) });// all valid ways to update with websocketssocket.put('/todo', {'id' : 1, 'title' : "updated1"}, function(results) { console.log(results) });socket.put('/todo/1', {'title' : "updated2"}, function(results) { console.log(results) });socket.get('/todo/update/1', {'title' : "updated3"}, function(results) { console.log(results) });// all valid ways to delete with websocketssocket.delete('/todo', {'id' : 1}, function(results) { console.log(results) });socket.delete('/todo/1', function(results) { console.log(results) });socket.get('/todo/delete/21',function(results) { console.log(results)});// listen for messagessocket.on('message', function(message) { console.log(message) });// listen for messages from a specific controllersocket.on('todo',function(message) { console.log(message) }); Coming
V0.10 Soon
http://socket.io/
POLICIESapi/policies/isAuthenticated.js
module.exports = function(req, res, next) { // User is allowed, proceed to the next policy, // or if this is the last policy, the controller if (req.session.authorized == true) { return next(); } // User is not allowed // (default res.forbidden() behavior can be overridden in `config/403.js`) return res.forbidden('You are not permitted to perform this action.');};
config/policies.jsmodule.exports.policies = { // Default policy for all controllers and actions // (`true` allows public access) '*': true, // Policies for /Foo/* routes FooController: { "*" : true, 'update' : 'isAuthenticated', 'destroy' : ['isAuthenticated','isOwner'] }};
ASSET MANAGEMENT
sails new <appName> --linker
Enabling Asset Linking
Place Assets in between special linker flags
assets/linker/
GET STARTED BUILDING
https://github.com/cgmartin/sailsjs-angularjs-bootstrap-example
https://www.npmjs.org/package/generator-sails
http://irlnathan.github.io/sailscasts/
THANKS!
@nickmcclay