64位ElementaryOS(Ubuntu)配置cordova/ionic

1. JDK

JDK 比较简单,直接在官网下载即可 。

2. ANDROID SDK

ANDROID SDK 非常地费劲!伟大的墙各种添堵!我是先通过 ssh 把压缩包 wget 到 vps 上(wget三百多m十秒下完,美帝的网速太腐败了),再下载到本地。这样速度会比通过 vpn 的稍快些。

3. 两个巨大的包下载后解压,然后设置环境变量

1
$ vim ~/.bashrc

加入以下变量:

1
2
3
export JAVA_HOME=/path/to/jdk
export ANDROID_HOME=/path/to/android_sdk
export PATH=${PATH}:${JAVA_HOME}/bin:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/tools

刷新

1
$ source ~/.bashrc

4. 安装 Android SDK 的组件

包括一些基本依赖和版本相关的镜像。这个只能联上VPN搞了。不翻墙是行不通的。联上后回到 shell, 打入

1
$ android

这个是 sdk 管理器,加载好后,默认会自动选上基础包和最新的 android 所有的镜象。考虑到网速,建议在“版本”如 Android x.x.x (API xx),下面,选中 SDK Platform 和 ARM EABI xxx System Image 这两个包就可以了,但如果你的电脑支持虚拟化,可以把 ARM 架构的换成 Atom x86-64位的,再装好 kvm ,速度会快很多。我选的就是 Atmo x86-64。

下载好镜像之后,在 Tools -> Manage AVDs -> Create 新建一个虚拟机器,后面的 ionic emulate 会用到

4.1 安装 KVM (仅限支持虚拟化的机器)

1
$ cat /proc/cpuinfo

若输出的 flags 中有 vmx(INTEL) 或 svm(AMD) 的话即是支持

1
$ sudo apt-get install qemu-kvm virt-manager bridge-utils libvirt-bin

装完看下有没有 /dev/kvm 若没有,重启进 bios 开启虚拟化。(我的笔记本设置开启虚拟化后还需要冷启动才能生效)

4.2 安装 Android SDK 依赖

由于 Android SDK 很多工具都是32位的,我的系统是 64 位,还需要一些依赖,否则无法会导致 cordova 无法build:

<span class="pln">$ sudo apt</span><span class="pun">-</span><span class="kwd">get</span><span class="pln"> install libncurses5</span><span class="pun">:</span><span class="pln">i386 libstdc</span><span class="pun">++</span><span class="lit">6</span><span class="pun">:</span><span class="pln">i386 zlib1g</span><span class="pun">:</span><span class="pln">i386
</span>

装完以上的库可以解决 cordova 的 build 问题。

接下来还要解决 emulate 的问题。

1
2
3
$ cd ${ANDROID_HOME}/tools
$ rm emulator
$ ln -s emulator64-x86 emulator

总之 emulator 的架构要和 image 对应。

5. Node.js 的版本

实测 v5.0.0 是不行的, v0.12.7/v4.2.1 可以,建议使用 nvm 管理 node.js 版本。

6. 安装 cordova 与 ionic

ionic 依赖 node-sass ,node-sass 需要 g++,先安装 g++

1
2
$ sudo apt-get install g++
$ npm install -g cordova ionic

7. 好了,测试一下

1
2
3
4
$ ionic start myApp tabs
$ ionic platform add android
$ ionic build android
$ ionic emulate

:)

8. 真机调试

emulator 不给力,速度慢调试麻烦。直接用手机调试效果更加直观,只要安卓4.1以上版本,在设置里打开USB调试,接入到电脑上,就可以开搞了。

$ ionic build && ionic run

手机会提示安装,点击同意,程序就会安装并开始运行了。然后打开 chrome,在地址栏输入:

chrome://inspect/#devices

注可以看到手机设备,点击 inspect ,这个调试界面就不用多讲了吧!

Share Comments

ubuntu 搭建VPN Server - 浪漫小生 - 博客园

Source: ubuntu 搭建VPN Server - 浪漫小生 - 博客园

废话不多说,直接上步骤:

  1. 以 root 账户登录 VPS,或者当你运行下列命令提示权限不够时,加上 sudo 再运行。
  2. 安装 PPTPD:
    编辑 /etc/pptpd.conf 文件,取消注释以下 2 行:

    1
    2
    localip 192.168.0.1
    remoteip 192.168.0.234-238,192.168.0.245
  3. 添加 PPTP 账户:
    编辑 /etc/ppp/chap-secrets 文件,按如下格式添加:

    1
    用户名 pptpd "密码" 允许接入的IP

    其中密码要用引号包围。允许任何 IP 接入则用 * 号表示。

  4. 设置 DNS Server:
    编辑 /etc/ppp/pptpd-options 文件,找到 ms-dns 部分来修改,我使用的是 Google 的:

    1
    2
    ms-dns 8.8.8.8
    ms-dns 8.8.4.4
  5. 开启 IP 转发:
    编辑 /etc/sysctl.conf 文件,取消注释该行:

    1
    net.ipv4.ip_forward=1

    然后运行这行代码使配置生效:

    1
    sysctl -p
  6. 安装 iptables:

    1
    2
    3
    apt-get install iptables
    iptables -t nat -I POSTROUTING -j MASQUERADE
    sudo iptables-save &gt; /etc/iptables-rules
  7. 自动加载配置:

    修改 /etc/network/interfaces 文件,找到 eth0 那一节,在对 eth0 的设置最末尾加上下面这句:

    1
    pre-up iptables-restore > /etc/iptables-rules
  8. 重启 pptpd:

    1
    /etc/init.d/pptpd restart
Share Comments

为Node.js4配置VIM编辑器

安装必要插件:

pathogen 插件管理

主页:https://github.com/tpope/vim-pathogen

有了这个,就可以像 Textmate / Sublime Text 那样方便地安装卸载插件。后面安装的插件只需将它们放到 ~/.vim/bundle/ 里面独立一个文件夹,pathogen 更会自动加载。卸载时进入到 ~/.vim/bundle/ 找到对应的文件夹删除即可!干净利落。

mkdir -p ~/.vim/autoload ~/.vim/bundle &amp;&amp; \ curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim

然后在 ~/.vimrc 加入:

execute pathogen#infect()

auto-pairs 自动配对

主页:https://github.com/jiangmiao/auto-pairs

当输入(时会自动补齐),包括中括号,尖括号等。删除的时候更会自动删除右边对应的配对符号。需要在括号之间加一对空格也只需要按一下空格就行。

git clone git://github.com/jiangmiao/auto-pairs.git ~/.vim/bundle/auto-pairs

vim-closetag 自动关闭 HTML 标签

主页:https://github.com/alvan/vim-closetag

