之前写过一个browserSync的脚本编译jade/sass/coffee,在多个项目中拷贝脚本文件比较麻烦,业余封装了一个npm插件,这里介绍一下用法。
项目地址:https://github.com/thinkjs/autocommand-cli,插件使用typescript开发,欢迎有兴趣的同学一起完善。
mkdir example && cd example
mkdir _source _source/jade _source/sass _source/live
mkdir html html/css html/js
_source目录存放源代码,_source/jade、_source/sass、_source/live分别存放jade、sass、livescript源文件。html目录存放生成结果,html、html/css、html/js分别存放html、css、js文件。
echo '{}' > package.json
npm install thinkjs/autocommand-cli#latest jade node-sass livescript --save-dev
如果网络不好,建议使用cnpm替代
"scripts": { "acmd": "acmd", "start": "acmd watch", "build": "acmd run" },
完整的package.json如下
{ "scripts": { "acmd": "acmd", "start": "acmd watch", "build": "acmd run" }, "devDependencies": { "autocommand-cli": "thinkjs/autocommand-cli#latest", "jade": "^1.11.0", "livescript": "^1.5.0", "node-sass": "^3.9.3" } }
npm run acmd -- config -i
默认在当前目录下创建名为_config的配置文件,编辑该配置文件
修改file字段内容为:
"file": ["_source/**/*.{jade,sass,ls}"],
修改define字段内容为:
"define": { "_source/": { ".jade": { "file": "html/#{fileName}.html", "command": "jade -Po html/ #{file}" }, ".sass": { "file": "html/css/#{fileName}.css", "command": "node-sass --output-style compact #{file} html/css/#{fileName}.css" }, ".ls": { "file": "html/js/#{fileName}.js", "command": "lsc -co html/js/ #{file}" } } },
修改 browserSync > init下的server为:
"server": { "baseDir": "html/" },
完整配置文件如下:
{ // 侦听的文件 "file": ["_source/**/*.{jade,sass,ls}"], // 过滤 "ignore": ["_*.*", "node_modules/"], // 变量 "variable": { "localBin": "~/node_modules/.bin" }, // 环境变量 "environment": { ":PATH": "#{localBin}" }, // 定义 "define": { "_source/": { ".jade": { "file": "html/#{fileName}.html", "command": "jade -Po html/ #{file}" }, ".sass": { "file": "html/css/#{fileName}.css", "command": "node-sass --output-style compact #{file} html/css/#{fileName}.css" }, ".ls": { "file": "html/js/#{fileName}.js", "command": "lsc -co html/js/ #{file}" } } }, // browserSync配置 "browserSync": { // 初始化配置 "init": { "server": { "baseDir": "html/" }, "open": false, "ui": false }, // 启动livereload "reload": true } } // vim: se sw=2 ts=2 sts=2 ft=javascript et:
创建_source/jade/index.jade,内容如下:
doctype html html head title jade page body h1 hello world
在terminal中运行 npm start,浏览器访问 http://localhost:3000,可查看页面。
创建_source/sass/style.sass,内容如下:
html, body padding: 0 margin: 0 font-size: 16px h1 color: green
编辑_source/jade/index.jade加入对style.sass的引用,在head中插入以下内容:
link(href="css/style.css" rel="stylesheet")
保存查看浏览器,内容已自动刷新。
创建_source/sass/script.ls,内容如下:
console.log \hello
编辑_source/jade/index.jade加入对script.ls的引用,在body中插下以下内容:
script(src="js/index.js")
保存可查看浏览器中的控制台输出。
有时需要对项目的所有源文件进行编译,使用如下命令:
npm run build
比如需要编译所有的sass文件,可以使用如下命令:
npm run build -- -f _source/sass/*.sass
-f选项支持glob表达式,使用引号括起即可,例如编译jade和sass可以使用如下命令:
npm run build -- -f '_source/**/*.{jade,sass}'
如果使用git管理文件,需要编译git中已经修改的文件,可以使用如下命令:
git status -uno | awk '{print $2}' | npm run build -- -o
使用find查找sass进行编译,命令如下:
find _source -type f | grep *.sass | npm run build -- -o
autocommand-cli项目最初的出发点是为了锻炼一下typescript的能力,经过半年的迭代,功能已经逐渐完善。命令行参数解析、文件改动侦听、http服务器功能都使用现成npm插件实现。前端的工具链不断完善,各种工具层出不穷,花大量时间造一个自己用起来的顺手的工具还是有点奢侈:)
近期公司站点通过联通4G网站访问时被运营商插入广告代码,通过手机抓包定位到运营商值入的广告代码。
植入的广告代码通过在固定url的请求中添加内容插行一段js,通过body.appendChild在页面引入一段js代码。想到一个应急方案,通过改写appendChild来拦截广告,经测试有效。代码如下:
(function() { var rootEl = top.document.body; var originAppendChild = rootEl.appendChild; try { rootEl.appendChild = function (elem) { var src = ''; if (typeof elem == 'object' && elem != null && elem.getAttribute) { src = elem.getAttribute('src'); } if (typeof src == 'string' && src.length) { if ( src.match(/^(?:http(?:s)?:)?\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) || src.match(/cifenqi\.com/) != null) { return; } } originAppendChild.apply(rootEl, [].slice.call(arguments, 0)); } } catch(e) { } })();
ISP广告干的是非法勾当多使用ip地址,因此直接屏蔽ip地址的元素。此方法只能应急可被绕过,难怪某度、某宝纷纷启用全站https。
之前因为vim上的TFS插件一直不好使,自己写了一个简单的插件在vim上签出TFS源文件。为了在NERDTree上面集成了菜单功能,研究了下NERDTree插件。年底忙里偷闲整理了一些关于NERDTree的知识点,分享出来。
1. 添加NERDTree文件菜单
在vim插件目录创建一个nerdtree_plugin文件夹,在其中新建vim脚本文件,使用NERDTreeAddMenuItem函数添加一项按钮项。参数格式:
{'text': 'menu text', 'shortcut': 'short key', 'callback':'callback function name'}
示例:
call NERDTreeAddMenuItem({ 'text': 'TFS Check (o)ut', 'shortcut': 'o', callback': 'TFSCheckoutCallback' })
2. 获取当前文件节点
let currentNode = g:NERDTreeFileNode.GetSelected()
3.获取文件路径
let currentFile = currentNode.path._strForGlob()
4.更新文件状态
cal currentNode.path.refresh()
cal NERDTreeRender()
# 当我们通过currentNode.path.refresh() 可以刷新文件节点,之后通过NERDTreeRender重绘获得更新。当通过操作修改了文件的属性后需要通过此操作更新文件状态。
附插件git地址:https://github.com/thinkjs/TFSCheckout
NERDTree FileNode参考:https://github.com/scrooloose/nerdtree/blob/5.0.0/lib/nerdtree/tree_file_node.vim
NERDTree Pathp参考:https://github.com/scrooloose/nerdtree/blob/5.0.0/lib/nerdtree/path.vim
NERDTree Public API参考:https://github.com/scrooloose/nerdtree/blob/5.0.0/plugin/NERD_tree.vim#L164
之前使用预处理器一直是基于vim插件autocommand,最近和同事协作开发,vim入门太陡峭,抽了点时间用browser-sync实现了类似功能。具体功能如下:自动检测jade、sass、livescript等源码文件的改动,编译对应的html、css、js,更新改动到浏览器中打开的页面。
明确几个定义:
1. browser-sync nodejs插件,官方说明 Time-saving synchronised browser testing。
2. 前端预处理器 jade、sass、livescript。
3. 工作流 使用browser-sync整合jade、sass、livescript完成开发的一套流程。
依赖:
nodejs
npm package: jade、livescript、browser-sync
ruby
gem package: sass
工作目录:
说明:_source目录下存放预处理器源代码,html目录存放生成的html、css、js等。
在workspace目录下创建 browserSync.ls 具体代码如下:
path = require \path exec = require \child_process .exec browserSync = require \browser-sync .create! # config {{{ outputDir = 'html' cssOutputDir = "#outputDir/css" jsOutputDir = "#outputDir/js" reloadWatchFile = '' # "#outputDir/*.html" # "#jsOutputDir/*.js" # "#outputDir/img/*.*" compileWatchFile = "_source/jade/*.jade" "_source/sass/*.sass" "_source/live/*.ls" autoCompileFile = true # }}} # getTimeToken {{{ getTimeToken = -> currDate = new Date() hours = currDate.getHours() minutes = currDate.getMinutes() seconds = currDate.getSeconds() if hours < 10 hours = "0#hours" if minutes < 10 minutes = "0#minutes" if seconds < 10 seconds = "0#seconds" "#hours:#minutes:#seconds" # }}} # compileTask {{{ getCompileCmdAndFileName = (file, ext) -> filename = path.basename file, ext switch ext case '.jade' then compileFileName = "#outputDir/#{filename}.html" cmd = "jade -Po #outputDir #file" case '.sass' then compileFileName = "#cssOutputDir/#{filename}.css" cmd = "sass --sourcemap=none --style compact #file|sed '/^@charset/d'>#compileFileName" case '.coffee' then compileFileName = "#jsOutputDir/#{filename}.js" cmd = "coffee --no-header -bco #jsOutputDir #file" case '.ls' then compileFileName = "#jsOutputDir/#{filename}.js" cmd = "lsc --no-header -bco #jsOutputDir #file" default compileFileName = cmd = '' [cmd, compileFileName] compileTask = (file, ext, reload) !-> cmdIndex = -1 try [cmd, filename] = getCompileCmdAndFileName file, ext if not cmd or not filename console.log "cmd not define. file: #file ext: #ext" # exec callback execCallback = (err, stdo, stde) !-> if err is null and not stde if cmdIndex is -1 console.log "[#{getTimeToken!}] compiled #filename" reload filename if reload else execCmd() else console.log err || stde # execute command do execCmd = !-> if Array.isArray cmd currCmd = cmd[++cmdIndex] if cmd.length <= cmdIndex+1 ``cmdIndex = -1;`` else currCmd = cmd if currCmd exec currCmd, execCallback compileCallback = (file) !-> ext = path.extname file filename = path.basename file # ignore partial file if filename.charAt(0) is '_' return undefined switch ext case '.jade', '.coffee', '.ls', '.sass' # compileTask file, ext # case '.sass' compileTask file, ext, browserSync.reload default console.log 'unknown file type.' # }}} # browserSync {{{ browserSync.init do server: baseDir: outputDir index: \index.html open: false if reloadWatchFile and reloadWatchFile.length browserSync.watch reloadWatchFile .on \change, browserSync.reload wacher = browserSync.watch compileWatchFile .on \change, compileCallback # auto compile file if autoCompileFile wacher.on \add, compileCallback # }}} # vim: set sw=2 ts=2 sts=2 et fdm=marker:
使用方法如下:
cd workspace
lsc browserSync.ls
* lsc 是 livescript的命令,使用npm 全局安装 livescript 后方可使用。
配置说明:
outputDir html文件的输出目录,web server的根目录。
cssOutputDir css文件输出目录。
jsOutputDir js文件输出目录。
reloadWatchFile 检测改动的目录,如果文件发生变更,会更新到打开的页面中。html/css/js已自动更新,默认可留空。
compileWatchFile 监测的源码文件,如果发生变化执行编译任务。
autoCompileFile 自动编译文件,设为true时,每次启动会把所有的源码文件编译一遍。
vim-coloresque是一款高亮插件,将css/sass/less中的颜色用背景色高亮出来方便预览。用了一段时间发现代码补全一定程度上受影响,自己对插件进行了一些优化,修改后git地址:https://github.com/thinkjs/vim-coloresque。
看插件github上的issue,插件中对isk这个选项的设置影响了补全,全并且影响了vim全局的keyword识别,直接注释掉isk+=-、isk+=#、 isk+=# 解决了补全问题但针对colorDict的高亮识别又发生错误,例如样式名为 .red或.common-red-btn,此处的red会被高亮。作者使用"\<"单词边界来识别colorDict,修改isk以后单词边界识别出错高亮出错,想到用正则表达式来调整此处的colorDict匹配。
匹配规则如下:
1. 前字符为行首或空白或:(冒号)
2. 后字符为行尾或空白或;(分号)
正则如下:
'\(^\|\s\|:\)\@<=\c'._color.'\($\|\s\|;\)\@='
vim中使用@<=作逆序环视、@=做顺序环视(vim中使用:h @<=查看详细介绍),修改后效果不错,符合我的应用场景。diff代码如下:
From f162c7a118caba4af655741feeae11720329bb4e Mon Sep 17 00:00:00 2001 From: leaf Date: Wed, 21 Jan 2015 16:27:40 +0800 Subject: [PATCH] remove "isk" change, improve color dict regexp --- after/syntax/css/vim-coloresque.vim | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/after/syntax/css/vim-coloresque.vim b/after/syntax/css/vim-coloresque.vim index c80a9a5..b60f863 100644 --- a/after/syntax/css/vim-coloresque.vim +++ b/after/syntax/css/vim-coloresque.vim @@ -122,9 +122,9 @@ function! s:VimCssInit(update) if a:update==1 call s:ClearMatches() endif - :set isk+=- - :set isk+=# - :set isk+=. + " :set isk+=- + " :set isk+=# + " :set isk+=. if len(keys(b:color_pattern))>0 call s:RestoreColors() @@ -301,7 +301,10 @@ function! s:AdditionalColors() "let w:colorDictRegExp = '\(' for _color in keys(w:colorDict) "let w:colorDictRegExp.='\<'._color.'\>\|' - call s:MatchColorValue(strpart(w:colorDict[tolower(_color)], 1), '\<\c'._color.'\>') + " old + " call s:MatchColorValue(strpart(w:colorDict[tolower(_color)], 1), '\<\c'._color.'\>') + " new regexp + call s:MatchColorValue(strpart(w:colorDict[tolower(_color)], 1), '\(^\|\s\|:\)\@<=\c'._color.'\($\|\s\|;\)\@=') endfor "let w:colorDictRegExp=strpart(w:colorDictRegExp, 0, len(w:colorDictRegExp)-2).'\)\c' endfunction -- 1.7.11.1
编辑缩进嵌套的文件时想找到对应的层级比较困难,写了一个函数,使用cc选项设定一条辅助线,标识到指定的缩进层级。代码如下:
" -------------------------------------------------- " [参考线切换] {{{ " -------------------------------------------------- fu! ReferenceLine(t) if exists('w:ccnum') let ccnum=w:ccnum elsei exists('b:ccnum') let ccnum=b:ccnum else let ccnum=0 en let oldcc=ccnum " let ccc=&cc " ec oldcc let ccc=','.&cc.',' " add/sub if a:t=='add' || a:t=='sub' " check old cc if match(ccc, ','.oldcc.',')<0 let oldcc=0 let ccnum=0 en " step let csw=&sw if a:t=='add' let ccnum=ccnum + csw elsei a:t=='sub' let ccnum=ccnum - csw if ccnum < 0 | let ccnum=0 | en en if oldcc > 0 | let ccc=substitute(ccc, ','.oldcc.',', ',', '') | en let ccc=ccc.ccnum " ec ccc " ec ccnum let ccc=substitute(ccc, '^0,\|,0,\|,0$', ',', 'g') let ccc=substitute(ccc, '^,\+\|,\+$', '', 'g') " ec ccc let w:ccnum=ccnum let b:ccnum=ccnum exec "setl cc=".ccc " del elsei a:t=='del' let ccc=substitute(ccc, ','.oldcc.',', ',', '') let ccc=substitute(ccc, '^,\+\|,\+$', '', 'g') " ec ccc let w:ccnum=0 let b:ccnum=0 exec "setl cc=".ccc en endf nn <silent> <A-u> :call ReferenceLine('sub')<CR> nn <silent> <A-o> :call ReferenceLine('add')<CR> " }}}
使用方法:
Alt+o 增加参考线、Alt+u 减少参考线,最后两行是键映射,可以按照需求自行修改。
设定参考线后,如果想要设定cc做列宽参照,请使用set cc+=<num>来设定。
vim-addon-manager是一款功能非常强大的vim插件管理工具,但其依赖git, hg, svn, curl, unzip等工具在win平台配置较繁琐,一直没在win平台中使用这种方案。这两天在试着在win平台配置,分享一下配置过程。
1. 依赖程序
1.1 git 主页 http://msysgit.github.io/
下载地址:https://github.com/msysgit/msysgit/releases/download/Git-1.9.2-preview20140411/Git-1.9.2-preview20140411.exe
1.2 svn 主页 http://www.sliksvn.com/
下载地址:http://www.sliksvn.com/pub/Slik-Subversion-1.8.9-x64.msi
1.3 hg 主页 http://mercurial.selenic.com/
下载地址:http://mercurial.selenic.com/release/windows/Mercurial-3.0.exe
msysgit包中带有curl以及unzip等工具
2. 添加环境变量
编辑vim配置文件($VIM/.vimrc)为vim指定相关环境变量(将C:\Program Files替换成自己的安装路径)
if(has('win32') && match($PATH,'SlikSvn')<0) let $PATH=$PATH.';C:\Program Files\SlikSvn\bin;C:\Program Files\Mercurial;C:\Program Files\Git\bin' en
3. 下载vim-addon-manager并配置
从https://github.com/MarcWeber/vim-addon-manager 获取源代码到插件目录,在vimrc中添加配置。
附官方建议配置:
fun! SetupVAM() let c = get(g:, 'vim_addon_manager', {}) let g:vim_addon_manager = c let c.plugin_root_dir = expand('$HOME', 1) . '/.vim/vim-addons' " most used options you may want to use: " let c.log_to_buf = 1 " let c.auto_install = 0 let &rtp.=(empty(&rtp)?'':',').c.plugin_root_dir.'/vim-addon-manager' if !isdirectory(c.plugin_root_dir.'/vim-addon-manager/autoload') execute '!git clone --depth=1 git://github.com/MarcWeber/vim-addon-manager ' \ shellescape(c.plugin_root_dir.'/vim-addon-manager', 1) endif call vam#ActivateAddons([], {'auto_install' : 0}) endfun call SetupVAM() VAMActivate matchit.zip vim-addon-commenting
不建议将所有配置内容都放在vimrc中,向大家推荐我整理的vim配置分割方案:http://zshou.i11r.com/posts/39266.html
1.配置用户超时
<Project>/config/initializers/devise.rb
编辑 # config.timeout_in = 30.minutes 这一行,修改为想要配置的时间
<Project>/app/models/user.rb
配置devise :database_authenticatable, :registerable,这一行,加上 :timeoutable
2.设置用户单处登陆
原理:devise在用户登陆后会设置users表中的current_sign_in_at字段为最后一次登陆的时间,用户每次登陆后在session中保存current_sign_in_at字段,在application中校验session中current_sign_in_at字段,如果为空或者小于current_user对象的current_sign_in_at则强制注销
1) 在application_controller基类中定义after_sign_in_path_for方法在当中设置session对象
def after_sign_in_path_for(resource) session[:current_sign_in_at] = current_user.current_sign_in_at root_path end
2) 定义has_signed取代:authenticate_user!判断用户登陆
def has_signed unless user_signed_in? redirect_to root_path else if session[:current_sign_in_at] == nil or session[:current_sign_in_at] < current_user.current_sign_in_at redirect_to destroy_user_session_path end end end
3)在需要判断用户登陆的controller中添加before_action :has_signed过滤器。
给rails系统加i18n,根据浏览器的头去识别用户语言。记录一下相关实现:
1. 添加i18n配置
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
config.i18n.available_locales = [:en, :cn]
config.i18n.default_locale = :cn
2. 获取语言设置
# app/controller/application_controller.rb before_action :set_locale # set local {{{ def set_locale I18n.locale = get_locale end # }}} # locale {{{ def get_locale locale = I18n.default_locale if params[:locale] locale = params[:locale].downcase elsif cookies[:locale] locale = cookies[:locale] elsif locale = get_accept_language locale end # set cookies if cookies[:locale] != locale cookies[:locale] = locale end locale end def get_accept_language begin first_lang = request.env['HTTP_ACCEPT_LANGUAGE'].split(';').first default_lang = first_lang[0,2].downcase if default_lang == 'zh' first_lang[3,2].downcase else default_lang end rescue nil end end # }}}
说明:locale的值优先从url地址读取,次之从cookie,最后从浏览器头判断。英文浏览器的头为Accept-Language:en-US,en;,取en即前两位。中文浏览器头为Accept-Language:zh-CN,zh;,取cn即3-5位,转为小写。
3.编辑语言文件
# config/locales/en.yml
en:
hello: "Hello world"
# config/locales/cn.yml
cn:
hello: "你好"
4. 在模板文件中应用
# app/views/home/index.html.erb
<p><%= t('hello'); %></p>
<ul>
<% I18n.available_locales.each.each do |locale| %>
<li><%= link_to locale, params.merge(locale: locale) %></li>
<% end %>
</ul>
说明:点击生成的链接可以手动切换语言,测试的时候方便一些。
devise默认已经有较好的ajax支持了,只需要简单的配置一下即可使用ajax登录/注册。
1. 修改config/application.rb加下以下配置,启用json输出。
# devise respond_to json config.to_prepare do DeviseController.respond_to :html, :json end
2. 修改form标签,添加data-remote, data-type,设置id
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :remote => true, html: {id: 'ajax_user_signin', data: {type: :json}}) do |f| %>
3. 设置ajax头
$.ajaxSetup({ beforeSend: function(xhr){ var token; token = $('meta[name="csrf-token"]').attr('content'); if (token) { xhr.setRequestHeader('X-CSRF-Token', token); } } });
4. js处理代码
$('#ajax_user_signin').on('ajax:complete', function(e, xhr, type){ if (type === 'success') { location.href = '/'; } else { try { alert(xhr.responseJSON.error); // this.reset(); } catch (e$) { e = e$; } } });
关于注册:
注册的方法和登录基本相同,给form_for添加data-remote、data-type和id等属性。需要注意的是注册返回的表单验证是多项错误,使用xhr.resonseJSON.errors获取错误集合。