博客迁徙记

长期的不更新导致更新最新的 Hexo 之后各种的不兼容,费了一番功夫迁移,难得有一番时间给这里拔拔草,休憩一番。然后再吼一嗓子:

博客复活了!

博客的历史文章通过几次的系统和脚本转换过,已经不堪入目。在统一 markdown 格式的时候还翻看了自己写的文字有些感触。复活记可能还要经历几次,不如就写一篇博客的迁徙记吧。

My Opera

2006 ~ 2008

记忆已经有些模糊了,依稀记得第一次真正开始写博客是在上大二的时候,当时作为一名苦逼学生党,在网络上到处搜刮免费资源,误打误撞的在 My Opera 安家、开始了博客的更新。起初无非就是些不痛不痒的牢骚、生活流水,哪个黑客杂志发布我投稿的文章了和一些网络上发现的各种新鲜的技术资源。尤其热衷于博客主题的修改。

My Opera 定位是一个社交服务平台,在那个年代应该是和 MySpace 等竞争类似网站,自身提供的功能可谓强大,不仅仅是博客,还开放相册,自创建维护的群组论坛和好友社交等主要功能。也有不少的中国人和国外的留学生(及华人)在上面聚集。通过群组的查找(类似豆瓣小组但比起强大)加入了几个华人圈,其中我比较活跃的主要是一个名叫板秀的群组。里面提供各种在当时看来非常酷炫漂亮的主题。My Opera 博客除了默认几套主题以外,还提供可自定义 CSS 样式表作为其重要”秘密武器”。也使得我为之着迷。从此也在网络开开启了第二个交际圈(第一个是非安全黑客论坛圈)。

大学学的科目都是非常基础的语言课程,汇编语言、C、VB/VF 之类的和 CSS + HTML 一比较来说前者的诱惑力明显不如后者。之后除了学业以外开始正式的接触 HTML/CSS/Javascript 为后面的 web 开发做好了前期的铺垫。这当时是后话了。

好久不长,My Opera 因是免费注册使用被一些国内的偏激份子占据后开始各种宣传转法轮和反国家等言论。正碰巧那个时候 GFW 初出茅庐,开始了域名封杀的策略。在期间 GFW 偶尔抽风也就开始慢慢的淡出 My Opera 转战到迅速火起来的 Twitter。

自 2007 年注册 Twitter 之后所有话唠的信息都开始转移。My Opera 只剩下一些网络技术资源和扯蛋疼的文字。最后终结到大学毕业之后停止更新。

最终随着 Opera 被 Android 和 iOS 等现代智能浏览器的出现逐渐没落,2014 年 3 月 3 日正式的关闭了 My Opera 服务。

还好 Archive.org 有索引保存到历史快照,也算是一种纪念:(可能需要翻墙)

WordPress

2007 ~ 2012.3

鉴于 My Opera 进行被 GFW 抽打,最终管制监禁之后,开始考虑更换新的博客平台,在使用 WordPress 之前也在国内寻找一个稳定的平台。新浪博客因为明星效应略有反感再加上网站广告横飞、没有什么玩头就按着可以自定义 CSS 的方式找了几个(如今都消失了)都感觉不太靠谱。有些想放弃,有次回家过年和挚友 @lopor 在网吧上网,聊到这事,他说他已经和几个 V2EX 朋友合租了一台 Dreamhost 虚拟主机(VPS 那个时候还处于买不起的状态),要不要自己搭建一个 WordPress,心里一边听他给我演示一边看他打开网站和 ssh 工具进行着不知道什么事情的操作。

说来也巧,在大学的时候和学校网络维护的老师混的比较熟悉,老师说看看 PHP 吧,语言很简单很容易就可以开发出来一个网站。而 WordPress 本来也是 PHP 开发的。接着就有了注册域名,让朋友把 WordPress 给我部署到虚拟空间,解析域名。一切起了就开始了第二阶段的博客记录,也是时间最长的一个阶段。

使用 WordPress 之后的两年里,工作写 Java/HTML/CSS/Javascript,晚上的时候就开始学习 PHP,最开始就是拿 WordPress 的插件练手。粗略的熟悉整体的通用函数和钩子,等运用的比较熟练了,找了一个轻量级的 Kohana 框架开始学习整体的架构和编程思想。

也是这一阶段开始了跨城市出走的经历,有 5 篇文章整理了去新疆乌鲁木齐出差之余游走在 7-5事件之后的一些所见所闻(当前从文字来看看不出什么端倪),里面的照片放在已经被墙的 Google Picasa 相册。再那的 11 天让我知道了 GFW 已经完善到可以瞬间做到一个内部的局域网(当时本地除了电话可以和外界沟通之外,短信和网络全部封闭)

WordPress 同样也是收到 Twitter 这类碎片化的微服务的冲击,文章数量开始逐年减少。内容上开始增加了养生(我这小小年级就开始了)和一些软件推荐和技巧性文章,偶尔还会发一些我开源代码的宣传。

最重要的是我开始了写年度计划和年终总结的文章,这将作为我人生的记录和回顾的最重要的证据。

这个期间的朋友大多数来自于 V2EX 社区和 Twitter 上面。还和部分的朋友一起参加过第一届的 Wordcamp 2008,朋友聚集了私下一起吐槽各种渣的地方。

WordPress 因简单方便,功能强大而流行也因太过于扩充里面的功能而没有很好的优化核心代码而遭人唾弃。@yichi 有保存但是的部分照片 Flickr 相册

使用期间也挂过 Google Ads 和阿里妈妈广告联盟试图赚些收入。收入明细甚微,到目前对自己的定义还都是技术,营销果然不是我的菜。

历史快照:https://web.archive.org/web/20100701151339/http://icyleaf.com/ 网站界面完整当时的仿照 Delicious.com (美味书签) 全站复制过来。

这里再说一个域名的小插曲,域名最开始还是 icyleaf.cn,国内为了推销 cn 域名起初大体都是 1 元第一年。用了一年之后发现之后的费用飙升到比 com 域名还贵,加上开了 Visa 信用卡立即切换过去。庆幸的是我的域名比较个性没有什么人抢注。

Chyrp

2012.4 ~ 2012.12

最新补充: 终于想起来为什么更换掉 Wordpress,是因为 Wordpress 有个版本连续有后门被捅菊花,我的博客连连中招。反复清理无数感染的文件还是被捅。实在没精力跟它周折。想想也是时候放弃这个庞然怪物了。