snipmate 的html标签太少了,不实用。自动化的更合适,当输入 时会自动补全 。

git clone https://github.com/alvan/vim-closetag.git ~/.vim/bundle/vim-closetag

安装完成之后在 ~/.vimrc 加入配置,指定生效的文件扩展名:

let g:closetag_filenames = "*.html,*.xhtml,*.phtml"

vim-surround 智能包围

主页:https://github.com/tpope/vim-surround

在 Sublime Text 中,如果你选定一段文字。按一下’键或”等有包含意义的按键,编辑器会自动在尾部帮你追加一个对应的结束符。这个插件不但可以完成类似的功能还更加地强大!替换括号,再增加一层括号,追加。都不是问题!并且 html tag 的包含也是支持的哇!

git clone git://github.com/tpope/vim-surround.git ~/.vim/bundle/vim-surround

nerdcommenter 快速注释

主页:https://github.com/scrooloose/nerdcommenter

安装:

git clone https://github.com/scrooloose/nerdcommenter ~/.vim/bundle/nerdcommenter

快速切换注释代码。可视模式下,当前行或选中行后按以下按键进行切换:

1
2
3
4
\cc 注释
\cs “性感注释",貌似在js里面就是函数说明的注释样式
\cu 撤销注释
\ci 按行切换注释`

vim-repeat 自动重复插件

主页:https://github.com/tpope/vim-repeat

这个有点类似宏的东西,配合 vim-surround 功能无比强大。比如说你现在要把 js 中声明的 { a: 'foo': b: 'bar', c: ...} 转化成为一个标准的 json 文档,那么,需要将 a,b,c…加上双引号,’foo’,’bar’,…转换为双引号。利用 vim-surround 可以追加和替换,但是每一次重复打 ysiw” 和 cs’” 也是挺麻烦的。vim-repeat 可以自动帮你重复上一次的命令。只要将光标定位到对应的单上面再按一下.键,命令就自动重复!

git clone git://github.com/tpope/vim-repeat.git ~/.vim/bundle/vim-repeat

NERDTree 文件管理器

主页:https://github.com/scrooloose/nerdtree

即是在侧栏显示一个树型的文件浏览器,几乎稍微现代些的代码编辑器都是内置的。但对于VIM来讲,这个功能还是需要加个插件才能实现。由于要考虑到 shell 环境,这个在 gvim + tab 的环境体验并不良好。但在单 tab 模式下。该有的功能还是一样不少,比 Sublime text 和 Textmate 的文件管理功能都要强大。书签,定义根目录,新建,删除,重命名,移动都是有的。鼠标或键盘操作都支持!

git clone https://github.com/scrooloose/nerdtree.git ~/.vim/bundle/nerdtree

snipemate.vim 代码片断伴侣

主页:https://github.com/msanders/snipmate.vim

实现打几个字符按一下tab自动生成一段格式好的代码的功能,比如 func 生成 function() {} 之类的。具体片断跟语法插件有关,不再细表

git clone git://github.com/msanders/snipmate.vim.git ~/.vim/bundle/snipmate.vim

syntastic 语法错误定位

主页:https://github.com/scrooloose/syntastic

在出错行的行号列显示高亮提示,方便定位出错的行。

git clone https://github.com/scrooloose/syntastic.git ~/.vim/bundle/syntastic

vim-javascript javascript语法插件

主页:https://github.com/pangloss/vim-javascript

基本的javascript语法插件,目前还有在更新

git clone https://github.com/pangloss/vim-javascript.git ~/.vim/bundle/vim-javascript

vim-es6 ES6语法高亮及代码片断

主页:https://github.com/isRuslan/vim-es6

除了语法高亮,还提供了几个 es6 特有的代码片断。

git clone https://github.com/isRuslan/vim-es6.git

eslint

主页:http://eslint.org/

安装:

npm i eslint -g

配置文件: ~/.eslintrc

` { “parser”: “babel-eslint”, “env”: { “es6”: true, “mocha”: true }, “plugins”: [ “react”, “json” ], “ecmaFeatures”: { “jsx”: true, “modules”: true

}, “rules”: { “semi”: 2 } } `

安装 Unite 文件搜索快速打开

这是一个牛逼的插件,可以通过关键字快速打开切换不同的文件。配合 pt (the_platinum_searcher) 可以实现文件夹级别的搜索

首先需要安装 vimproc.vim 让 vim 可以支持异步操作,这个 clone 之后还要编译一下:

git clone https://github.com/Shougo/vimproc.vim.git ~/.vim/bundle/vimproc.vim cd ~/.vim/bundle/vimproc.vim make

然后安装 pt, 具体步骤查看: https://github.com/monochromegane/the_platinum_searcher

最后安装 Unite:

git clone https://github.com/Shougo/unite.vim.git ~/.vim/bundle/unite.vim

安装 tern_for_vim js 自动完成

  1. 安装 tern_for_vim

git clone https://github.com/ternjs/tern_for_vim.git ~/.vim/bundle/tern_for_vim

  1. 安装 ternjs

全局安装:npm i tern -g 或者在 tern_for_vim 里面执行 npm i 也可以

配置 .vimrc/.gvimrc

.vimrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
" 启用 pathogen 插件管理
execute pathogen#infect()

" 开启语法着色
syntax on

" 开启文件类型
filetype plugin indent on

" 一个 tab 等于 2 个空格的宽度
set ts=2
set sw=2

" 按 tab 插入对应数量的空格
set expandtab

" 显示行号
set number

" 切换 NERDTree
map <C-t> :NERDTreeToggle<CR>

" 自动缩进
set autoindent

" 允许未保存的情况下打开新的文件或切换 buffer
set hidden

" 退出时若 session 中含有未保存的 buffer 提示保存
set confirm

" 设定自动关闭标签的有效文件扩展名
let g:closetag_filenames = "*.html,*.xhtml,*.phtml,*.js"

" 重复粘贴时按 \p 即可
vnoremap <leader>p "_dP

" 设定 javascript 的语法检测使用 eslint
let g:syntastic_javascript_checkers = ['eslint']

" 设定 jsx 着色对非 jsx 的文件也有效,如 js 文件
let g:jsx_ext_required = 0

"高亮多作的行尾空格
" 1. 新建一个高亮组
highlight ExtraWhitespace ctermbg=red guibg=red
" 2. 匹配行尾空格
match ExtraWhitespace /\s\+$/

" 配置 Unite 使用 pt 搜索
if executable('pt')
let g:unite_source_grep_command = 'pt'
let g:unite_source_grep_default_opts = '--nogroup --nocolor'
let g:unite_source_grep_recursive_opt = ''
let g:unite_source_grep_encoding = 'utf-8'
endif

function! ProjectOpen()
execute ':Unite -no-split file:' . ProjectRootGuess()
endfunction

function! ProjectGrep()
execute ':Unite -no-split grep:' . ProjectRootGuess()
endfunction

" 对 pwd 进行正则搜索
nnoremap <silent> <Leader>g :<C-u>call ProjectGrep()<CR>

" 打开文件 file
nnoremap <silent> <Leader>f :<C-u>call ProjectOpen()<CR>

" 打开文件 file_rec
nnoremap <silent> <Leader>c :<C-u>Unite -no-split file:.<CR>

" 打开 Unite 结果窗口
nnoremap <silent> <Leader><Space> :<C-u>UniteResume -no-split<CR>

" 快速切换窗口
nnoremap <silent> <Leader>w <C-w><C-w>

" 允许 Unite 删除文件
call unite#custom#alias('file', 'delete', 'vimfiler__delete')

" 自动将 pwd 切换到当前文件
set autochdir

" 将 shell 执行结果放到新开的 Scratch buffer。 使用方法: Shell eslint ./
command! -complete=shellcmd -nargs=+ Shell call s:RunShellCommand(<q-args>)
function! s:RunShellCommand(cmdline)
let isfirst = 1
let words = []
for word in split(a:cmdline)
if isfirst
let isfirst = 0 " don't change first word (shell command)
else
if word[0] =~ '\v[%#<]'
let word = expand(word)
endif
let word = shellescape(word, 1)
endif
call add(words, word)
endfor
let expanded_cmdline = join(words)
botright new
setlocal buftype=nofile nobuflisted noswapfile nowrap
call setline(1, 'You entered: ' . a:cmdline)
call setline(2, 'Expanded to: ' . expanded_cmdline)
call append(line('$'), substitute(getline(2), '.', '=', 'g'))
silent execute '$read !'. expanded_cmdline
1
endfunction

" 命令别名
ca shell Shell

" 关闭临时文件
set noswapfile

.gvimrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
" 高度 50 行
set lines=999

" 亮度 140 列
set columns=999

" 颜色主题
colo solarized

" 背影黑色
set background=dark

" 字体
set guifont=Monaco:h13

" 隐藏菜单栏
set guioptions-=m

" 自动开启 NERDTree
"NERDTreeToggle

" 设定 NERDTree 根目录
"NERDTree ~/Projects

" 默认目录
cd ~/Projects

if has("gui_macvim")
" Press Ctrl-Tab to switch between open tabs (like browser tabs) to
" the right side. Ctrl-Shift-Tab goes the other way.
noremap <C-Tab> :tabnext<CR>
noremap <C-S-Tab> :tabprev<CR>

" Switch to specific tab numbers with Command-number
noremap <D-1> :tabn 1<CR>
noremap <D-2> :tabn 2<CR>
noremap <D-3> :tabn 3<CR>
noremap <D-4> :tabn 4<CR>
noremap <D-5> :tabn 5<CR>
noremap <D-6> :tabn 6<CR>
noremap <D-7> :tabn 7<CR>
noremap <D-8> :tabn 8<CR>
noremap <D-9> :tabn 9<CR>
" Command-0 goes to the last tab
noremap <D-0> :tablast<CR>
endif

Share Comments

node.js 4.0 新特性

node.js 4.0 是 node.js 与 io.js 回归统一之后正式推出的第一个版本。这说明从此以后, node.js 将采用 io.js 的理念,积极跟进 v8 引擎的更新,支持各种各样的ES6新特性而不必加上 harmony 标签。测试稳定后的新特性将会成为 node.js 的一等公民。新特性未来将分成三种组别,分别是:交付、展示、进行中。分别是稳定版和公测版以及测试版的意思。

 

目前发布的 4.0 测试版有以下稳定特性:

Block scoping 块级作用域

块是指由显式或隐式{}包含起来的代码,比如 if (…) {} for (…) {} 括起来的部分即为块。在以往的标准中块是没有独立的作用域的,而是与函数体共享一个作用域。在作异步编程中有时会制造一些麻烦。ES6带来的块级作用域便是用来解决这一类的问题。

let

let 和 var 一样,是用来声明变量的,不同的时,var 是作用于函数体,而 let 则是作用于块级。请看代码:

for (var i = 0; i < 10; i++) {
  process.nextTick(function(){
    console.log(i);
  });
}
也许你会以为上面的代码会输出0到9,事实上这个只会输出10个9。因为 js 是按块级去执行代码,上面其实是先把循环0到9执行完了,再去一个个执行 nextTick 传入的函数。而每个函数引用的都是局部变量i,而这个时候i已经是9,自然就输出10个9了。 而let这个时候把 var 换成 let ,你会发现程序可以正确地输出 0~9 了,let 声明的是块级变量。也就是说每个 process.nextTick 会引用于属于它自己的 i ,而不是引用到同一个局部变量,如此一来程序便可以正确输出。 #### const const 关键字就好理解了,毕竟各种语言都有。js 一向缺少定义常量的功能,const 就是用来定义常量的。需要注意的是如果 const 定义的是一个 Array 或 Object 这样类型的变量,那变量的本身内容是可能被修改的:
const FOOBAR = { "foo": 1, "bar": 2 };
FOOBAR.foo = 3; // this is legal.
FOOBAR = { "foo": 3, "bar": 4}; // this is illegal.

function-in-block

这个应该块级函数作用域方面的特性,没有找到太多资料。目测应该与 let 是一类的意思。根据说明文档,这个东西目前还不是按ES6标准实现。未来可能会发生变化。

Class 类 (仅严格模式可用)

这个是比较重量级的新特性了,OO编程的基本元素。虽说一直以来有 prototype 这样的东西可以实现相似的功能,但定义起来毕竟别扭,虽然有很多类库帮助方便的定义类,但这样也造成各种不统一。参考起别人的代码总要费些劲。

<span class="token keyword">class</span> Square<span class="token class-name"> extends Polygon</span> <span class="token punctuation">{</span>
  <span class="token function">constructor<span class="token punctuation">(</span></span>height<span class="token punctuation">,</span> width<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>height <span class="token operator">=</span> height<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>width <span class="token operator">=</span> width<span class="token punctuation">;
</span>    super.name = 'Square';
  <span class="token punctuation">}

</span>  get area() {
    return this.height * this.width;
  }

  static areaEqual(a, b) {
    return a.area == b.area;
  }
<span class="token punctuation">}</span>`</pre>
很好,构造函数、属性、静态函数都有了,也不用写那么多 function ,这样定义一个类可要比以往写少很多代码了。结构明显清晰多了。

