跳到主要内容

sails model 生命周期回调

· 阅读需 2 分钟

1、创建时的回调

beforeValidate: fn(values, cb)
afterValidate: fn(values, cb)
beforeCreate: fn(values, cb)
afterCreate: fn(newlyInsertedRecord, cb)

2、修改的时候回调

beforeValidate: fn(valuesToUpdate, cb)
afterValidate: fn(valuesToUpdate, cb)
beforeUpdate: fn(valuesToUpdate, cb)
afterUpdate: fn(updatedRecord, cb)

3、销毁的时候回调

beforeDestroy: fn(criteria, cb)
afterDestroy: fn(destroyedRecords, cb)

如在创建用户的时候对密码机密操作:

var bcrypt = require('bcrypt');

module.exports = {
attributes: {
username: {
type: 'string',
required: true
},
password: {
type: 'string',
minLength: 6,
required: true,
columnName: 'encrypted_password'
}
},
beforeCreate: function (values, cb) {
bcrypt.hash(values.password, 10, function(err, hash) {
if(err) return cb(err);
values.password = hash;
cb();
});
}
};

sails 使用 connect-redis 存储 session 的一个小坑

· 阅读需 1 分钟

我在config/session中添加session配置的时候,提示我需要安装connect-redis

安装完后启动发现报错,如下信息:

Could not load Connect session adapter :: connect-redis
A hook (`session`) failed to load!

后来搜到github上的issue发现,还只能使用v1.4.5版本的,于是我切换到1.4.5的,启动就没报错了。

https://github.com/balderdashy/sails/issues/2379

sails 中使用事务

· 阅读需 2 分钟

解决的思路主要还是使用原生的方式打开一个事务。

下面是在stackoverflow上的一个帖子的答案

http://stackoverflow.com/questions/25079408/how-to-handle-async-concurrent-requests-correctly/25100188#25100188