自己已经记不得这个系统了,因为使用的时间不算很长,根据 Archive.org 的历史快照显示于 2012 年 4 月切换过来。系统提供迁移脚本直接把 mysql 数据转为为自己支持的字段格式。Chyrp 号称是最轻量级的微博客系统,有些类似 Tumblr。转换的原因现在想想应该是 Twitter 大多时候也是被 GFW 的状态,想用微博客系统把自己的牢骚、文章和一些图片揉到一起使用。还有一个原因是有插件可以支持 markdown 编写文章。

这段时间是在新浪乐居从事移动开发和团队建设的事情,文章更多的增加了针对 Android 和 iOS 开发相关的技术类文章。

我是一个念旧的人,之前记录的文字总想着可以保留下来,从这里开始后面的任何的博客系统的迁徙都会把数据转换过去。懂技术也算是一个优势(笑)

历史快照:https://web.archive.org/web/20120414131120/http://icyleaf.com/

Pelican

2013.1 ~ 2013.12

这是一个短暂的瞬间,@CNBorn 建议把动态的博客内容静态化速度就上去了。但是有些功能就需要依托外部的服务,比如搜索、评论。当时网站也还在 ping 值在 300 - 400 之间 DreamHost 虚拟主机上面。GFW 在不断进化和完善,这个时候就开始用上了 IP 封杀,Dreamhost 的大部分 IP 也没能幸免。毫不犹豫的就改换了 Pelican 这套框架。

动态切换为静态之后最大的问题就在于后台没有了,所有信息都需要在配置文件里面来处理。文章也只能本地写后生成静态文件并发布。没有选择部署在 Github Page 还是因为它的速度有些不太稳定了。已经使用了虚拟主机配合自建 VPN 的 Linode 主机而不使用确实有些浪费资源。

Chyrp 切换过来的历史文章通过自己写的脚本迁移过来,多少有些格式的不兼容和转换问题,手查和纠正了部分。主题借鉴于 Squarespace 给别人订制的样式,自己使用部分配色用 Twitter Bootstrap 重新实现。

使用 Pelican 的另外一个因素是想学习 Python,之后的文章开始增加了 python 相关和 Linux 基础技术文章。学习上重点放在了日常脚本的使用以及 pip+django 的功能学习,配合提交部分代码到 OpenParty 网站。

顺带提一句,Dango 自带的模板引擎和 Jinja2 用着是最爽的事情,和 Ruby 的 Haml/Slim 是不一样的 G 点。

本人从小偏爱骑行,在北京漂泊的日子,也只能是户外登山扎营(装备可压缩),反复的搬家对于还要再带一个自行车和一堆配件来说有些艰巨,在更换一个相对比较大的租房一狠心入了美利达公爵 650。长时间的骑行和休闲骑车差别很明显。累!很累!尤其是和公路车一起骑更累!骑行不像是登山,后者只需要完成登山的过程享受一把自虐,骑行是全程的,从你报名一个活动开始,你要从家骑到集合点、骑到目的地、骑到腐败点、最后还要疲惫的骑回家,这个过程是值得回忆的。也就有了我第一次长距离骑行,献给了http://icyleaf.com/2013/05/25/riding-to-hulunbeier/

历史快照:https://web.archive.org/web/20130201044534/http://icyleaf.com/

Hexo

2013.12 ~ 至今

Hexo 由台湾人使用 Node.js 开发并开源,网站本身提供明细的文档和插件扩展。对比 Pelican 来说比较适合国人,附带实用的命令行工具。Pelican 在使用中总有些不太爽的地方,Hexo 就在哪个时候出现在了我的视野,弥补了我的需要,用的顺手的系统就不在乎什么语言开发的,更何况也是静态发布。

主题真的是太喜欢之前实现的那个就直接由迁移的过来,历史文章又经历了一次迁移,其实已经有些部分的内容有些损坏了。单纯从主题和内容来说,整个网站有些支离破粹。

这个阶段对我而言是幸福的一年,对我的博客来说是相当冷落的一年。从结识土豪妞到一起走上婚姻殿堂,我感到非常的幸福。这里确是一片静寂两年的时间文章数量一个手都能数过来(寥寥 5 篇文章)。爱情和工作两方面的因素,直接导致各种的事情急剧减少,包括不仅限于户外登山、骑行、看书、电影。话说也奇怪了文章不少了可以理解,怎么外出活动也居然少了呢 >.<

博客的复活是意料之外的事情,前几天惯例的吸收网上的信息,看到推荐一个 VPS,和 DigitalOcean 类似 SSD 硬盘、最低每月 5 刀的租金。速度方面很明显,尝试一番后发现最优情况 ping 值可以稳定在 60 ~ 100 左右,下载速度也能达到 1~2Mb/s。信用卡刷钱先试 3 个月再说。自然就想到把博客迁移过来。年底将至,还要有两年的总结需要完成,提前做好准备也是件好事。

从 Dropbox 找回博客系统和日志,重新本地部署环境。悲催,Hexo 不工作了。检查后发现升级到 3.0 版本,代码结构变化了的。文章的格式也做了调整。又有事情要忙活了。

写好升级兼容脚本,文章没问题了,主题有崩了。脑海里立即飘出来一个曾经看到过的网站用的是 Hexo 搭建的,主题还挺好看。打开 Chrome 页面历史搜索关键字,打开目标网站快捷键切换到页面底部 “Theme - NexT”,一切就顺理成章的平滑过来。还顺手试了一把 git subtree 代替费死牛力的 git submodule

数字统计

统计自使用自建博客系统开始计算,My Opera 作为一个自留地不再统计范围:

文章

历经 8 年之余的时间共计 90 篇:

  • 2007 年总文章数量 7 篇(有删减)
  • 2008 年总文章数量 14 篇(有删减)
  • 2009 年总文章数量 13 篇(有删减)
  • 2010 年总文章数量 7 篇(有删减)
  • 2011 年总文章数量 7 篇(有删减)
  • 2012 年总文章数量 23 篇
  • 2013 年总文章数量 14 篇
  • 2014 年总文章数量 4 篇
  • 2015 年总文章数量 1 篇(排除复活之后的文章)