### Collection 集合

#### Map 字典

基本上这是跟 Object 很类似的类型,在 ES6 出现以前我们一向是把 Object 当作字典来使用的。那么 Map 与 Object 相比有哪些优势?我们为什么要用 Map 而不是 Object:
  • Object 是有 prototype 的,也就是说在遍历的时候要特别处理,否则 prototype 的条目也会被包含进来。当然,用 Object.create(null) 也可以避免这个问题。
  • Object 只接受 String 类型作为键(KEY),而 Map 可以接受任意类型的变量作为键,这跟Java/.NET 的字典类型行为一致。
  • Map 内部有维护一个条目数量 size ,随时可以通过 size 属性来知道字典里面条目的数量,跟Array的length差不多。而 Object 则是没有这样的东东的,只能手动一个个地算。
  • 虽然目前 Object 实际上是有序的,但根据标准,Object 并不保证 key 的有序性。而 Map 规定是有序的,按插入顺序排列。

    WeakMap 弱字典

    这个跟 Map 是类似的东西,但它的键是弱引用。在 Map 中由于键是强引用,它会阻止垃圾回收系统对键和值进行回收。弱引用的在于它不会阻止垃圾回收对键值进行删除,当程序其它引用该键的变量解除引用后,GC便会进行回收,相应的,WeakMap 中的键值对也会被删除。因为受到垃圾回收的影响,所以 WeakMap 是无法对键值进行遍历。这个貌似可以用来做缓存系统?

    Set 唯一集合

    跟数组类似,不同的是 Set 针对相同的值只存储一个,另外的区别就是不能下标引用。以往在没有 Set 的情况下只能用 Object 的 Key 来代替,有了这个,在内存占用上会更有优势,另外还有 length 的属性可供使用,方便多了。

    WeakSet 弱引用唯一集合

    这个原理上跟 WeakMap 是一样的,只不过 Map 变成了 Set

    TypedArray 强类型数姐

    如果你接触的语言够多,会发现,弱类型语言的数组和强类型的数组是很不一样的。一般的强类型语言,数组Array是长度不变的(当然类型也是固定的),而弱类型数组更像是强类型中的 List 或者 LinkList ,长度可变,可以存储任意类型的数组。随着 js 在 web Server端的崛起,人们开始需要直接通过 js 操作二进制原始数组,而 TypedArray 就是对应这个需求出现的特性。

    需要注意的是,在使用 Array.isArray 对 TypedArray 进行判断时是会返回 false 的。也就是它在本质上并非是 Array 。

    在架构上,TypedArray 分为两种层,底层是 ArrayBuffer 类型,这是一种纯粹的二进制数据,没有格式,无法直接进行操作。要对原始数据进行操作,需要通过 View 层进行,分别是 Int8Array、Uint8Array、Int16Array、Uint16Array 等类型。

    `var buffer = new ArrayBuffer(16);
    `
    `if (buffer.byteLength === 16) {
      console.log("Yes, it's 16 bytes.");
    } else {
      console.log("Oh no, it's the wrong size!");
    }
    `
    `var int32View = new Int32Array(buffer);`

    Generator 生成器

    通过 function 关键字,可以定义一个返回 Generator 的函数。Generator 某程度上跟 c# 的迭代器很相似。Generator 类似于 c# 中实现了 IEnumerable 的类。function 返回的实际上是一个 Generator 实例,这个 Generator 在 yield 的地方会将值返回给调用方,然后再继续执行后面的指令。

    `function* anotherGenerator(i) {
      yield i + 1;
      yield i + 2;
      yield i + 3;
    }
    
    function* generator(i){
      yield i;
      yield* anotherGenerator(i);
      yield i + 10;
    }
    
    var gen = generator(10);
    
    console.log(gen.next().value); // 10
    console.log(gen.next().value); // 11
    console.log(gen.next().value); // 12
    console.log(gen.next().value); // 13
    console.log(gen.next().value); // 20`
    整个 Koa.js 都是基于生成器作为中间件链接动作,说明这个特性也是重量级的特性,将来应用必然会很广。 #### Binary & Octal literal 二进制和八进制字面量
    0777 // 前面加零即为定义八进制字面量,若数字中有超过8的则视为定义十六进制字面量
    0o777 // 前面加0o即为定义八进制字段量
    0b00001111 // 前面加0b即为定义二进制字面量
    0xA0A0A0 // 前面加0x即为定义十六进制字面量

    Extended Object literal 扩展对象字面量

    这是对 Object 实便声明的一种扩展,说起来挺复杂,不如直接看代码:

    var obj = {
        // 指定 prototype
        __proto__: theProtoObj,
        // ‘handler: handler’ 简化声明方式
        handler,
        // 'toString: function()' 的简化声明方式
        toString() {
         // Super calls
         return "d " + super.toString();
        },
        // 动态属性名
        [ 'prop_' + (() => 42)() ]: 42
    };
    那么,没错,就是这么吊! #### Promises 承诺对象 那么,也就是说 bluebird 可以退休了?也好!棒子的英语水平太牛逼了,看他们的文档实在是折磨人! #### New String methods 新增的 String 方法 代替了一部份 lodash 的功能,[详情请戳这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla#Additions_to_the_String_object) #### Symbol 符号 这玩意大概是从 ruby 移植过来的概念,代表唯一且不可变的标识!嗯,太抽象了,说实话一直不太喜欢 ruby 的这种设定。有什么特别实用的地方吗?看 mozilla 的文档,貌似可以用来将一些信息隐式存储到 Object 实例中。因为 for in 和 Object.getOwnPropertyNames 都不会返回 Symbol 类型的键。由于唯一性,new Symbol('foo') != new Symbol('foo'),[详情还是戳这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol),大家发现有什么应用的地方可以参详一下。 #### Template String 模板字符串 来自 Perl、Shell Script、PHP的精华!通过反引号``介单,可以跨行,支持变量自动替换。很实用的新特性。 ES5代码:
    `var a = 5;
    var b = 10;
    console.log("Fifteen is " + (a + b) + " and\nnot " + (2 * a + b) + ".");
    // "Fifteen is 15 and
    // not 20."`

    ES6代码:

    <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">; </span>var b = 10; console<span class="token punctuation">.</span><span class="token function">log<span class="token punctuation">(</span></span>Fifteen is ${a + b} and
    not ${2 * a + b}.<span class="token punctuation">)</span><span class="token punctuation">;</span>

    可以看到代码量又显著减少了。各种喜闻乐见啊有木有。

    Arrow Function 箭头函数

    可以说这个是我个人最喜欢的新特性了,能少打几个字不说,代码看起来也更简练直观!

    `var a = [
    “Hydrogen”,
    “Helium”,
    “Lithium”,
    “Beryl­lium”
    ];

    var a2 = a.map(</span>function(s){ return s.length }); //es5
    var a3 = a.map(</span> s => s.length ); // es6

这个特性必须给五星好评。

Share Comments

php-fpm / nginx 修改上传文件限制

一般情况下,新装的phpServer默认的上传大小限制在2m的样子。如果需要上传较大的文件,如视频时将会完全不够用。对于采用 nginx + php-fpm 的布署方式,需要分别修改反向代理服务nginx和php.ini两个软件实现上传限制调整。比如要将上传大小调整至500m

php-fpm

配置文件在 /etc/php5/fpm/php.ini 中,需要修改2项:

1
2
post_max_size 500m
upload_max_filesize 500m

upload_max_filesize 用于限定单一文件的大小,每个被上传的文件不能超过这个值,post_max_size 用于限定同时上传的文件大小总和,限定多文件上传时总的文件大小。

配置完成后,执行下面的命令重启 php-fpm

1
service php5-fpm restart

ps: 不要用 /etc/init.d/php5-fpm restart 命令,这个无法正常重启。完成后刷新 wordpress 可以看到上传限制就会变成 500m (post_max_sizeupload_max_filesize的最小值)

完成php-fpm设定之后还需要调整 nginx 的限制,否则数据还未传到 php 之前就会被 nginx block 掉。

nginx

配置 server 节增加或修改 client_max_body_size 为 500m 然后 service nginx restart 即可。

 

Share Comments

儿子的作文

IMG_20150429_203523

Share Comments

快速上手Promise

这里,我不打算讲Promise的原理,以及回调地狱 blah, blah, blah… 之类。这样的文章网上实在太多了,google 一下一地都是。
但是,讲究竟如何实际去运用的却很少,使用上细节其实很重要。这也是很多人搞不明白这玩意怎么用的原因所在。这里我们以 bluebird 为例看下 Promise 是啥玩意。

首先,Promise它长啥样?

简单来讲,它就是一种把回调转换成链接操作的编程方法。那么什么是链式?jQuery大家都会比较熟悉吧。看下下面这段代码:

1
$('div').css('highlight').html('Hello world').show();

这就是链式操作。那么,这个链式操作是怎么完成的呢?稍微看下 jQuery 的代码或者写过 jQuery 扩展的人都知道,它是通过在每个 function 体最后返回一个 jQuery 对象完成的。在上面这段代码中,所有的调用之后其实都是一个 jQuery 对象。所有的 css,html, show 都是由 jQuery 对象提供的方法。

jQuery和Promise有什么关系?

jQuery 是链式操作的老祖宗,是不是原创的我不敢说,但是 jQuery 把这种风格推广到整个技术界这点是勿用置疑的。本质上 Promise 也是对这链式操作思想的发扬光大。链式操作的优势是很明显的,对比 jQuery 和以往传统的 javascript 可以明显地看到代码可读性不在一个层级上。我们先看下正常情况下如何读取文件:

1
2
3
4
fs.readFile('file.txt', "utf-8", function (err, data) {
if (err) return console.error(err);
console.log(data);
});

很常见的。Promise 完成同样的功能是怎么写呢?首先,要初始化生成 promisified function.

1
2
3
4
5
var Promise = require('bluebird');
var readAsync = Promise.promisify(fs.readFile);
var readFile = function(path) {
return readAsync(path, 'utf-8');
}

然后是使用:

1
readFile('/etc/passwd').then(console.log).catch(console.error);

风格上是不是跟 jQuery 很像?但是好像除了短一点,其它好处不明显呐?

1
2
3
4
5
6
7
fs.readFile('file.txt', 'utf-8', function (err1, data1) {
if (err1) return console.error(err1);
fs.readFile(data1, 'utf-8', function(err2, data2) {
if (err2) return console.error(err2);
console.log(data2);
}
});

vs

1
readFile('file.txt').then(readFile).then(console.log).catch(console.error);

结果是一样的,可读性不在一个层次上。并且,万一哪个回调中忘了写上 if(err) 这样的判断或者是忘了写 return ,除起错了可不好玩!你会不会写错我不知道,反正我不敢保证我一定不会写错!

上面的代码是怎么回事?

这里,我们确定几个术语:

  1. Promise是一种编程方法(思想),哲学上的概念。
  2. bluebird 是对 Promise 的一种实现。
  3. readFile 是 Promisified Function,它会返加一个 Promise对象。一个 Promise对象就是对一个异步任务的包装,它会产生两种结果:resolved代表成功执行下一个 then, rejected代表失败执行 catch。
  4. Promise 对象最主要就是提供了 then 方法(catch、finally 都是特殊的 then)。then 是 Promise 的核心,所以 Promise 对象也称作 Thenable。
  5. then 方法接收一个 function 。在 Promisified Function 顺利完成之后执行这个 function 。出错则执行 catch 的 function

关键的地方在于 then 是怎么运作的?理解 then 也就解决了问题的百分之六十。

  • then 接收一个 function 或一个 promise对象(这点要特别注意,后面有讲到一个示例会牵涉到这个关键点)
  • then 在异步任务完成后调用这个 function ,同时,会把异步操作的结果值传入,所以上面第一个 then 的 readFile 会接收到 file.txt 的内容。
  • then 不管你这个 function 里面返回什么东西,它对返回值只作一个判断:是不是 Promise对象
  • 是 Promise对象:把它插入当前链式操作中,后面的 then 会在这个 Promise对象 完成后继续。
  • 否 把这个返回值传入到下一个 then 中(当然,如果没有 return ,这个值就是 undefined)。
  • 若传入的是promise对象,则会等待该 promise对象执行完成,再继续执行当前的链,之前的结果会被继续传递

总的来讲,then方法执行完毕后总是返回一个 Promise对象。结果是你可以一直 then 下去。。。
好吧,then方法好像很牛逼的样子,那到底怎么牛逼法?

1
2
3
4
5
readFile('file.txt').then(function(data) {
if (data == '')
return readFile('file_init.txt').then(readFile); //是 Promise对象
return data; //不是 Promise对象
}).then(console.log).catch(console.error);`