buyItem: function(req, res) {
try {
// Start the transaction
User.query("BEGIN", function(err) {
if (err) {throw new Error(err);}
// Find the user
User.findOne(req.param("userId").exec(function(err, user) {
if (err) {throw new Error(err);}
// Update the user balance
user.balance = user.balance - req.param("itemCost");
// Save the user
user.save(function(err) {
if (err) {throw new Error(err);}
// Commit the transaction
User.query("COMMIT", function(err) {
if (err) {throw new Error(err);}
// Display the updated user
res.json(user);
});
});
});
});
}
// If there are any problems, roll back the transaction
catch(e) {
User.query("ROLLBACK", function(err) {
// The rollback failed--Catastrophic error!
if (err) {return res.serverError(err);}
// Return the error that resulted in the rollback
return res.serverError(e);
});
}
}

不过提个建议就是,这种写法的风格不好,建议改成promise方式

还有个npm模块是在sails-mysql基础上封装了事务 https://github.com/postmanlabs/sails-mysql-transactions

node.js sails框架同步表的坑

· 阅读需 2 分钟

错误信息

以下是报的错误

error: Error: The hook `orm` is taking too long to load.
Make sure it is triggering its `initialize()` callback, or else set `sails.config.orm._hookTimeout to a higher value (currently 20000)
at tooLong [as _onTimeout] (C:\Users\KAMI\AppData\Roaming\npm\node_modules\sails\lib\app\private\loadHooks.js:92:21)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15

解决方案

在config目录下创建两个文件,orm和pubsub,不过据回答的作者说是名字并不重要。

// config/orm.js
module.exports.orm = {
_hookTimeout: 60000 // I used 60 seconds as my new timeout
};
// config/pubsub.js
module.exports.pubsub = {
_hookTimeout: 60000 // I used 60 seconds as my new timeout
};

不建议直接在 node_modules 中修改

原问题地址:http://stackoverflow.com/questions/28524926/the-hook-orm-taking-too-long-to-load

语义化版本控制

· 阅读需 2 分钟

简介

在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的套件越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。

摘要

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  1. 主版本号:当你做了不兼容的API 修改,
  2. 次版本号:当你做了向下兼容的功能性新增,
  3. 修订号:当你做了向下兼容的问题修正。

先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

http://semver.org/lang/zh-CN/

新浪微博的时间是怎么实时变化的

· 阅读需 7 分钟

由来

那时候还很无知,虽然发现了有这么一个用户体验不错的细节,可惜开动了小脑经也并不知道怎么玩的。

场景

地点:新浪微博 事件:发现每条微博的时间都会不断的自己变咧!这样就算你放了N久,也会知道这条微博是到底什么时候发的,不用再去刷新页面了。

问题

是怎么能实现批量更新时间的呢? 通常都是在服务端查询后根据时间算出对应的人性化时间,“几秒前”“*分钟前”等等。 而这样的字符串是很难再哪来二次计算的。

分析

时间的代码片段

上面是时间的代码片段,简单看一下,跟时间有关系的属性,只有titledatenode-tpye,这三个属性:

  • title 从值看来是年月日时分的格式化后的字符串
  • date 时间戳
  • node-type 从值来看应该算是表示了某一类的类名

通常来讲,用时间戳来计算是最方便的,因为他是以毫秒为单位,而天、时、分、秒都可以用毫秒来表示,而超过了某个单位的最大值,比如一分钟前的微博,用现在的时间戳减去之前的时间戳,大于60000毫秒的话,那么肯定就是一分钟前了。

而找到这些元素只需要通过找到node-type属性为feed_list_item_date的元素再获取date属性计算后改变即可。

这里我是尝试了一下把feed_list_item_date改为feed_list_item_date1他的时间就不变了,而其他的已然在周期性的变化,改回去之后又在周期的变化。


补充

然而有个事被我差点忘记了,就是这个title属性,为毛没有讲是干啥用的,我开始也没想明白,而后来我看到了这个 时�间格式化

我好像有点明白了,我又观察了一下,在一个小时内,时间是会显示xx分钟前的,但是超过了一个小时之后,时间就会变成今天 13:00, 昨天 13:008月20日 13:00这个样子了,那么这个title感觉很好解释了,超过1小时候拿这title来替换掉其中的yyyy-MM-dd部分,就成了稍微久远点的人性化时间。

总结

其实一些不错的产品很值得琢磨的,不管是用户体验也好,优化也好

mongodb从2.6迁移到3.0过程

· 阅读需 11 分钟

总共有这么几个问题需要解决:

  1. mongodb3.0版本的二进制包
  2. wiredTiger引擎的配置
  3. 数据的迁移
  4. 权限

mongodb3.0的二进制包

这个就不用多说了,直接去官网选择对应的系统下载就好 https://www.mongodb.org/downloads 我是centos 6.3,对应的发行版貌似是redhat6.x,所以选择的redhat6 64bit

wiredTiger引擎的配置

官方给的例子是以yaml形式写的配置,我还是以conf文件形式,所以就在后面追加了一个选项就可以了。当然我是重建的库路径,如果原有的数据路径的话,启用wiredTiger是会报错的。这就是下面要说的一个部分。

dbpath=/your/data/path
logpath=/your/log/path
logappend=true
bind_ip = 127.0.0.1
auth = true
port = 27017
journal = true
#fork=true
storageEngine=wiredTiger

数据的迁移

由于需要换引擎,所以原来的数据不能直接使用了,必须使用monogodumpmongorestore两个工具来迁移数据。 具体操作可以参见这篇文章 通过mongodump和mongorestore实现Mongodb备份和恢复

权限

那么将数据导入之后,如果需要设置权限的话,首先是需要admin库中创建一个user的,还是先把auth=true给注释掉了,创建个用户,到这里懵逼了一下,提示addUser方法错误,查了一下发现,3.0的用户及权限有些改变。

定义

创建一个数据库新用户用db.createUser()方法,如果用户存在则返回一个用户重复错误。

语法

db.createUser(user, writeConcern)

  • user这个文档创建关于用户的身份认证和访问信息;
  • writeConcern这个文档描述保证MongoDB提供写操作的成功报告。

· user文档,定义了用户的以下形式:

{ user: "<name>",
pwd: "<cleartext password>",
customData: { <any information> },
roles: [
{ role: "<role>", db: "<database>" } | "<role>",
...
]
}

user文档字段介绍:

  • user字段,为新用户的名字;
  • pwd字段,用户的密码;
  • cusomData字段,为任意内容,例如可以为用户全名介绍;
  • roles字段,指定用户的角色,可以用一个空数组给新用户设定空角色; 在roles字段,可以指定内置角色和用户定义的角色。

Built-In Roles内置角色

  1. 数据库用户角色:read、readWrite;
  2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;
  3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
  4. 备份恢复角色:backup、restore;
  5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
  6. 超级用户角色:root // 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
  7. 内部角色:__system PS:关于每个角色所拥有的操作权限可以点击上面的内置角色链接查看详情。

writeConcern文档 官方说明

  • w选项:允许的值分别是 1、0、大于1的值、"majority"、<tag set>
  • j选项:确保mongod实例写数据到磁盘上的journal(日志),这可以确保mongd以外关闭不会丢失数据。设置true启用。
  • wtimeout:指定一个时间限制,以毫秒为单位。wtimeout只适用于w值大于1。

例如:在products数据库创建用户accountAdmin01,并给该用户admin数据库上clusterAdmin和readAnyDatabase的角色,products数据库上readWrite角色。

use products
db.createUser( { "user" : "accountAdmin01",
"pwd": "cleartext password",
"customData" : { employeeId: 12345 },
"roles" : [ { role: "clusterAdmin", db: "admin" },
{ role: "readAnyDatabase", db: "admin" },
"readWrite"
] },
{ w: "majority" , wtimeout: 5000 } )

以上是新版本用户、角色、权限的一些说明。

看看我们现在要做的: 先创建个管理员

use admin
db.createUser(
{
user: "adminuser",
pwd: "12345678",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)

再创建我们应用库的用户

use appdb
db.createUser(
{
user: "luoyjx",
pwd: "12345678",
roles: [
{ role: "readWrite", db: "appdb" }
]
}
)

使用db.auth(username,pwd)可以验证一下

创建用户大概到这里了,创建完admin,再创建一个普通用户就基本OK了。

到这里,单实例的mongodb从2.6迁移到3.0就基本完成了。

TODO

副本集(没有折腾)

JavaScript Promise迷你书

· 阅读需 2 分钟

本书的目的是以目前还在制定中的ECMAScript 6 Promises规范为中心,着重向各位读者介绍JavaScript中对Promise相关技术的支持情况。

通过阅读本书,我们希望各位读者能在下面三个目标上有所收获。

  • 学习Promise相关内容,能熟练使用Promise模式并进行测试
  • 学习Promise适合什么、不适合什么,知道Promise不是万能的,不能什么都想用Promise来解决
  • 以ES6 Promises为基础进行学习,逐渐发展形成自己的风格

书的地址: http://liubin.github.io/promises-book/

PDF下载地址: http://liubin.github.io/promises-book/javascript-promise-book.pdf

源码地址: https://github.com/liubin/promises-book/

javascript变量声明提升--hoisting

· 阅读需 11 分钟

javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。

先看一段代码

var v = "hello";
(function(){
console.log(v);
var v = "world";
})();

这段代码运行的结果是什么呢? 答案是:undefined 这段代码说明了两个问题, 第一,function作用域里的变量v遮盖了上层作用域变量v。代码做少些变动

var v = "hello";
if(true){
console.log(v);
var v = "world";
}

输出结果为"hello",说明javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构

第二,在function作用域内,变量v的声明被提升了。所以最初的代码相当于:

var v = "hello";
(function(){
var v; //declaration hoisting
console.log(v);
v = "world";
})();

声明、定义与初始化

声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。 用C++来表述这三个概念

extern int i;// 这是声明,表明名字 i 在某处已经存在了
int i;// 这是声明并定义名字 i, 为 i 分配存储空间
i = 0;// 这是初始化名字 i, 为其赋初值为 0

javascript中则是这样

var v;// 声明变量 v
v = "hello";//(定义并) 初始化变量 v

因为javascript为动态语言,其变量并没有固定的类型,其存储空间大小会随初始化与赋值而变化,所以其变量的“定义”就不像传统的静态语言一样了,其定义显得无关紧要。

声明提升

当前作用域内的声明都会提升到作用域的最前面,包括变量和函数的声明

(function(){
var a = "1";
var f = function(){};
var b = "2";
var c = "3";
})();

变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:

(function(){
var a,f,b,c;
a = "1";
f = function(){};
b = "2";
c = "3";
})();

请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:

(function(){
//var f1,function f2(){}; //hoisting, 被隐式提升的声明

f1(); //ReferenceError: f1 is not defined
f2();

var f1 = function(){};
function f2(){}
})();

上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。

名字解析顺序

javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下: 1、语言内置:所有的作用域中都有 this 和 arguments 关键字 2、形式参数:函数的参数在函数作用域中都是有效的 3、函数声明:形如function foo() 4、变量声明:形如var bar;

名字声明的优先级如上所示,也就是说如果一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上优先级的影响。看代码:

(function(){
var foo;
console.log(typeof foo); //function

function foo(){}

foo = "foo";
console.log(typeof foo); //string
})();

如果形式参数中有多个同名变量,那么最后一个同名参数会覆盖其他同名参数,即使最后一个同名参数并没有定义。

以上的名字解析优先级存在例外,比如可以覆盖语言内置的名字arguments。

命名函数表达式

可以像函数声明一样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提升。

f();//TypeError: f is not a function
foo();//ReferenceError: foo is not defined
var f = function foo(){console.log(typeof foo);};
f();//function
foo();//ReferenceError: foo is not defined

命名函数表达式的名字只在该函数的作用域内部有效。

文章来自 http://openwares.net/