标签

  • Mac: 12
  • iOS: 10
  • PHP: 9
  • Linux: 8
  • Git: 7
  • 年终总结: 7
  • 养生: 6
  • Kohana: 5
  • Ruby: 5
  • Shell: 5
  • 旅行: 5
  • Python: 5
  • App: 5
  • 游记: 5
  • 豆瓣: 4
  • Android: 4
  • XCode: 4
  • Virtualenv: 2
  • CentOS: 2
  • Google: 2
  • 魔豆: 2
  • CSS: 2
  • 卤煮: 1
  • Dash: 1
  • regex: 1
  • Flask: 1
  • Ubuntu: 1
  • 美食: 1
  • 家常菜: 1
  • Universal: 1
  • Xcode: 1
  • brew: 1
  • Hiking: 1
  • Dropbox: 1
  • apache: 1
  • Javascript: 1
  • Chrome: 1
  • Laptop: 1
  • eclipse: 1
  • VirtualBox: 1
  • Map: 1
  • Gem: 1
  • testflightapp: 1
  • Django: 1
  • umeng: 1
  • 手机: 1
  • 黑莓: 1
  • Vagrant: 1
  • Mockup: 1
  • 北京: 1
  • Rails: 1
  • Gitlab: 1
  • ActiveRecord: 1
  • Unicorn: 1
  • Nginx: 1
  • Book: 1
  • Riding: 1
  • 呼伦贝尔: 1
  • CocoaPods: 1
  • golang: 1
  • Sublime Text 2: 1
  • Database: 1
  • 摄影: 1
  • 校内: 1
  • 海内: 1
  • 开心网: 1
  • QuickLook: 1
  • Github: 1
  • 食材: 1
  • 沙漠: 1
  • Puma: 1

极速化 CocoaPods

Cocopods 本身是一个优秀的 iOS 开发的包管理工具,涵盖了 7k+ 的开源组件,包管理库是托管在 Github。
众所周知的原因它的速度日渐缓慢,有时会频繁报如下错误:

1
2
3
4
5
6
7
$ pod install

Cloning into '/path/to/ios/project/Pods/xxx'

error: RPC failed; result=52, HTTP code = 0

fatal: The remote end hung up unexpectedly

本文主要为解决该问题而诞生的,以下的加速方案不局限于目前已流传的优化方案,而是在此基础上彻底的加速

  • 使用淘宝 Ruby Gems 源(Cocoapods 使用 ruby 开发)
  • pod install 时不设置包的更新:参考文章
  • 使用国内 git 服务器镜像 Cocoapods Spec: 参考文章

如果你对 Cocoapods 有更深层次的理解,请参见:objc.io: Cocoapods under the hood 中文版本

今天早晨看到微博众多 iOS 开发者赞同转发《CocoaPods最佳实践探讨》一文,
针对 Pods 建议纳入版本控制也是无奈之举。之前公司项目中也是这样施行很长一段时间,不排除更新可能会造成很多无用信息”刷屏”,
偶尔还会因为版本冲突造成一些混乱状况需要处理。个人还是更倾向于精简原则,遵循官方的建议

大家都是技术人员,其实这些小问题难道因为 github 倒下就没有解决方案了吗?!看我如何撕破这层纸老虎:

技术概述

  • Cocopods v0.34.0+
  • Gitlab: 自建私有 git 服务器
  • gitlab-mirrors: 专用于 github 镜像至 gitlab 并保持定期更新
  • rake: ruby 的代码构建工具(不懂 ruby 的可以把它理解为命令聚合工具)

技术剖析

Cocoapods 自身支持私有仓库
恰好的是就在前不久发布的 0.34.0 版本支持 Podfile
添加多个的包源仓库,举个例子:

1
2
3
4
5
source 'https://github.com/artsy/Specs.git'
source 'https://github.com/CocoaPods/Specs.git'

pod 'AFNetworking'
pod 'Mantle'

这个特性其实是为了扩充官方 Spec 的同时可以更好的让开发者管理私有的公共组件,那我同样是从这里下手:

前提是自己以及搭建好 gitlab 服务器:官方教程 (Ubuntu) | 本人教程 (CentOS)

自力更生

首先我们需要创建一个自己的 Spec 仓库,目录结构如下:

1
2
3
4
5
6
.
├── CocoaPods-version.yml
├── Specs/
├── README.md
├── Rakefile
└── Gemfile

配置不做详细描述,这里比官方多了两个文件 RakefileGemfile 都是 rake 所需的文件,这个后面会讲到。
再者就是配置 gitlab-mirrors,教程很详细不再重复。

偷梁换柱

利用私有 Spec 仓库特性,可以把官方 Spec 目录下面的包按需或全部镜像过来,再次基础上把里面涉及 github 的地址替换成 gitlab 的地址

你没有看错,这是核心步骤,如果这步没有做那么和国内镜像的地址没有任何差别。核心代码如下:

Rakefile
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
require 'uri'
require 'fileutils'
require 'multi_json'
require 'net/ssh'

desc '镜像一个 github 包至 gitlab 仓库'
task :clone, [:name] do |t, p|
name = p[:name]
current_path = Dir.pwd

specs = Dir[File.join(File.expand_path('~'), '.cocoapods/repos/master/Specs/*')]

repo = specs.select { |s| File.basename(s) == name }.first

if repo
puts " * found repo, copy it here"
repo_store_path = File.join(current_path, 'Specs')
FileUtils.cp_r repo, repo_store_path

puts " * updating repo url"
Dir["#{repo_store_path}/#{name}/*"].each do |f|
pod_file = File.join(f, "#{name}.podspec.json")
json = File.read(pod_file)
data = MultiJson.load json

if data['source']['git']
puts " -> #{data['version']}: git"
orginal_repo_url = data['source']['git']
coverted_repo_name = URI.parse(orginal_repo_url).path[1..-1].gsub('/', '-').downcase
data['source']['git'] = "http://gitlab.dev/mirrors/#{coverted_repo_name}"

File.write(pod_file, JSON.pretty_generate(data))
else data['source']['http']
puts " -> #{data['version']}: http url, do you want speed up?"
else data['source']['svn']
puts " -> #{data['version']}: svn repo, do you want speed up?"
end
end
else
puts "Not find spec named: #{name}"
end
end

desc 'gitlab 服务器镜像 Cocoapod Spec'
task :mirror, [:repo] do |t, p|
host = '172..0.1'
user = 'icyleaf'
options = {:keys => '~/.ssh/keys/id_rsa.pub'}

puts "Connect gitlab server and mirror"
Net::SSH.start(host, user, options) do |ssh|
gitmirror_path = '/home/gitmirror/gitlab-mirrors'
cmd = "sudo -u gitmirror -H rake \"add[#{p[:repo]}]\""
stdout = ssh.exec!("echo 'cd #{gitmirror_path} && #{cmd}'")
puts stdout
ssh.loop
end
end
Gemfile
1
2
3
4
5
6
source "https://ruby.taobao.org"