牛逼的地方就在于我们可以动态的修改链式操作的走向。这样的代码看起来既舒服又简单,试一下用传统的方法写一下?

刚才说的示例

1
2
3
4
5
6
7
8
9
10
11
doSomething().then(function () {
return doSomethingElse();
});

doSomething().then(function () {
doSomethingElse();
});

doSomething().then(doSomethingElse());

doSomething().then(doSomethingElse);

这是下面四种 Promise 的用法有何差异?, ( 从 阮一峰微博里面挪过来的)。 中修正后的代码。
原文还提到:

doSomething & doSomethingElse both promise object

我想说:

No, they are not. They are promisified function. Function and Object are quite different things.

先别往下拉,请先看下上面四种方式,有什么不同?如果明白了,说明你已经基本掌握 Promise 了。

确认结论是否正确(修正)

  1. 脱裤子放屁,多了一层 function 包装,跟4是一样的效果。
  2. 它后面再加 then 是不会等到 doSomethinElse 完成后执行,而是同时进行。
  3. 传入的其实是一个 promise object,这个 promise object 会被执行并等待其完成,但结果会被忽略,第三个 then 接收到的仍是第一个 readFile 的结果。
  4. doSomethingElse 本身就是个 function,传入后会被调用,返回一个 Promise对象 ( doSomethingElse 不是 promise object, 它的返回值才是 promise object)。

    当然,看不出来也说明不了什么问题,因为这样的示例迷惑性很强,给出的提示也不明显。正常情况下更不会存在这样的代码。顺便一提,这也是函数式语言的 side effect,很容易绕晕掉到坑里。根据 We have a problem with promises里面所讲,掉坑的人不在少数!