gem 'rest_client'
gem 'multi_json'
gem 'rake'
gem 'net-ssh'

rake 里面有两个 task:

  • mirror: 镜像 iOS 开源组件
  • clone: 负责把官方 spec 指定包(开源组件的版本控制)替换 gitlab 地址并加入到私有包仓库

总结

通过工具总有办法可以改进和提升开发者的效率和解决各种的问题,希望本文可以给大家带来更多的灵感!

答疑解惑

F: 这套理论靠谱吗?

A: 目前我们团队已经采用并运行了很长一段时间,没有任何风险。最大的优势在于兼容官方的仓库,
就算无法链接自己的私有服务器,使用官方和国内镜像的都可以瞬间切换。

F: 如果没有服务器可以实现吗?

A: 醒醒吧孩子,就连单纯的镜像官方 Cocoapods Spec 还需要一个服务器执行定期同步脚本呢。

F: 国内 git 托管服务器能够支持吗?

A: 据我所知国内大部分 git 托管服务器的解决方案都是基于 gitlab 二次开发的,理论上可行,
上面提到的 gitlab-mirror 本身依赖于 gitlab 的 api 在镜像的同时自动新建仓库。如果有成功的欢迎反馈。

F: 我从你代码发现服务器同样调用了一个 rake 脚本,你没有开源!

A: 眼睛真够敏锐的,个人对 gitlab-mirror 再做镜像时做了一个约束,新建一个 Rakefile 文件放到你的 gitlab-mirror 项目根目录即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'uri'

desc "Adding repo to gitmirror"
task :add, [:repo] do |t, p|
repo = p[:repo]

begin
name = URI.parse(repo).path[1..-1].gsub('/', '-').gsub('.git', '')
if name
`./add_mirror.sh -f --git --project-name #{name} --mirror #{repo}`
end
rescue Error => e
puts 'not url'
end
end
F: 我还有问题!

A: 麻烦请留言,谢谢!

Puma 替换 Unicorn 跑 Gitlab

前篇介绍到如何在《CentoOS 上面安装 Gitlab》一文,gitlab 默认使用的是 unicorn 作为内部的 app server,再用 nginx 做代理转发。之前是在公司内部搭建了一个平台,用着还算可以。有打算在 Linode 购买的 VPS 上面,使用 unicorn 跑服务的时候 ruby 的进程居然占了 400-500M 左右,对于 Linode 刚刚免费升级之后才有 1G 内存的环境上,我还真有点放弃安装它的欲望。于是在想是否可以使用 puma 替换掉原先的。

puma

简单介绍下 puma,它是一个由 ruby 编写的转为 rack 设计的 app server,在性能和资源占有上却有极大的优势(下表数据来自官方)

1
2
3
4
PUMA - 78 Mb
RAINBOWS! (1X16) - 120 Mb
UNICORN - 1076 Mb
RAINBOWS! (16X32) - 1138 Mb

而且集成也非常的简单,若使用 rails 或者 sinatra(及 padrino)都已经支持,直接 gem install puma,然后跑默认的 rails/padrino server 会自动加载。

教程

Okay,经过一番查找,官方在收集的 repices 里面有关于 puma 的一些配置。他们也是收集的非官方资料,里面的资料只有借鉴意义,真正拿来用的时候各种问题,所以才有了本篇文字。

首先是关闭启动的 gitlab 服务

1
$ (sudo) service gitlab stop

关闭之后,添加 puma gem,打开 Gemfile

1
2
3
4
group :unicorn do
gem 'unicorn', '~> 4.6.3'
gem 'unicorn-worker-killer'
end

找到上面的这段 group 替换成:

1
gem 'puma'

再者修改 config.ru,把下面这段代码做下替换,删除 unicorn 的代码,加载 puma:

1
2
3
4
5
6
7
unless defined?(PhusionPassenger)
require 'unicorn'
# Unicorn self-process killer
require 'unicorn/worker_killer'
# Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, (200 * (1 << 20)), (250 * (1 << 20))
end

更新成

1
2
3
unless defined?(PhusionPassenger)
require 'puma'
end

替换完毕更新 gem

1
2
3
4
# mysql 数据库
bundle install --without development test postgres --path vendor/bundle --no-deployment
# postgres 数据库
bundle install --without development test mysql --path vendor/bundle --no-deployment

最后还有两处需要修改,添加 config/puma.rb(替代 config/unicorn.rb) 以及替换 /etc/init.d/gitlab 服务脚本代码。

config/puma.rb的代码在这里可以下载,无需做任何的修改。

/etc/init.d/gitlab 服务脚本:CentOS | Debian(Ubuntu)

服务脚本需要设置下执行权限: chmod +x /etc/init.d/gitlab

最后开启服务应该就完美了

1
$ (sudo) service gitlab start

我想用 Apache 怎么办?

嗯,我没尝试过,官方有提供收集的资料,自己查看下吧,记得要活学活用,直接套肯定会出问题的。

初识 Ruby Gem Guard

Guard 是一个很好的 Web 辅助开发工具,因为看了 tutsplus 网站的介绍视频:Guard is your best firend。这里把视频的东西提取出来自我消化:

1
$ gem install guard

它实际的工作就像名字那样,实时守卫这某些文件并做出对应的操作,本质上相当于一个有这个思想的禁卫兵,通过指派命令(安装扩展 gem)而负责守卫和执行,比如视频中提到的:

上面是概念的理解,实际上的流程是,设定一系列的规则,让他监控文件内容(修改时间)的动态,一旦发生变化则根据不同的命令执行不同的操作。

具体基本操作不再多少,官方 README 写的非常详细,或者通过上面视频也能熟悉。

guard-sass

监听 sass 文件并转换成 css 文件

guard-coffeescript

监听 coffeescript 文件并转换成 js 文件

guard-rspec

自动跑 rspec test

guard-livereload

配合浏览器的辅助工具,做到无需手动刷新页面即可看到 html/css/js 的修改变化,浏览器需要安装配套插件。

guard-zeus

自动接管 zeus

guard-puma

puma 是目前比较新的一个 web 服务器,这个扩展可以监听配置变化从而自动重启服务器。

官方列举的所有扩展:List of available Guards

Homebrew 隐藏命令

Homebrew 可谓是 Mac 开发者必备的工具之一,它承载在各种包管理的特性以及拥有一票热情高涨的社区提供强大支持。今天我就给大家解密一下 brew 内部的隐藏命令都有哪些。

服务管理

当我们安装了众多需要挂载的服务,需要重启或停止的时候,都特别希望 Mac 可以有个统一的命令可以管理服务的状态,比如 services 命令甚至说 /etc/init.d/ 也可以啊!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ brew services command [formula]

usage: [sudo] brew services [--help] <command> [<formula>]
Small wrapper around `launchctl` for supported formulae, commands available:

cleanup Get rid of stale services and unused plists
list List all services managed by `brew services`
restart Gracefully restart selected service
start Start selected service
stop Stop selected service

Options, sudo and paths:

sudo When run as root, operates on /Library/LaunchDaemons (run at boot!)
Run at boot: /Library/LaunchDaemons
Run at login: /Users/icyleaf/Library/LaunchAgents

这个是我要讲的第一个隐藏命令!太坑爹了,那么实用的命令居然没有包含在 brew --help 帮助里面!我们再也不用发愁记住 launchctl load/unload <path> 的坑爹命令了!

重启 Nigix 就那么简单:

1
$ brew services restart nginx

停止 Postgresql 服务

1
$ brew services stop mysql

查看系统通过 brew 安装的服务:

1
2
3
$ brew services list
postgresql started - /Users/icyleaf/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
nginx started - /Users/icyleaf/Library/LaunchAgents/homebrew.mxcl.nginx.plist

清除已卸载无用的启动配置文件:

1
2
3
$ brew services cleanup
Removing unused plist /Users/icyleaf/Library/LaunchAgents/homebrew.mxcl.mysql.plist
Removing unused plist /Users/icyleaf/Library/LaunchAgents/homebrew.mxcl.redis.plist

安装扩展

这个相信很多人都已经用到过了,安装他人扩展的 brew 服务。由于 brew 和包含的包源都是通过 github 来管理,人为的维护管理,除了自己的源还允许别人的源添加进来。类似与 Ubuntuppa。好处在于只有我安装规定的方式把包丢到 github 上面就可以用了!

1
$ brew tap <gihhub_user/repo>

这个命令并没有包含任何的帮助说明,其实它只接受上面的这个参数。

举例说明一下,Mac OS 比较歧视 PHP ,所以每次系统更新都会把常用的开发包(Ruby、Python 等)也顺带着更新到最新版本。(吐槽:Java 都已经被抛弃不再默认安装了),而 brew 居然也不包含 PHP 的包,那怎么办呢?

1
$ brew tap josegonzalez/php

命令完成之后,执行(当前最新是 php 5.5 版本,请根据需要替换)

1
$ brew install php55

当我们没有传递任何参数,默认显示已经通过 tap 安装了哪些扩展,为什么我说是通过 tap 呢,因为 brew 其实除了这些自身也用了一些其他扩展,通过下面命令显示:

1
$ brew ls-taps

Web 化显示可用包和已安装工具

对于习惯命令行的人这个用途不大,就顺带一提而已,这个命令依赖 sinatra,大家通过 gem 安装即可。

1
$ brew server

如果你用 puma 可能报一个 [BUG] Segmentation fault 错误,那是因为你通过 rvm 或 renv 安装了跟高级的版本,而系统却用的 1.8.7 造成了版本差,请切换为系统依赖后再重试(你可能需要重新安装 sinata):

1
2
$ rvm use system
$ /usr/bin/gem install sinatra

彩蛋

1
$ brew beer

更多隐藏命令

一次性没太多精力完解读所有隐藏命令,这个艰巨的任务就交给大家了:

1
$ brew commands

其实这些命令可以在官方源代码看到。

Linux 101 系列:ssh OpenSSH 客户端工具

使用 *nix 服务器肯定会用到 ssh,它是一个链接到远程服务器终端的工具。

基本常识

基本组成部分