还需要知道其它的吗?

以上只是基础知识,实际应用时当然还有一些扩展的方法需要去了解,比如 spread, all 等。npmjs.org 上的文档那是必读的……

Share Comments

VS2013 Community使用T4 Toolbox实现更改Resource文件自动生成cs文件

.NET/VS 最强大的功能之一就是多语言的支持,通过利用 Resource File (.resx) 资源文件。可以让界面根据当前运行环境自动显示对应的语言。内置的很多有需要文字的类也有对应的支持,如 DisplayAttribute, RquiredAttribute 等。

如何使用?

首先,在你的工程文件创建一个 Resources 文件夹(当然也可以是其它名称,i18n之类的也可以)。然后在文件夹内添加一个 Resource File (.resx)文件,文件名你可以自定,比如说我们这里叫 Resource.resx ,Resource.resx 就会成为默认的本地化资源文件。在程序运行的过程中,如何没有找到合适的资源文件,就会使用这个默认的资源文件。

然后,双击打开 Resource.resx 文件,添加相应的名值和内容,可以是文字,图片,文本文档和 Object 格式。比如说我们添加一个名为 APP_NAME 值为 Hello world 的文字项。在需要使用到Hello world 的地方,我们使用 Resources.Resource.APP_NAME 即可得到相应的文字。针对 DisplayAttribute ,我们需要使用这样的方式让 DisplayName 实现本地化:

1
[Display(Name = "APP_NAME", ResourceType = typeof(Resources.Resource)]

这样在使用 Html.LabelFor 的时候,相应的文字也会变化。

后期,在需要添加新的本地化资源时,只需将 Resource.resx 复制一份,重命名为:Resource.{culture}.resx 即可!比如说默认的是英文的,现在要增加简体中文的支持,就是重命名为 Resource.zh-Hans.resx。很方便吧!

不需要多语言支持也建议使用

即使你Programming的过程中短期内无需支持多语言,在一开始就使用多语言支持的策略也是有好处的。抛开后期扩展不讲,一般情况下我们在程序Programming过程中不可避免地要和错误和错误信息打交道,执行逻辑中若遇到问题我们需要向调用方返回一个错误码和错误信息,处理的情况不外乎使用 enum 或者 string 来处理这样的情况。string 最大的问题是无智能感知,万一拼错了整个程序就无法正常运行,enum 的问题就是中文处理起来不便,通过使用 Resource.resx + DisplayAttribute 的方式也可以绑定错误信息进行处理。但这样要定义一个 enum 再定义 DisplayAttribute 再添加 resource 键值太麻烦了,而且也没有智能感知,容易打错!

更好的方式?

显然,我们需要的是一个 ERROR_CODE 和它对应的 ERROR_MESSAGE, 我们就可以满足错误子系统的需求。如果可以通过 Resources.Resource.APP_NAME 这样的方式去引用 ERROR_CODEERROR_MESSAGE 即可实现智能感知防止输错的问题,整个引用也会简单很多,更快速,不易出错。 Resource.resx 是通过自动生成一个对应的 cs 文件实现 Resources.Resource.APP_NAME 这样的智能感知引用(你只要点击一个 resx 文件的+号就能看到对应的 cs 文件)。

如果我们可以操纵这个 cs 文件的生成那就不OK了?那么,如何实现?一番 google 之后,我发现 T4 就是真爱!

T4 真是太他娘好用了,在相同文件夹内,新建一个 Text Template (.tt)文件,将它的名称改成跟源文件同名,它就会根据同名文件(后缀不同)生成对应的文件(默认是cs文件)然后包含到工程中!这个真是简单直白!在我们开始应用 TT 之前,先要把 Resource.resx 属性中的 Custom Tool 去掉(这个就是为什么 .resx 会自动生成.cs 文件的原因,清空它就不会再生成 .cs 文件)。然后把 tt 文件改成 Resource.tt ,最后把这个我在网上偷来的模板内容粘贴到 tt 文件里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
T4Resx Version 0.1
Maintained by Kenneth Baltrinic
http://blog.baltrinic.com

Related blog posts: http://blog.baltrinic.com/software-development/dotnet/t4-template-replace-resxfilecodegenerator

The certain parts of this template were copied from the T4MVC template which is distributed under the MvcContrib license (http://mvccontrib.codeplex.com/license)

This template if free for redistribution in accordance with the same license.
*/
#>
<#@ template debug="true" hostspecific="true" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#
   var serviceProvider = Host as IServiceProvider;
    if (serviceProvider != null) {
        Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
    }

    // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
    if (Dte == null) {
        throw new Exception("T4MVC can only execute through the Visual Studio host");
    }

    Project = GetProjectContainingT4File(Dte);

    if (Project == null) {
        Error("Could not find the VS Project containing the T4 file.");
        return"XX";
    }

     AppRoot = Path.GetDirectoryName(Project.FullName) + '\';
     RootNamespace = Project.Properties.Item("RootNamespace").Value.ToString();
#>
using System.Threading;
using System.Web;

<#
try{
    AllEntries = new List<ResourceEntry>();
    FindResourceFilesRecursivlyAndRecordEntries(Project.ProjectItems, "");
    AllEntries.Sort( new Comparison<ResourceEntry>( (e1, e2) => (e1.Path + e1.File + e1.Name).CompareTo(e2.Path + e2.File + e2.Name)));

    var currentNamespace = "";
    var currentClass = "";
    var thisIsFirstEntryInClass = true;
    var names = new List<string>();
    foreach(var entry in AllEntries)
    {
        //WriteLine("//" + entry.Path + ":" + entry.File+ ":" + entry.Name);

        var newNamespace = ("Resources." + entry.Path + ".").Replace(".Resources.", ".");
        newNamespace = RootNamespace + "." + newNamespace.Substring(0, newNamespace.Length-1);
        var newClass = entry.File;

        bool namesapceIsChanging = newNamespace != currentNamespace;
        bool classIsChanging = namesapceIsChanging || newClass != currentClass;

        //Close out current class if class is changing and there is a current class
        if(classIsChanging &amp;&amp; currentClass != "")
        {
            EmitNamesInnerClass(names);
            WriteLine("t}");
        }

        if(namesapceIsChanging)
        {
            //Close out current namespace if one exists
            if( currentNamespace != "" )
                WriteLine("}");

            currentNamespace = newNamespace;

            //open new namespace
            WriteLine(string.Format("namespace {0}", currentNamespace));
            WriteLine("{");

        }

        if(classIsChanging)
        {
            currentClass = newClass;
            WriteLine(string.Format("tpublic class {0}", currentClass));
            WriteLine("t{");
            thisIsFirstEntryInClass = true;

        //Emit code for the ResourceManager property and GetResourceString method for the current class
        #>
    private static global::System.Resources.ResourceManager resourceMan;

    /// <summary>
    ///   Returns the cached ResourceManager instance used by this class.
    /// </summary>
    [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
    public static global::System.Resources.ResourceManager ResourceManager
        {
      get
            {
        if (object.ReferenceEquals(resourceMan, null))
                {
          global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("<#=string.Format("{0}.{1}{2}", RootNamespace, entry.Path + "." + entry.File, entry.Type) #>", typeof(<#=entry.File#>).Assembly);
          resourceMan = temp;
        }
        return resourceMan;
      }
    }

    /// <summary>
    ///   Returns the formatted resource string.
    /// </summary>
    [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
    public static string GetResourceString(string key, params string[] tokens)
        {
            var culture = Thread.CurrentThread.CurrentCulture;
      var str = ResourceManager.GetString(key, culture);

            for(int i = 0; i < tokens.Length; i += 2)
                str = str.Replace(tokens[i], tokens[i+1]);

        return str;
    }

    /// <summary>
    ///   Returns the resource string.
    /// </summary>
    [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
    public static string GetString(string key)
        {
            return ResourceManager.GetString(key);
    }
        <#
        }

        //Emit the static resource string access method for the current entry
        if(entry.Comment != null)
        {
            if(!thisIsFirstEntryInClass) WriteLine("");
            WriteLine(string.Format("rntt///<summary>rntt///{0}rntt///</summary>", entry.Comment.Replace("rn", "rntt///")));
        }
        else
            WriteLine("");

        //Select all tokens between braces that constitute valid identifiers
        var tokens = Regex.Matches(entry.Value, @"{(([A-Za-z]{1}w*?)|([A-Za-z_]{1}w+?))?}").Cast<Match>().Select(m => m.Value);

        if(tokens.Any())
        {
            var inParams = tokens.Aggregate("", (list, value) => list += ", string " + value)
                .Replace("{", "").Replace("}", "");
            if(inParams.Length > 0 ) inParams = inParams.Substring(1);
            var outParams = tokens.Aggregate("", (list, value) => list += ", "" + value +"", " + value.Replace("{", "").Replace("}", "") );

            if(entry.Value.StartsWith("HTML:"))
                WriteLine(string.Format("ttpublic static HtmlString {0}({1}) {{ return GetResourceHtmlString("{0}"{2}); }}",  entry.Name, inParams, outParams));
            else
                WriteLine(string.Format("ttpublic static string {0}({1}) {{ return GetResourceString("{0}"{2}); }}",  entry.Name, inParams, outParams));
        }
        else
        {
            if(entry.Value.StartsWith("HTML:"))
                WriteLine(string.Format("ttpublic static HtmlString {0} {{ get {{ return GetResourceHtmlString("{0}"); }} }}",  entry.Name));
            else
            {
                WriteLine(string.Format("ttpublic static string {0} {{ get {{ return GetResourceString("{0}"); }} }}",  entry.Name));
                //WriteLine(string.Format("ttinternal static string V{0} {{ get {{ return GetResourceString("{0}"); }} }}",  entry.Name));
                }
        }
        names.Add(entry.Name);

        thisIsFirstEntryInClass = false;

    } // foreach(var entry in AllEntries)

    //close out the current class when done
    if(currentClass != "")
    {
        EmitNamesInnerClass(names);
        WriteLine("t}");
    }
}
catch(Exception ex)
{
    Error(ex.ToString());
}
#>
}
<#+
    const string Kind_PhysicalFolder = "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}";
    bool AlwaysKeepTemplateDirty = true;
    static DTE Dte;
    static Project Project;
    static string AppRoot;
    static string RootNamespace;
    static List<ResourceEntry> AllEntries;

void FindResourceFilesRecursivlyAndRecordEntries(ProjectItems items, string path)
{
    foreach(ProjectItem item in items)
    {
        if(Path.GetExtension(item.Name) == ".resx")
            RecordEntriesInResourceFile(item, path);
        if(item.Kind == Kind_PhysicalFolder)
            FindResourceFilesRecursivlyAndRecordEntries(item.ProjectItems, path+"."+item.Name);
    }
}

void RecordEntriesInResourceFile(ProjectItem item, string path)
{
    //skip resource files except those for the default culture
    if(Regex.IsMatch(item.Name, @".*.[a-zA-z]{2}(-[a-zA-z]{2})?.resx"))
            return;

    var filePath = (string)item.Properties.Item("FullPath").Value;
    var xml = new XmlDocument();
    xml.Load(filePath);
    var entries = xml.DocumentElement.SelectNodes("//data");

    var parentFile = item.Name.Replace(".resx", "");
    var fileType = Path.GetExtension(parentFile);
    if(fileType != null &amp;&amp; fileType != "")
        parentFile = parentFile.Replace(fileType, "");

    foreach (XmlElement entryElement in entries)
    {
        var entry = new ResourceEntry
        {
            Path = path.Substring(1),
            File = MakeIntoValidIdentifier(parentFile),
            Type = fileType,
            Name = MakeIntoValidIdentifier(entryElement.Attributes["name"].Value)
        };
        var valueElement = entryElement.SelectSingleNode("value");
        if(valueElement != null)
            entry.Value = valueElement.InnerText;
        var commentElement = entryElement.SelectSingleNode("comment");
        if(commentElement != null)
            entry.Comment = commentElement.InnerText;

        AllEntries.Add(entry);
    }
}

string MakeIntoValidIdentifier(string arbitraryString)
{
    var validIdentifier = Regex.Replace(arbitraryString, @"[^A-Za-z0-9-._]", " ");
    validIdentifier = ConvertToPascalCase(validIdentifier);
    if (Regex.IsMatch(validIdentifier, @"^d")) validIdentifier = "_" + validIdentifier;
    return validIdentifier;
}

string ConvertToPascalCase(string phrase)
{
    string[] splittedPhrase = phrase.Split(' ', '-', '.');
    var sb = new StringBuilder();

    sb = new StringBuilder();

    foreach (String s in splittedPhrase)
    {
        char[] splittedPhraseChars = s.ToCharArray();
        if (splittedPhraseChars.Length > 0)
        {
            splittedPhraseChars[0] = ((new String(splittedPhraseChars[0], 1)).ToUpper().ToCharArray())[0];
        }
        sb.Append(new String(splittedPhraseChars));
    }
    return sb.ToString();
}

void EmitNamesInnerClass(List<string> names)
{
    if(names.Any())
    {
        WriteLine("rnttpublic static class Names");
        WriteLine("tt{");
        foreach(var name in names)
            WriteLine(string.Format("tttpublic const string {0} = "{0}";", name));
        WriteLine("tt}");

        names.Clear();
    }
}

Project GetProjectContainingT4File(DTE dte) {

    // Find the .tt file's ProjectItem
    ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);

    // If the .tt file is not opened, open it
    if (projectItem.Document == null)
        projectItem.Open(Constants.vsViewKindCode);

    if (AlwaysKeepTemplateDirty) {
        // Mark the .tt file as unsaved. This way it will be saved and update itself next time the
        // project is built. Basically, it keeps marking itself as unsaved to make the next build work.
        // Note: this is certainly hacky, but is the best I could come up with so far.
        projectItem.Document.Saved = false;
    }

    return projectItem.ContainingProject;
}

struct ResourceEntry
{
    public string Path { get; set; }
    public string File { get; set; }
    public string Type { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    public string Comment { get; set; }
}
#>

保存之后你会发现,就会自动生成新的 Resource.cs 文件,并且多了一个 Names 的 nested Class。现在我们可以通过 Resource.Names.APP_NAME 引用到 APP_NAME, Resource.APP_NAME 引用到 Hello world。KEY和VALUE都有了,这样我们就可以同时智能感知到 ERROR_CODEERROR_MESSAGE了。

但是还有一个问题, 由于 t4 引擎是在 tt 文件改动后,或者要手动调用 Run Custom Tool 才会生成新的 .cs 文件。而修改源文件 Resource.resx 是不会生成新文件的!这样很不方便,每次添加新项都要去点好几个鼠标!太不科学了!

接近完美的解决方案

这个方法需要有 VS 能支持 Extension ,也就是说 Express 版肯定是不行的。现有免费版本中只有 Community 版可以支持到,推荐使用(法律上讲公司、商业组织是不能使用的)。总之在 TOOL菜单下面有个 Extensions And Updates ,在里面 Online 搜索 T4 TOOLBOX,安装,重启VS。

然后要做两个设定,

首先把 Resource.tt 的 Custom Tool 清空(同样也是为什么 tt 文件能产生 cs 文件的原理,它通过这个 Custom Tool 属性让 VS 在它变动时自动执行)。

然后把 Resource.resx 的 Custom Tool 设定为 T4Toolbox.TemplatedFileGenerator

这样就OK了,现在每次更改 Resource.resx 就会自动生成新版本的 .cs 文件。

Share Comments

手动安装 node-heapdump 进行内存泄露检测

Windows 7 x64 系统, node.js v0.12.2,Visual Studio 2013 Community

npm/cnpm 安装皆报错,貌似是由于工作文件是2010格式的,尝试配置 node-gyp 使用2013 版本,失败!

最后没办法,把 node-heapdump 下载到本地手动进行安装。有些包可能依赖 windows SDK,这里是下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=8279

git clone https://github.com/bnoordhuis/node-heapdump
cd node-heapdump
node-gyp configure build

克隆到本地,进入,执行build,接着报错!build 是会自动生成对应平台的工程文件再调用相关的生成工具进行 build 。关键就是使用 msbuild 的时候会出错。

接下来就是用 vs2013 打开 build 下面的 sln 文件,会提示升级,升完级编译一下就OK了。

编译过程中可能会提示找不到 node.lib,这是由于引用路径错误引起的,可以右击项目 properties -> Linker -> Input,把 Additional Dependencies 中的

kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:UsersAdministrator.node-gyp.12.2$(Configuration)node.lib"

改成

kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:UsersAdministrator.node-gyp.12.2$(Platform)node.lib"

完成后打开命令行进入到 node-heapdump 目录,执行:

npm install . -g

把它安装到全局是因为一般情况下不需要引用到这个包,偶尔调试的时候才需要 require ,放在全局即可。键入以下命令查看 npm 全局包的存放位置:

npm config list

回显的 prefix 就是全局包的目录

为了能在有需要的地方可以 require 到全局包,我们还需要为系统添加一个 NODE_PATH 环境变量,以便在任何地方都可以加载这个包。

计算机=》属性=》高级系统设置=》环境变量=》新建=》NODE_PATH

把上面查到的包位置放入这个变量,现在在任意位置都可以 require(‘heapdump’)。

heapdump用来检测内存泄露的,一般需要观察到内存增长才进行 dump 操作。建议的做法是在目标脚本上开一个 repl socket 服务端口,在我们需要时候就 telnet 到这个商品,执行 heapdump。

// telnet 调试
require('net').createServer(function(socket) {
  require('repl').start('myapp>', socket);
}).listen(3001, 'localhost');

windows 7 自带的 telnet 是坨大便,建议使用 cygwin 的 telnet 程序,需要安装 inetutils 才有。

$ telnet localhost 3001
myapp> require('heapdump').writeSnapshot()

完成后按 Ctrl + ] 结束会话。

使用 Chrome F12 打开 developer toolbar 在 profiles 点击 load 就可以加载查看内存快照。

 

Share Comments

IIS 设定上传目录执行与浏览权限

Programming应用中很多时候需要允许用户自行上传图片之类的文件,一般我们会对文件后缀做处理来防止用户上传恶意程序。

为了更进一步确定用户上传的文件不会被运行,我们可以对 IIS 进行设置,禁止IIS执行用户上传目录下面的文件:

在 web.config 中的 configuration 节点,加入以下配置:

1
2
3
4
5
6
7
8
9
10
11
  <location path="files">
    <system.webServer>
      <handlers>
        <clear />
        <add name="StaticFile" path="*" verb="*" modules="StaticFileModule" resourceType="Either" requireAccess="Read" />
      </handlers>
      <staticContent>
        <mimeMap fileExtension=".*" mimeType="application/octet-stream" />
      </staticContent>
    </system.webServer>
  </location>

这会清空 files 目录下所有的 http handler, 只留一个 StaticFile 处理静态文件。

Share Comments