1
2
3
4
5
6
7
$ man ssh
ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
[-D [bind_address:]port] [-e escape_char] [-F configfile] [-I pkcs11]
[-i identity_file] [-L [bind_address:]port:host:hostport]
[-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
[-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port]
[-w local_tun[:remote_tun]] [user@]hostname [command]

有没有被帮助手册的参数吓到,那我们精简一下:

ssh [user@]hostname [-p port]

登录到远程服务器和登录本地电脑系统没什么大的区别,都需要用户名,密码,除此之外我们还需要知道远程服务器的地址(IP 地址或域名均可)及允许的端口(默认 22 端口)。

1
$ ssh root@10.10.10.10 -p 22

有些时候大家发现一些教程没有提到用户,实际上 ssh 很聪明,默认会使用当前系统的用户名:

1
(icyleaf) $ ssh 10.10.10.10

这个就等同于

1
$ ssh icyleaf@10.10.10.10

有些服务器可能为了安全期间修改了默认的端口,比如 2020:

1
$ ssh icyleaf@10.10.10.10 -p 2020

高级参数

Socket 代理

呐,你可能因为 GFW 的威力,尝试使用 ssh 的 socket 代理,实际上就是用到的 -D 参数:

1
2
3
4
5
6
7
8
9
-D [bind_address:]port
Specifies a local ``dynamic application-level port forwarding.
This works by allocating a socket to listen to port on the local
side, optionally bound to the specified bind_address. Whenever a
connection is made to this port, the connection is forwarded over
the secure channel, and the application protocol is then used to
determine where to connect to from the remote machine. Currently
the SOCKS4 and SOCKS5 protocols are supported, and ssh will act
as a SOCKS server. Only root can forward privileged ports.

我们来简单画下它的工作流程:

1
2
3
4
5
6
7
|----client----|                    |-----server----|
| | | |
| local port<| <incoming | |
| ssh port<>| <--------------> |<> ssh port |
| forwarding>| >outgoing connection

<<<------------local network-----------

它首先需要登录到远程服务器,并把本地的请求全部转发到服务器指定的端口上,然后通过由服务器再去请求。例如我们设置 8624 端口:

1
$ ssh -D8624 icyleaf@10.10.10.10 -p 22

这样保持这个连接,我们在设置浏览器或系统的 socket4/5 代理就能达到翻墙的目的。当前这个最大的前提是你的服务器在不手 GFW 的控制下(任意海外未被 GFW 服务器)

跳板代理

或许曾经你在看某本黑客杂志或电影出现过这样的片段,黑客使用肉鸡跳板不断的增加难度避开警察的追踪,实际上我们通过 ssh 也能非常简单又很酷的实现。

1
2
3
4
5
6
7
8
-L [bind_address:]port:host:hostport
Specifies that the given port on the local (client) host is to be
forwarded to the given host and port on the remote side. This
works by allocating a socket to listen to port on the local side,
optionally bound to the specified bind_address. Whenever a con-
nection is made to this port, the connection is forwarded over
the secure channel, and a connection is made to host port
hostport from the remote machine.

同样给出工作流程:

1
2
3
4
5
6
7
|----client----|                    |-----server----|                 |-----host-----|
| | | | | |
| local port<| <incoming | | | |
| ssh port<>| <--------------> |<> ssh port | | |
| forwarding>| -------------> |>host port |

<<<------------local network------------------->>>

实际上我只需要操作 -L [bind_address:]port:host:hostport 即可。这里假设我们想登录的目标服务器 target(10.10.10.10),而我们希望在肉鸡 chicken(20.20.20.20) 上做成跳板:

1
$ ssh -L2020:20.20.20.20:22 10.10.10.10

简化配置

反复的输入这些繁琐的参数,甚是苦恼,能不能通过一种起个别名就能把上面的参数全部自动设置好呢?!没问题!

创建 ~/.ssh/config 文件:

1
2
3
4
Host linode
HostName 10.10.10.10
Port 22
User icyleaf

这个就是基本组成部分。如果你想配置更多,下面是完整的参数:

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
AddressFamily
BatchMode
BindAddress
ChallengeResponseAuthentication

CheckHostIP
Cipher
Ciphers
ClearAllForwardings
Compression
CompressionLevel
ConnectionAttempts
ConnectTimeout
ControlMaster
ControlPath
ControlPersist
DynamicForward
EscapeChar
ExitOnForwardFailure
ForwardAgent
ForwardX11
ForwardX11Timeout
ForwardX11Trusted
GatewayPorts
GlobalKnownHostsFile
GSSAPIAuthentication
GSSAPIDelegateCredentials
HashKnownHosts
Host
HostbasedAuthentication
HostKeyAlgorithms
HostKeyAlias
HostName
IdentityFile
IdentitiesOnly
IPQoS
KbdInteractiveAuthentication
KbdInteractiveDevices
KexAlgorithms
LocalCommand
LocalForward
LogLevel
MACs
NoHostAuthenticationForLocalhost
NumberOfPasswordPrompts
PasswordAuthentication
PermitLocalCommand
PKCS11Provider
Port
PreferredAuthentications
Protocol
ProxyCommand
PubkeyAuthentication
RekeyLimit
RemoteForward
RequestTTY
RhostsRSAAuthentication
RSAAuthentication
SendEnv
ServerAliveInterval
ServerAliveCountMax
StrictHostKeyChecking
TCPKeepAlive

Tunnel
TunnelDevice
UsePrivilegedPort
User
UserKnownHostsFile
VerifyHostKeyDNS
VisualHostKey
XAuthLocation

配置文件

刚才讲到的 ~/.ssh/config 是配置 ssh 服务器的文件,其实除了这些还有好多,比如大家可能会经常见到的:

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
~/.ssh/config
This is the per-user configuration file. The file format and
configuration options are described in ssh_config(5). Because of
the potential for abuse, this file must have strict permissions:
read/write for the user, and not accessible by others.

~/.ssh/authorized_keys
Lists the public keys (DSA/ECDSA/RSA) that can be used for log-
ging in as this user. The format of this file is described in
the sshd(8) manual page. This file is not highly sensitive, but
the recommended permissions are read/write for the user, and not
accessible by others.

~/.ssh/identity
~/.ssh/id_dsa
~/.ssh/id_ecdsa
~/.ssh/id_rsa
Contains the private key for authentication. These files contain
sensitive data and should be readable by the user but not acces-
sible by others (read/write/execute). ssh will simply ignore a
private key file if it is accessible by others. It is possible
to specify a passphrase when generating the key which will be
used to encrypt the sensitive part of this file using 3DES.

~/.ssh/identity.pub
~/.ssh/id_dsa.pub
~/.ssh/id_ecdsa.pub
~/.ssh/id_rsa.pub
Contains the public key for authentication. These files are not
sensitive and can (but need not) be readable by anyone.

~/.ssh/known_hosts
Contains a list of host keys for all hosts the user has logged
into that are not already in the systemwide list of known host
keys. See sshd(8) for further details of the format of this
file.

更多文件:

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
~/.rhosts
This file is used for host-based authentication (see above). On
some machines this file may need to be world-readable if the
user's home directory is on an NFS partition, because sshd(8)
reads it as root. Additionally, this file must be owned by the
user, and must not have write permissions for anyone else. The
recommended permission for most machines is read/write for the
user, and not accessible by others.

~/.shosts
This file is used in exactly the same way as .rhosts, but allows
host-based authentication without permitting login with
rlogin/rsh.

~/.ssh/
This directory is the default location for all user-specific con-
figuration and authentication information. There is no general
requirement to keep the entire contents of this directory secret,
but the recommended permissions are read/write/execute for the
user, and not accessible by others.

~/.ssh/environment
Contains additional definitions for environment variables; see
ENVIRONMENT, above.

~/.ssh/rc
Commands in this file are executed by ssh when the user logs in,
just before the user's shell (or command) is started. See the
sshd(8) manual page for more information.

/etc/hosts.equiv
This file is for host-based authentication (see above). It
should only be writable by root.

/etc/shosts.equiv
This file is used in exactly the same way as hosts.equiv, but
allows host-based authentication without permitting login with
rlogin/rsh.

/etc/ssh/ssh_config
Systemwide configuration file. The file format and configuration
options are described in ssh_config(5).

/etc/ssh/ssh_host_key
/etc/ssh/ssh_host_dsa_key
/etc/ssh/ssh_host_ecdsa_key
/etc/ssh/ssh_host_rsa_key
These files contain the private parts of the host keys and are
used for host-based authentication. If protocol version 1 is
used, ssh must be setuid root, since the host key is readable
only by root. For protocol version 2, ssh uses ssh-keysign(8) to
access the host keys, eliminating the requirement that ssh be
setuid root when host-based authentication is used. By default
ssh is not setuid root.

/etc/ssh/ssh_known_hosts
Systemwide list of known host keys. This file should be prepared
by the system administrator to contain the public host keys of
all machines in the organization. It should be world-readable.
See sshd(8) for further details of the format of this file.

/etc/ssh/sshrc
Commands in this file are executed by ssh when the user logs in,
just before the user's shell (or command) is started. See the
sshd(8) manual page for more information.

资料参考:

  1. [Linux] man ssh
  2. Advanced SSH usage

2013 年终总结

人生能够走过几次 1314 新旧年的交替,都有些忘记过去的一年做了些什么事情,翻看日历和一些零碎的笔记在努力把之前的事情慢慢回忆起来。

工作

工作算得上又失落也有少许沉淀。

首先结束了人生第二家干了大约三年半的公司。当职位慢慢爬升,周围的环境已不再是曾经那么单纯程序员思维,在去年年终总结也提到过爬升所带来的宝贵经验,但今年却尝到了不一样的教训:

  • 产品和技术是为了市场(目标用户)服务
  • 团队不能够急剧扩张
  • 这不仅仅是一个人的『战役』
  • 提防小人的冷枪

其实前者还好,总有办法弥补,但面对最后一条,我选择了无奈的离开…

2007 年 12 月来北京之后,几乎没有怎么休息过,人生总是在岗位上奋斗,本想着这次离职可以在出走玩玩,时节又逢夏末秋初,正好是看雪山的季节。事情往往事与愿违,离职三天后,我入职了第三家公司,一朋友急缺人员,我选择了去帮他救火…

技术

在做移动互联网的这两年中,个人大概分析了下市场情况,挣钱的永远是那么两类,游戏和广告。而国内大多数公司都是为了抢占一个端口而做的业务相关的应用。除了不差钱的和玩票性质的以外,大多应用都不知道在干些什么。

『无论是 PC 还是 Mobile,展现给用户的最终还是数据。』基于这条理论,我决定尝试挖掘数据和分析数据,试图在数据中得到一些答案。《精益创业》却给了相反的实践方案。个人打算结合两者的优势积累一些资料。

因为数据的采集,建立数据仓库和分析,我并没有采用 Python,而是使用了 Ruby。或许语法糖太多了,可以让我更快的解决一些实际问题。

虽然在移动最热的时候,暂停了一下。我想重新再开始那里才是真正的战场。

生活

户外

攒了三年多的假,竟然有了 20 多天假期,一南一北各去了一个地方:

1. 风云莫测的贡嘎转山

五一假期左右请了几天假连成 11 天,花了 8 天贡嘎转山,其余天在成都市内转了转整理成册。

浏览照片:

2. 骑行呼伦贝尔之额尔古纳河右岸

2013 年的端午节,受 loddit 邀请组成了 4 人小队前往呼伦贝尔,踏上了第一次为期 4.5 天的骑行活动。我还特意做了一个路线征集计划书

浏览照片:骑行呼伦贝尔

3. 两次山西五台山朝圣(反穿/正穿)

去年冬季反穿过一次五台山,大雪纷飞。一直都知道大五台是一个四季变化的地方,特别想体验一下不同季节的风景。在 9 月初再次踏上五塔山的土地。幸福的是认识了土豪妞。

第二次去五台山是还想和她一起去体验雪景,结果天气晴朗,没有一点飘雪。那个时候已经是 11 月,哪里有像凛冬将至的赶脚,摔!

浏览照片:朝圣五台山

阅读

2013豆瓣阅读图表

上半年总把玩着 iPad,积攒的实体书虽多,看完的较少,更别提存储量更大的电子书了;下半年开始有读书量得益于从日本 amazon 海淘了一个 kindle paperwhite,加上豆瓣阅读和多看的给力,也购买了不少在价格上更有优势的电子书。

推荐书籍:

  • 技术:《Tmux – Productive Mouse-Free Development》
  • 创业:《精益创业》
  • 历史:《极简欧洲史》
  • 小说:《三国演义》《冰与火之歌》《星际争霸系列》

电影

2013豆瓣观影图表

囧,居然看了那么多电影,我自己都有些惊讶…

推荐电影:

  • 纪录片:《独立游戏大电影》《工业光魔 创造不可能》
  • 动画长篇:《疯狂原始人》
  • 美剧:《纸牌屋》《绝命毒师》
  • IMAX:《地心引力》

家庭

爷爷永远的辞世,永远缅怀。

老院子自爷爷过世之后开始没了灵气,就连每年硕果累累的枣树也聋了,没有结一个枣,又逢拆迁计划,于是从不崇拜买房主义的我在家给父母合资买了套房子,在家也忙活了十多天参与房子的装修,劳神劳力费钱,唉…从此荷包又瘪了 :(


新的一年我想尝试写写日记,就像《和时间做朋友》里面提到的,我想对于时间和事情的归档更加的精确,未来还有更多的事情要去做!

学习 Ansible + Vagrant

Ansible is a radically simple IT orchestration engine that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.

简单来说 Ansible 是一个极简化的应用和系统部署工具,类似 PuppetChefSaltStack。由于默认使用 ssh 管理服务器(集群),配置文件采用 yaml 而不是某一种特定语言制定。方便至极。

很多人说 salt 也很用的,为什么不考虑呢,我个人觉得,ansible 的配置文件编写起来比较方便,不需要使用 jinja2 模板引擎适合非 python 用户管理。而且我也较同意 “Ansible and Salt: A detailed comparison“ 文章的评测。

由于个人之前没用过任何其他工具,至于你想知道上面哪些之间有什么区别的话,参见此文:Review: Puppet vs. Chef vs. Ansible vs. Salt 或 “开始使用配置和发布管理“ 一文中也有提到其特性。

安装

大家移步 LinuxToy 的 Ansible 快速上手 一文,以及 使用Vagrant練習環境佈署 作为学习铺垫,我就不再多写。这里我想重点介绍下 Ansible + Vagrant 配合使用技巧。

其实上面 “使用Vagrant練習環境佈署” 提到的配置文件是 Vagrantfile config version = 1 时候的,当前 vagrant 版本是 1.4.1, Vagrantfile config version = 2,因此配置的部分已经有所变动。大家需要做下更新。

如果你使用的是 Vagrant 1.4.0+,工具已经完全集成了上述的 DevOps 工具(甚至 Docker,另外一神器,后续看看能否给个介绍)。默认配置文件只包含了 Puppet 和 Chef,大家需要看官方文档

如果你是 Mac OS 用户,可以通过 brewbrew-cask 命令安装:

1
2
3
$ brew update
$ brew install ansible
$ brew cask install vagrant

创建 Vagrant 实例

首先创建学习目录(~/src/learn_ansible)和一个实例,采用 CentOS 6.5 x64 系统:

1
$ vagrant init centos65 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box

下载完毕之后,编辑 Vagrantfile 添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
config.vm.network :private_network, ip: "33.33.33.10"
config.vm.provision :ansible do |ansible|

ansible.inventory_path = "ansbile/hosts"
ansible.playbook = "ansbile/playbook.yml"

# 默认使用 sudo 权限
ansible.sudo = true
# 开启调试信息模式
ansible.verbose = 'vvv'
end

并在学习目录创建 ansible 目录以及下面两个文件,结构如下:

1
2
3
4
5
6
7
8
9
$ tree
.
├── Vagrantfile
└── ansible
├── hosts
└── playbook.yml
1 directory, 3 files

`hosts` 文件内容,ip 地址和上面 `Vagrantfile` 设置的一致:

[webserver]
33.33.33.10

1
2

`playbook.yml` 文件内容:


ansbile 的配置文件,这里指定只作用于 webserver 服务器

使用 vagrant 的 sudu 权限执行任务

  • hosts: webserver
    user: vagrant
    sudo: yes
    tasks:

    任务只有一个,就是安装 nginx

    • name: Install Nginx
      yum: name=nginx state=present
      1
      2
      3
      4
      5
      6
      7


      # 连接 & 部署

      使用 Vagrant 的好处在于,它集成了这些工具,并通过 `vagrant provision` 这个命令就能连接服务器并部署。这里我想让大家学习到如何通过传统 ssh 链接 vagrant 虚拟机的方法:

      从上面的配置文件我们得知,服务器的 ip 是 `33.33.33.10` 而且默认登录到虚拟机上的用户是 vagrant(密码也是用户名),链接端口是 22。我们先拷贝 ssh public key 到服务器上:

$ ssh-copy-id vagrant@33.33.33.10

1
2

完成之后,我们就可以通过下面命令测试是否配置成功:

$ ansible -u vagrant -i ansible/hosts all -m ping

webserver | success >> {
“changed”: false,
“ping”: “pong”
}
```

返回 ping: pong 即为连接成功并可以进行部署。若你之前执行了 vagrant provision 就会自动执行 playbook.yml 的内容。

今天初探就到此结束,希望通过本篇文字大家对它有个大概的了解。

ActiveRecord 使用秘笈

ActiveRecord 是 Rails 内置的 ORM 框架,大多数人学习 Ruby 都是从 rails 开始,接触的也是这个 ORM,因此就有了这个使用秘笈。

支持 rake db:xxx 命令

在非 rails 项目怎么让 rake 支持 db:xxx 命令呢?把如下代码放到 Rakefile 中:

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
namespace :db do
require 'activerecord'
require 'yaml'

desc "加载项目表数据到数据库"
task :init => :dbenv do
file = "db/schema.rb"
load(file) # 参考 rails 文件结构
end

desc "创建数据库初始化数据"
task :seed => :dbenv do
seed_file = File.join(File.dirname(__FILE__), 'db', 'seeds.rb')
load(seed_file) if File.exist?(seed_file)
end

desc "合并 db/migrate 目录下的数据库文件"
task :migrate => :dbenv do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end

desc '设定 STEP=n 回滚之前版本的数据库结构'
task :rollback => :dbenv do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.rollback('db/migrate/', step)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end

task :dbenv do
dbname = ENV['db'] || 'development'
$dbconfig = YAML::load('db/database.yml')
ActiveRecord::Base.establish_connection($dbconfig[dbname])
end

namespace :schema do
desc "把数据库结构写入 db/schema.rb 文件"
task :dump => :dbenv do
require 'active_record/schema_dumper'
File.open(ENV['SCHEMA'] || "db/schema.rb", "w") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
end
end
end

初始化数据库结构

1
$ rake db:init

支持 SQL Server

只针对 *nix 系统:

  1. 安装 freetds
1
2
* Mac OS: `brew install freetds`
* CentOS: `yum install -y freetds`
  1. gem install tiny_tds
  2. gem install activerecord-sqlserver-adapter

引用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require 'tiny_tds'
require 'activerecord-sqlserver-adapter'
require 'active_record'

ActiveRecord::Base.establish_connection({
:adapter => 'sqlserver'
:host => '10.10.10.10',
:username => 'sa',
:password => 'p@ssword',
:database => 'development',
:timeout => 10,
:port => 1433,
})

class Users < ActiveRecord::Base
self.table_name = 'User'
default_scope { lock('WITH (NOLOCK)') }
end

多数据库支持

创建 config/database.yml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
development:
adapter: mysql2
host: localhost
username: root
password:
database: development
timeout: 10
port: 3306
test:
adapter: mysql2
host: 10.10.10.10
username: root
password: p@ssword
database: test
timeout: 10
port: 1433
production:
adapter: mysql2
host: 33.33.33.33
username: root
password: p@ssword
database: production

创建 lib/model.rb 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
$dbconfig = YAML::load(File::open('config/database.yml'))

class User < ActiveRecord::Base
establish_connection $dbconfig['development']
end

class Post < ActiveRecord::Base
establish_connection $dbconfig['test']
end

class Tag < ActiveRecord::Base
establish_connection $dbconfig['production']
end

动态创建表名

假若有个需求需要按照每月分表(当然也可以安装业务分表什么的),我们可以通过下面方式调用:

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
# 插入 post,如果表不存在则创建后插入
post = Post.date('201312').get_or_create_table.create(
title:'test',
content:'body'
)

# Model 实现代码
class Post < ActiveRecord::Base
@date = Time.now.strftime("%Y%02m")

def self.date(date)
@date = date
end

def self.get_or_create_table(params={})
self.date(params[:date]) if params[:date]
self.create_table(params) if !self.exists?
end

def self.create_table(params={})
self.date(params[:date]) if params[:date]
table_name = self.table_name
ActiveRecord::Schema.define do
create_table table_name do |table|
table.column :title, :string
table.column :content, :text
table.column :created_at, :datetime
table.column :updated_at, :datetime
end
end

return self
end

def self.table_exists?
# 如果你设置了多数据库请取消下行注解并更改配置名(参考上个技巧)
# ActiveRecord::Base.establish_connection($dbconfig['development'])

ActiveRecord::Base.connection.tables.include?(self.table_name)
end

def table_name
"#{@date_users}"
end
end