10 months ago

最近要准备开始做一件事情:需要从头搭建,运营并管理一个关于电子货币交易所的开源技术社区。这个开源社区包括在线论坛和 Github 上的相关开源项目,论坛用于讨论电子货币交易所的技术话题,以及用开源的方式管理和推动电子货币交易所向前发展。

关于论坛的选型和搭建,因为我一直管理 RubyChina 社区,且 RubyChina 是由华顺和社区的热心网友共同维护的一个开源项目,主要功能包括后台都围绕服务 RubyChina 社区为主,是一个比较典型的 Rails 应用,所以我一开始就考虑基于 RailsChina 来构建这个社区。但是考虑到将来这个社区可能要支持更多的语言,更通用,更好的用户体验以及背后有一个开发团队长期支持,于是 Discourse 成了一个更好的选择,36Kr 有一篇帖子 Discourse 的介绍——打造“下一代的论坛”,Discourse要做论坛界的WordPress

logo.png

Discourse 有一份如何在 Ubuntu Server 上的官方安装文档,我同时参考了这里这里,发现这两份中文文档有些步骤过时了,或者有些步骤可能不是最优方案,或者不求甚解,比如不应该直接用管理员用户来部署应用等问题。加上我自己日常部署应有的一些习惯,所以特地写下来自己如何在 Linode 上搭建论坛的详细步骤,以备将来作为参考。

如果是自己随便玩玩,开个虚拟机或者廉价的小内存 VPS 随便玩玩的话倒也无所谓,但是如果要真的拿来用的话,Discourse 推荐跑两个 Thin (NUM_WEBS=2) 服务进程的话,至少需要:

  • 2G 内存
  • 2G 交换分区
  • 双核处理器

如果要开更多的 Thin 服务进程,则可以处理更多的并发请求,当然内存和处理器都要加倍,注册 Linode 账号,选择数据中心,创建并启动基于 Ubuntu 12.04 TLS 的主机,然后以 root 用户的过程略过,具体可以看 Linode 的 Getting Started 文档。

Linode VPS 的基本设置

首先是简化登录过程,通过配置 SSH Public key 到 VPS 上,这样下次登录 VPS 将不再需要输入密码,在 Mac 下可以简单的通过 ssh-copy-id 搞定。

brew install ssh-copy-id
ssh-copy-id root@ip_address_of_linode_vps

上面的步骤会提示输入密码,提升成功后,编辑 ~/.ssh/config 并追加下面内容到文件底部,保存退出,然后就可以直接用 ssh linode_root 命令以管理员身份登录服务器了。

Host linode_root
  Hostname ip_address_of_linode_vps
  User root
  IdentityFile ~/.ssh/id_rsa
  TCPKeepAlive yes

因为 Linode 上的系统都是预制的,不包含 hostname ,所以登录系统后第一步就是设置 hostname,文档上的例子是用 plato 作为 hostname,你可以选择任何符合 FQDN 规范的主机名。

echo "plato" > /etc/hostname
hostname -F /etc/hostname

更新了 hostname 之后,还需要更新 /etc/hosts, 只需要添加一行,将主机的 ip_address 和 hostname 对应即可。

127.0.0.1       localhost
127.0.1.1       ubuntu
ip_address      plato

设置时区,Timezone 如果要选择中国的话,选择 Asia -> Shanghai 就可以了。

dpkg-reconfigure tzdata

更新系统安装包,这是熟悉 Debian/Ubuntu 的人最熟悉不过的命令了

apt-get update
apt-get upgrade

添加一个 discourse 用户,然后把这个用户添加到 sudo 管理员组,这样可以之行 sudo 命令。还可以按照上面的步骤,通过修改 ~/.ssh/config 的方式让 discourse 用户也可以免密码 ssh 登录。

adduser discourse
usermod -a -G sudo discourse

完成用户添加后,就没有管理员什么事情了,登出,然后以 discourse 用户登录,下面的操作全部通过 discourse 用户完成。

安装 PostgreSQL 数据库

Discourse 的官方文档以安装 package group 开始,需要安装三个 package group:

  • sudo tasksel install openssh-server
  • sudo tasksel install mail-server
  • sudo tasksel install postgresql-server

其实 Linode 上已经预先安装好了 openssh-server,所以第一个不用安装。关于第二个 mail-server 我认为也不要安装!因为千万不要自己折腾邮件服务器,这个东西其实极为复杂,自建一个玩票可以,但是如果真的在生产环境的话,还是用第三方专业服务比较好。比如 mailgun 或者 postmark,如果专注国内用户的话,我推搜狐的 SendCloud

Discourse 使用 Postgres 数据库,所以这里只需要安装最后一个 postgresql-server。安装完成后需要创建一个数据库用户,并且可以给这个用户设置一个密码。

sudo -u postgres createuser -s discourse
sudo -u postgres psql -c "alter user discourse password 'your_password';"

安装 Redis

Redis 是一个基于内存的 key-value 数据库,Discourse 使用 Redis 作缓存以及后台任务队列服务。Discourse 推荐使用最新版本的 Redis 替换掉 Ubuntu 中自带的老旧版本,安装需要用到 apt-add-repository,而这个是由 python-software-properties 提供的,具体安装如下:

sudo apt-get install python-software-properties # 如果 Ubuntu 版本 >= 12.10 则安装 software-properties-common

sudo apt-add-repository -y ppa:rwky/redis
sudo apt-get update
sudo apt-get install redis-server

安装 Nginx

Discourse 使用 Nginx 作为前端代理服务器,包括提供静态文件的服务以及缓存优化等。Discourse 推荐用最新版本的 nginx,替换掉 Ubuntu 中自带的老旧版本,方法如下:

# 移除现有的安装包

sudo apt-get remove '^nginx.*$'

# 添加 nginx 安装源

cat <<'EOF' | sudo tee -a /etc/apt/sources.list

deb http://nginx.org/packages/ubuntu/ precise nginx
deb-src http://nginx.org/packages/ubuntu/ precise nginx
EOF

# 添加 nginx key

curl http://nginx.org/keys/nginx_signing.key | sudo apt-key add -

# 安装 nginx

sudo apt-get update && sudo apt-get -y install nginx

安装 Ruby

Discourse 是一个用 Ruby 语言写的 Rails 应用,需要用 Ruby 语音来执行。这里通过 RVM 来安装 Ruby。RVM 是一个老牌的 Ruby 版本管理器,用 RVM 安装 Ruby 和同时管理 Ruby 多个版本非常方便。安装过程中,rvm 会自动安装编译 Ruby 需要的依赖包。其实 Discourse 团队非常重视性能,升级到支持 Ruby 2.1.0 已经在日程上了,很可能已经可以用最新版本的 Ruby 来跑应用了。

curl -s -S -L https://get.rvm.io | bash
source ~/.bash_profile
rvm install 2.0.0
rvm use 2.0.0 --default

安装完成后检查一下当前 Ruby 的版本,能正常看到 Ruby 的版本号就说明 Ruby 安装没有问题。

ruby -v

安装其他依赖包

sudo apt-get install git imagemagick libpq-dev

安装 Discourse

从 Github 上 clone Discourse 的源码,然后安装 Discourse 所以来的 gems。

mkdir ~/www
cd ~/www
git clone git://github.com/discourse/discourse.git
cd discourse
bundle install --deployment --without test

配置 Discourse

首先需要准备好两个配置文件 discourse.confdiscourse.pill, discourse.conf 基于 quickstart 配置模板定制。

cp config/discourse_quickstart.conf config/discourse.conf
cp config/discourse.pill.sample config/discourse.pill
配置 config/discourse.conf
  • db_name 数据库名
  • hostname 你从外部访问论坛的主机名
  • smtp 服务器相关的配置,请以第三方 SMTP 邮件服务器配置信息为主
  • db_* 跟数据库相关的配置,包括数据库用户名,密码,数据主机等信息
  • redis 如果 VPS 上只跑 Discourse,没有其他应用使用 redis 的话,这部分不需要改动
配置 config/discourse.pill

Discourse 使用 Bluepill 来管理 Rails 的进程。

  • 默认应用名称是 "discourse",可以根据需要修改
  • 如果用管理员运行 bluepill 的话,需要注释掉普通用户执行对应的配置信息,具体可以查看文件内的注释内容

继续安装并配置 bluepill

gem install bluepill
echo 'alias bluepill="NOEXEC_DISABLE=1 bluepill --no-privileged -c ~/.bluepill"' >> ~/.bash_aliases
rvm wrapper $(rvm current) bootup bluepill
rvm wrapper $(rvm current) bootup bundle
初始化数据库
createdb your_db_name
RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ENV=production bundle exec rake db:migrate
编译所有的静态文件

Rails 的所有静态文件 (static assets) 是需要预先编译的,编译命令。

RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ENV=production bundle exec rake assets:precompile
配置 Nginx

首先复制一份配置文件到 nginx 的配置文件目录

sudo cp ~/www/discourse/config/nginx.sample.conf /etc/nginx/conf.d/discourse.conf

然后修改 /etc/nginx/nginx.conf 文件,在 http section 中添加 server_names_hash_bucket_size 64;

如果 VPS 上的 Discourse 是一个独立应用,则关闭掉 Nginx 的 default 网站。

sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.disabled

修改 /etc/nginx/conf.d/discourse.conf,包括

  • 配置 server_name
  • 修改 socket count 跟 Thin 服务器的进程数 (NUM_WEB) 一致
  • 将所有的 /var/www 换成 /home/discourse/www
sudo vim /etc/nginx/conf.d/discourse.conf

配置完成后保存退出,检查 Nginx 的配置是否正确

sudo nginx -t

让 nginx 重新加载配置文件

sudo services nginx reload
自动切分 logs 配置

通过系统自带的 logrotate 来切分logs,执行命令 contab -e, 然后添加下面的内容即可。

0 0 * * * /usr/sbin/logrotate /home/discourse/www/discourse/config/logrotate.conf
启动 Discourse

通过 bluepill 管理 Thin server 并启动 Discourse 的命令是

RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ROOT=/home/discourse/www/discourse RAILS_ENV=production NUM_WEBS=2 bluepill --no-privileged -c ~/.bluepill load /home/discourse/www/discourse/config/discourse.pill

查看 bluepill 的进程管理信息,可以看到启动了两个 Thin server 以及一个 sidekiq workder 后台任务。

bluepill status

如果没有配置文件没有什么问题,这个时候在浏览器中访问 url,就应该可以看到 Discourse 论坛界面了。

为了重启后 Discourse 可以自动运行,可以把 bluepill 命令放到 contab 中,执行命令 crontab -e 然后写入下面的内容即可。

@reboot RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ROOT=/home/discourse/www/discourse RAILS_ENV=production NUM_WEBS=2 /home/discourse/.rvm/bin/bootup_bluepill --no-privileged -c ~/.bluepill load /home/discourse/www/discourse/config/discourse.pill
 
11 months ago

http://teahour.fm 是一个面向互联网开发人员的 Podcast 音频类节目,因为我们都是做技术的,所以我们录制节目的时候采用的是一整套免费的解决方案,具体见如何使用GarageBand和Skype录制Podcast

剪辑2013年最后一期Teahour podcast的时候,我遇到一个非常奇怪的问题,当导出剪辑好的音频时,GarageBand出现下面这个报错信息:

Screen Shot 2013-12-31 at 12.30.06 AM.png

在网上Google了一圈没有找到解决办法,看到某个论坛里面说这是GarageBand的一个Bug,本期节目只有一小时四十分钟,我曾经录制过超过两小时的节目都没有遇到过这个问题,后来经过摸索找到一个办法。首先将音频文件分割成两个部分分别导出,然后借助工具将两个导出的文件合并即可。

首先安装FFmpeg,在Mac下用Homebrew安装非常简单:

brew install ffmpeg

然后用FFmpeg的Concat demuxer的方式合并,建立一个叫做mylist.txt的文件

file '/Users/Daniel/Desktop/teahour_1.m4a'
file '/Users/Daniel/Desktop/teahour_2.m4a'

然后执行下面的命令就搞定了。

ffmpeg -f concat -i mylist.txt -c copy output.m4a
ffmpeg version 1.2.4 Copyright (c) 2000-2013 the FFmpeg developers
  built on Dec 31 2013 00:45:03 with Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/1.2.4 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-nonfree --enable-hardcoded-tables --enable-avresample --enable-vda --cc=clang --host-cflags= --host-ldflags= --enable-libx264 --enable-libfaac --enable-libmp3lame --enable-libxvid
  libavutil      52. 18.100 / 52. 18.100
  libavcodec     54. 92.100 / 54. 92.100
  libavformat    54. 63.104 / 54. 63.104
  libavdevice    54.  3.103 / 54.  3.103
  libavfilter     3. 42.103 /  3. 42.103
  libswscale      2.  2.100 /  2.  2.100
  libswresample   0. 17.102 /  0. 17.102
  libpostproc    52.  2.100 / 52.  2.100
[concat @ 0x7feda9004a00] Estimating duration from bitrate, this may be inaccurate
Input #0, concat, from 'mylist.txt':

  Duration: 00:00:00.01, start: 0.000000, bitrate: 64 kb/s
    Stream #0:0: Audio: aac (mp4a / 0x6134706D), 32000 Hz, stereo, fltp, 63 kb/s

Output #0, ipod, to 'output.m4a':

  Metadata:
    encoder         : Lavf54.63.104
    Stream #0:0: Audio: aac (mp4a / 0x6134706D), 32000 Hz, stereo, 63 kb/s

Stream mapping:
  Stream #0:0 -> #0:0 (copy)

Press [q] to stop, [?] for help
size=   48984kB time=01:42:53.31 bitrate=  65.0kbits/s
video:0kB audio:48229kB subtitle:0 global headers:0kB muxing overhead 1.565309%
 
12 months ago

查看 Heroku 上的 PostgreSQL 版本号,可以帮助你确认某些高级功能是否可以启用。

首先通过 heroku toolbelt 远程打开服务器上得 psql

heroku pg:psql

然后执行一条查询语句就可以得到当前 PostgreSQL 的版本号

select VERSION();
 
about 1 year ago

距离 RubyConfChina 2013 还有不到十天的时间,但是依稀觉得去年的大会就像是在昨天一样。当初误打误撞的进了Ruby圈子,学习Rails开发,到后来接手组织上海的Ruby on Rails的线上线下社区活动,策划第一届RubyConfChina大会,然后一直坚持每年做一次大会,到今年为止,这件事居然坚持了五年。坚持办Ruby大会五年了都,这件事说出来我自己都觉得有点难以想象,好像生命中还没有其他任何一件事情可以跟这件事相提并论,并可以让自己都觉得唏嘘不已的。

其实长期以来,我对自己的要求是成为一个让大家都觉得你很优秀,很出色的开发者。走上IT这条路,时间久了,就会发现其实自己的长处并不在开发本身,而身边优秀的开发者众多,很多人的才能,勤奋,甚至是态度都能总叫人望尘莫及。于是就很容易会变得浮躁,自己身上有什么优点是可以拿出来炫耀的呢?数来数去,大概就是坚持这件事吧。

从第四届RubyConfChina大会开始,我把Ruby大会从一天的日程改成两天,其实办两天的大会并不是一天大会的工作量乘二,而是乘四。这个我一开始并不知道,是后来才明白过来的。因为你要接触更多一倍的人,找多一倍的讲师,拉多一倍的赞助,计划多一倍的日程,而这些多一倍加在一起,总共所需要投入的时间精力就真的是四倍有余了,怎么办?

这个世界上有的事情,是你没有到达那个境界,没有那个才能或者条件,你无论如何都做不到的。而另外一些事情,你初看起来好像很难,超出你的能力范围,但是只要你每天都坚持去做,持续的投入并推进这件事情向前一点点,待到过了那个时间点,你会发现,哦原来只要坚持,聚沙成塔,这件事真的就办成了。RubyConfChina大会就是后者,只要每天都能抽出时间去推动他,坚持三四个月,后来的结果大家都看到了,RubyConfChina 2012 大获成功!

每年办会过程中,都会遇到各种个样的情况,有的事情非常给力,有的事情非常狗血,有的事情则是让你完全意想不到的,比如这件至今还让我记忆深刻的一件事:记得去年大会的前一天晚上,@kgen 给我打了个电话,告诉我说 云梯 很想赞助我们的大会,但是因为一些条件的限制,于是呢,他们去猫的天空之城买了50张明信片,由公司的小姑娘亲手书写,希望在大会上送给有缘人,以留作纪念。我心说这是好事呀,作为大会主持人的我,把这些明信片散出去,举手之劳嘛。我实际上是这么想的也是这么做的,但是结果却是这样:

在下午两场主题分享的间隙,我突发奇想,说我这里有50张云梯MM亲手写的明信片,我就站在这里,大家上来抢吧,先到先得,于是一群人霎那间冲了上来......简直一群禽兽将我扑倒,我身后的大会展板都被差点推倒了。记得当天晚上打开 RubyChina 看见好几个网友在上面给我留言,说今天大会最精彩,最高潮的地方就是看到 Daniel 被扑倒。于是在这次大会上,大家知道了一个叫做云梯的服务,中国最好的VPN服务提供商(没有之一),今年 @kgen 他们的产品发展的极好,并决定正式以赞助商的身份进驻本次大会,希望今年不要在被扑倒了吧,哈哈。

 
about 1 year ago

首先感谢所有支持并购票参与大会的朋友们,谢谢你们。大会门票于9月27日凌晨调价,从优惠期每张门票 499 元调整到现在每张 500 元,门票价格上浮人民币一元。

请允许我跟大家解释一下这次调价,以及调价之前到底发生了一些什么事情。首先我们的定价策略是由本次大会的财务预算决定的,因为成功的邀请到了多位海外知名大牛,高手,承担这些嘉宾的费用是一笔很大的开销,其次北京的大会会场非常贵,我们花了很多时间物色合适的场地,最终选择了鑫海锦江大酒店,两天场地费8w,还不包括嘉宾休息室,考虑到大会日程比较满,中午休息时间有限,加上大会会场周围的餐饮不算很发达,于是决定所有参会者在酒店吃中西餐自助,这些条件放到一起,让我们在一开始的财务压力巨大。

大会的门票销售可以分为几个阶段:

初期阶段

在初期赞助情况不明朗的情况下,我们决定 Early Bird 门票按照成本价 499 元销售,普通门票按照 599 元销售,我们计划销售 100 张 Early Bird 门票,300张普通票,加上一定的赞助费,并尽可能节约办会,这样就能解决大会的所有资金问题。当大会开始正式售票时,前100张门票销售及其火爆,仅仅用了一天的时间就销售了超过100张,也就意味着,第一天如果没有看到大会门票开售的消息的人,会错过优惠门票。第一天晚上我们公布了消息并调价后,门票销售情况开始急转直下,每天大概只售出几张门票,甚至一周时间销售不到十张。毫无疑问,门票定价过高,超出了普通参会者的心理预期。但是如果拿不到足够的赞助费,大会就会亏本,我们陷入了两难,为了促销,我们决定延迟优惠售票期限至一个月,期限截止到在9月26日,距离大会开幕一个月的时间。

衰退阶段

当门票销售经历了第一天的火爆之后,即使后面调回 499 元的价格,延长优惠期,依然无法刺激大家继续购票,门票销售情况甚至可以说是惨淡。在后面的两周,每周大概只能销售十几张,这让我们主办方非常着急。初步分析下来,我们认为初期购票的人主要以社区的活跃分子,铁杆粉丝为主,这些人最有意愿参会,且有条件参会,所以他们是第一批购票者,对于这些人,无论我们定什么样的价格,他们都一定会参会。另外一个原因是我们的宣传渠道出了问题,我们除了在社区发宣传帖并置顶之外,没有什么其他形式的宣传。从大会官网的访问数据可以看出,该知道的人都已经知道了,就不来访问了,还不知道的人没有什么渠道或者途径知道这次大会,所以也不来访问官网。对于这种情况,我们必须要采取措施了。

高速增长阶段

我们首先继续加强大会相关的帖子在论坛的曝光度,@jasl 保证每两三天都会发出一个帖子,披露一些大会的信息,并反复不断强调大会的官网以及门票购买地址,不过这个做法收效只能说一般,大会官网和门票销量虽然有一定提升,但是并不符合预期。我发现在购票平台上有近百张门票的订单处于未支付状态,这些人其实是有意愿购票的,但是他们由于某种原因没有支付或者支付不成功,从而放弃了购买,他们才是最容易被转化的目标用户,于是我逐个给每个这样的订单用户发了一封邮件,问询具体是什么原因导致他们没有支付,以及我能否帮助他们,通过邮件才了解到有些人下单后忘记了支付,比如有些人是在公司下单后,需要晚上回家完成支付的人,然后就忘了。还有的人需要公司财务审批,而审批还在走流程,还有的人是日程不确定,所以当时不能决定是否要支付的,还有其他各种各样的原因,等等... 这个做法立竿见影,立即带来大概十多张门票的销售。

因为第一次在北京办会,人生地不熟,还没有拜过山头,于是我给最近六个月所有在论坛上发过招聘帖的北京公司挨家挨户的发邮件,邀请公司携带团队来参会,因为考虑到如果程序员有了所在的公司支持,会更有参会意愿,而 RubyConfChina 这么难得的学习机会,开明的公司应该会鼓励员工参会,并且比较容易达成公司报销门票这一局面,这真的是一个行之有效的做法,不但短期内带来大概几十张门票的销量,还帮我们挖掘出了潜在的赞助公司,并最终谈成了一笔赞助,这是完全是意料之外的结果。

RubyChina 注册会员9k多,活跃会员只是其中一部分,但是即使如此,在北京的程序员基数依然很大,所以没有理由总共只卖出这些门票。因为大量的用户并不活跃,所以我怀疑很多人在最近30天内没有上过论坛,对大会不知情,于是我试着给所有 RubyChina 会员来了一次邮件群发,在邮件里面介绍大会,让这些不活跃的用户知晓大会信息(邮件群发是另外一个故事,有机会写篇Blog出来给大家说说)。同时我跟赞助公司比如 @ucloud 合作了一个论坛营销推广活动,并尽可能联系赞助我们的关系公司的微博官方账号互动,在 Weibo 和 Twitter 上增加曝光率,增强宣传力度。这一套组合拳下来,我们的门票销售有迎来了一波高峰,在领近优惠期结束的最后一周时间,我们销售了近百张门票,到目前为止,我们的门票销售已经完成了四分之三,而距离大会开幕还有一个月时间去销售剩下的四分之一,情况非常的乐观。

关于调价一元

想要办一次大会离不开赞助商的支持,从个人角度来说,第五年办会,第五次为技术大会拉赞助,其中艰辛没有做过或者参与过的人估计难以理解,办成一次大会背后需要筹集的资金,资源,沟通,协调,组织,执行,加在一起真不是一件轻而易举的事情。找赞助就如同销售打 Cold calling,打出去20个电话能有一个回应你一句就算成功。所以我对这些愿意付钱的赞助商们充满了个人感激。可能是因为坚持了5年的原因,其实我们一直在努力攒人品,攒了5年,就无需在去证明自己做大会这件事的决心,以及做这件事的意义,已经有相当一批公司非常认可并赞同我们做社区,做大会的理念,所以不算太难就联系到了一批赞助公司,所以今年的赞助情况很不错。我们在开始计划大会之初做了最好的憧憬以及最坏的打算,虽然每天都在为迎接最坏的打算而惴惴不安,但是结果却是我们的愿景成为了现实,我们征集到的赞助商,让我们在财务上终于能松一口气了。

已经到了 Early Bird 门票优惠截止日期,可是我突然发现找不到门票调价的意义了。如果门票价格调高100元,首先500元是一个心里门槛,调价会让门票价格超过这个心里门槛,其次是对后面剩余的门票促销没有任何帮助,而我们又不需要通过这些剩余的门票创造收入,那么到底调价的意义是什么呢? 如果办会的钱够了,要更多的钱有什么用呢?我找不到调价的理由,于是我跟另外两个负责人 @jasl 和 @yedingding 说出我不提价的想法,经过一番讨论,最终我得到了他们的认同。因为之前的宣传过要调价,以及为了跟Early bird购票者拉开差别,我们象征性的只提高了一元。

只调价一元,对早期的 Early bird 购票者是不是不公平呢?的确不公平,这些最诚意支持我们的朋友,理应拿到 100 元的折扣,但是现在这些折扣完全体现不出来了。我有点一意孤行想要在办好大会的同时,尽可能让利给所有人,所以这些 Early bird 是有理由怪罪我的,如果在门票价格上我不能弥补这些 Early bird 的同学们,那么我一定会想办法在其他地方弥补你们,现在大会还在进行中,财务方面能剩余多少仍未可知,等大会办完后才能知晓。即使有剩余我也希望能用于明年大会的起步资金。但是不管怎样对于 Early Bird 我一定要尽可能多的回馈大家,请大家理解并多多包涵。

大会进入倒计时一个月了,后面的工作主要围绕后勤和场地相关的事务,繁杂,不容易做好,但是跟大会的整体体验有息息相关。因为门票的销售情况以及大会的赞助情况,让我们可以不必过多的操心其他,而是专心搞好现场工作,力争办一场更好的大会,所以我们身上依然肩负着巨大的压力,后面的工作需要北京的志愿者团队们的帮助,我始终相信我们的志愿者团队是最棒的团队。

最后感谢在这段时间 @jasl 和 @yedingding 的努力工作,@saberma 的 19wu 给大会提供票务系统强力支撑,以及多位朋友为我出谋划策,帮我解决实际的问题和困难,请原谅我没有提及你们的名字。最后希望大家继续支持我们,支持这个 Rubyist 自己的年度盛会,感谢大家!


广告

请大家留意官网,向身边的朋友宣传 RubyChina 社区和本次 RubyConfChina 技术大会!

大会官网: http://rubyconfchina.org

大会购票: http://19wu.com/rubyconfchina

 
about 2 years ago

缘起

我是第四次筹办RubyConfChina,借着 RubyChina 的人气,大会的筹办一开始蛮顺利的,年初就定了Matz先生的行程,6月份定了会场,9月份定了所有的讲师和赞助。这次大会的沟通协调工作量大概是往年四倍多,因为大会的规模增加了四倍呀。事情往往一开始非常顺利的话,到了后来就会生出许多曲折,本次大会也不例外。

从写稿的时刻算起,距离本年度的Ruby大会倒计时24天。筹备工作进入了冲刺阶段。刚刚过去的一周,充满了各种纠结,各种无奈,真的是好漫长,好漫长的一周。到底发生了什么事? 具体的情况,在下面这个链接中有详细的说明:

RubyConfChina 筹款页

这是一个众筹页面,用来募集办会的资金,当这个页面已经完成到准备发布的时候,我决定放弃这次众筹页面,不采用这个方式来筹集资金了。为什么放弃?下面我会用问答的方式,把我对众筹页的考虑,以及未尽的话都写出来吧。

Q: 为什么要发起一次众筹?

10月18日那天下午,我去一位好友的工作室拜访,在回来的路上收到通知,大会会场不再免费,要我们自筹5万元场地费。当时我就暴走了,抓起电话联系我所有可以联系的相关的人,其实最终结果一直是协商到了第三天才有了定论。在没有主意的时候,两个人我非常感谢,一个是VMware的Frank,他其实给了我一个Backup的方案,而这个方案是我万不得已的时候就会需要。另外一个人是盛大创新院的老庄,老庄帮我冷静分析了一下情况,给了我一个可供操作的对策,发起一个众筹网站,为了吸纳热心的Ruby爱好者的帮助,也为了完成一次危机营销,说不定能解决问题,还能借这个方式达到宣传推广Ruby大会的目的。

Q: “页面中提到组织者之间分歧很大,其实并未完全达成一致”怎么说?

过去三年,我们努力为大家办了三次免费的大会,那么今年突然要收费了?其实一开始我们计划免费的,要办最好办一场免费的大会。有一个核心因素决定了是收费还是免费,考虑到创智天地中饭难解决的问题,我们决定给大家定饭,中午时间不让大家离开会场太远。注意,购票定饭不是强制的,如果你不需要组织方提供中饭,那么你完全可以不必买票,你依然可以不受限制的参会。当出现场地费问题后,要不要众筹,大家还是觉得,如果你已经找了赞助,还卖票,还直接从大家哪里筹钱,为什么我们处处跟别人要钱?其他人会怎么看?这是几个与会者最大的担心,这个担心是不无道理的。

Q: 如果这个众筹页面发布出去,会有什么样的影响?

说实话我不知道,页面做好了以后,我先发给了几个熟人朋友,华顺,老庄,几个大会志愿者,好友玎玎,Terry等等。我收到了非常多的带有强大正能量的反馈,包括长篇的全文订正,包括各种支持,鼓励的话语。在征求他们意见的时候,我就要求大家只关注内容,不要捐款,但是还有几个朋友表示要立即捐款,还有几个朋友行动起来,开始帮我寻找身边,圈子里面的潜在的赞助商,帮我宣传,推广,争取赞助。今天上午,我接到了来自棒棒糖的电话,一番沟通我又落实了2千元赞助,感谢棒棒糖成为我们的铜牌赞助商之一。Dingding跟我说,他被我的晓之以情,动之以理的页面感染了,真心想要帮助。

Q: 既然小范围内效果这么好,那么为什么不发布出去?

其实是明眼人仅仅从我披露出的信息中找到了症结,帮我找到了解决问题的另外一个途径。这次问题其实就是2万元钱,而我自己已经解决掉了大部分了,而小部分却采用这种可能备受争议的方式,其实有些冒险,如果带来太多负面评价,对大会的品牌和推广都不利。其实还有其他途径的,朋友问我为什么一定要给大家定30元的中饭?因为我想给大家定必胜客披萨,根据我参会的经验,招待大家吃一顿披萨大家普遍会比较满意。当遇到了资金问题,就不能给大家吃披萨了,可以稍微便宜一点,比如每人20元,也可以安排大家吃好。这样可以节约出来了8千元,问题不就解决了嘛?虽然整体的伙食水准下降了,但是相比说服大家去众筹来说,到底哪个更容易让大家接受?既然问题能够从内部解决,那么是不是可以不必走外部途径?这些问题让我一下子茅塞顿开,决定放弃众筹这个方案,尽管努力做了这么一个页面,没有用上有点不甘心,呵呵。

Q: 既然不用众筹,怎么说服大家接受伙食标准下降这个问题?

我会通过其他方式的弥补的,绝对不会亏待大家。大会最重要的就是会议质量。拿最近的PyCon2012大会举例,我作为见证者(其实我是上海会场的现场主持人),大会给参会者留下了糟糕的体验,Ruby大会的几个负责人,大家立即开始讨我们怎么能够保证质量,让大家满意,尽量不重蹈PyCon的覆辙。我已经要求OpenShift,盛大云,以及其他来自厂商的主题分享人这周内提交PPT给我,我们通过PPT审核主题内容(主要是是否夹带广告)。如果内容方面跟大会主旨不符,我们先撤下,然后要求对方证明自己的主题值得上我们的大会。如果水太大,我们哪怕全部换上开放式讨论话题,也要严防死守很水的主题,确保大会干货质量。OpenShift跟盛大云已经立即响应,从他们的调整内容看,比较令人满意。我希能经过这么一轮敲打后,厂商们的主题会缩水不少,这样演讲的内容也会有较多干货。现在已经到了会议筹备的冲刺阶段,我们都在努力尽量办好这次大会,需要大家的支持与理解。

Q: 为什么页面上不公布明细财务报表?

其实是应该公布的,但是希望稍微能晚一点公布出来,因为有些具体条目我只有估算,还要去落实,而有的条目,要到大会开始之前才能落实最终的数字,所以现在有部分条目,只有一个大差不差的约数。收到的赞助费,所有演讲嘉宾的差旅费,食宿都已经确定了。大会的中饭,茶歇要看会议最终的售票情况,实到人数,所以现在我只按照400人来估算。真心希望更多的人来参会,但是人多压力就更大了,呵呵。还有个支出的大头是开发票要缴纳的税款,以及帮助走账的公司的人情往来费用。这次办会是社区办会,背后没有实体公司,要找朋友公司帮助解决财务走账问题,每家赞助公司,我都会问小额赞助是否能用收据充抵?如果要发票的话,餐饮,住宿发票能不能用?目的都是为了能节约一点算一点,想要节约开支也不容易,拜托朋友公司帮助开票,欠下的人情债是不能列入大会预算中的,这些其实都是我自己去埋单了。待稍微晚些时候,很多条目落实了,我会把财务报表完整公布出来,这样能最大限度的减少眼下的争议,质疑,也节约一些时间用于解释。我希望能多花点时间精力到筹备会务本身,还要大家的支持与理解。

Q: 那么你们这么辛苦办会,为了什么?

真的辛苦,过去每次办会我都会产生一个念头:真心不容易,明年我真不愿意再办了。但是另外一个念头会同时产生:真心觉得自己的付出都是有意义的,都是值得的。为了今年办会,我耗费在各种事务上的时间,保守估计,至少再200小时以上,这意味着一个人月的工作量。但是回过头来,通过努力能被大多数人肯定,被大多数人认可,我觉得值了。如果没有这些人的认可和支持,大会早就办不下去了。过去三年的积累,让今年的计划两天,两个会场,24个主题成为了可能。如果在两年前,哪怕是一年前,真的不敢想。时间的损失,金钱的损失,工作上的耽搁,我就不会计较这么多了,因为我很满足。

Q: 还需要帮助么?

要的,现在还有5千元的资金缺口,虽然我关闭了个人捐助的路径,但是以公司方式的捐助渠道依然开放,希望使用Ruby的公司能关注我们,支持我们,不需要太多,小额赞助就好,500,1000,最多不超过2000。这几天来自周围朋友以及社区的热心会员然我感动,我现在更有信心了,办会剩下的这些钱一定会筹集到的。这里我还想跟和所有相关公司保证,在会议期间,我们能给满足公司的尽量满足,能做到的一定做到,让RubyChina社区和公司建立长期合作关系,已经跟我们建立良好关系的公司,比如VMware,CloudFoundry,盛大云,Upyun,这些公司都可以作为例子,我们社区和公司良好合作互动,一起去兑现建立社区之初的承诺:

We push the China Ruby & Rails community forward!!

Update:


我们通过调整伙食费的预算,已经从内部解决5千元的资金问题。感谢大家的关注和支持!
在RubyChina论坛上,我发起了一个主题贴,详细说明此事:http://ruby-china.org/topics/6278
公司赞助通道还会继续开放,虽然已经临近大会开幕,如果能在筹集一两笔小额赞助的话,我们就可以改善大家的伙食,提升大家的参会体验,同时我们能够远道而来的演讲嘉宾们照顾的更好。

 
almost 3 years ago

Ruby除了rails还能做什么?

这两天Ruby-China上有个一个讨论,关于Ruby除了rails还能做什么,其实除了Rails之外,Ruby能做的太多太多了,不过就我来说,除了用于Rails开发之外,Ruby我用的最多的就是写各种Command Line工具来解决各种小问题,Command Line工具又称为命令行工具。

提到用Ruby写命令行工具,就绕不过一个问题,如何解析命令行参数?

Unix下的命令行工具

先啰嗦一下Unix下的命令行工具,Unix的命令行工具历史悠久,这里面故事非常非常多(以后再讲,或者参见Unix编程艺术)。随着时间的推移,对于如何正确构建优良的命令行工具,Unix社区慢慢形成了一整套完整的Convertion以及惯用法,如果你的命令行工具遵从这些Convertion,那么用户将会非常容易的去使用你的命令行工具,甚至通过简洁的方式,将你的命令行工具和各种其他工具组合起来,用来完成各种复杂的操作。

正确的处理命令行参数对于写出高质量的命令行工具非常重要,那么如何正确的处理命令行参数呢?如果有C语言编程经验,或者用C语言写过命令行工具的人可能很熟悉getopt(GNU getopt_long()),getopt是C Library中一个专门用于解析命令行参数的工具,通常用C去写命令行工具的时候,getop是一个很自然选择。

用Ruby写命令行工具

当使用Ruby写命令行工具的时候,我们在不借助任何内置/外置的命令行参数解析工具的情况下,可以直接从ARGV取到传入命令行的参数,然后手工判断,验证并执行后续操作。不过从遵循Unix的命令行工具的Convertion角度来讲,我不建议你直接从ARGV取数值,而是利用现有的库来作这件事情。Ruby的标准库内置提供了一个getopt的Ruby实现GetoptLongGetoptLong基本上模拟了C语言版本的全部借口/功能,不过Ruby开发社区不推荐你使用GetoptLong,而是建议使用另外一个也是内置的且更加强大的解析库:OptionParser

这个世界上总是有人不断的重新发明轮子,除了Ruby已经内置的OptionParser,还有下面这些第三方实现的轮子:

Thor是Rails 3以后内建的命令行工具,严格意义上说,Thor不仅仅用于解析命令行参数,而是用于替代rake作为新的task标准工具,Thor的命令行参数的Parser是自己实现的,我个人建议在写Rails的task的时候,把Thor作为首选,但是作一般用途的命令行工具,Thor有点overkill了。

Gli是一个用于建立“Git-Like Interface Command Line Parser”的工具,这里我简单给出一个什么是“Git-Like”的解释。通常Unix下的命令行工具都符合一个哲学,即“作一件事并且把它做好”,但是有些功能强大复杂的工具,如git,可以通过指定不同的Action执行不同的操作,比如gitpushpull操作:

$ git push
$ git pull

就是两个完全不同的操作,但是他们的command部分都是git,只是action部分不同。我们也可以把这样的通过不同的action来实现不同的操作的命令行工具叫做Command-Suit工具,即从功能上看,它不是一个命令,而是一个命令的suit集合。Gli就是帮助你快速实现这种Command-Suit的框架,如果你需要编写复杂的命令行工具,Gli是一个不错的选择。

TrollopChoiceOptiflag都是命令行参数的Ruby Parser,他们的目的一致,而且他们解析过程都遵循Unix的约定,只是实现各有不同,用法也不同,不过对我来说,他们都是一回事。就Unix命令行来说,参数只有Options,Arguments,以及Actions而已,所以具体用哪个,看你的个人喜好,简单对比下来我认为Choice的DSL语法最易读,简洁,优雅,如果你需要这些第三方Command Line parser的时候,不妨考虑一下Choice。不过我奉行另外一个原则,如果系统内置了的,我就不考虑第三方gem,而且Ruby内置的OptionParser足够强大,能满足我对解析Unix的命令行参数的一切需求,所以我优选使用OptionParser。这里我简单猜测一下为什么还有这么多第三方的轮子,第一是不知道Ruby已经内置了这个,第二个可能就是不爽Ruby内置的这个parser的文档或用法,虽然OptionParser足够强大灵活,但是不代表它好用,容易上手,相反,它的文档就相当坑爹!

用OptionParser创建命令行工具

下面这张图就是Ruby给出的OptionParser的文档,除了这张图片之外就是一个官方范例,然后就没了… 说实话我第一眼看了这张图和官方范例后感觉看不懂,需要反复通过Google各种文章和范例,才了解到了OptionParser的基本用法。

+--------------+
| OptionParser |<>-----+
+--------------+       |                      +--------+
                       |                    ,-| Switch |
     on_head -------->+---------------+    /  +--------+
     accept/reject -->| List          |<|>-
                      |               |<|>-  +----------+
     on ------------->+---------------+    `-| argument |
                        :           :        |  class   |
                      +---------------+      |==========|
     on_tail -------->|               |      |pattern   |
                      +---------------+      |----------|
OptionParser.accept ->| DefaultList   |      |converter |
             reject   |(shared between|      +----------+
                      | all instances)|
                      +---------------+

通常的Unix命令行参数包含下面这些形式:

  • Option - Option主要功能是用于调整命令行工具的行为,Option的表现通常有两种形式,short option或者long option。Option的类型有两种,switchflagswitch不带argument,而flag带有argument。
  • Argument - Argument通常表示命令行工具要操作的对象,通常是路径,URL或者名称等等。
  • Action - 表示命令行工具的行为,比如git命令的push或者pull等等。

举个例子git log --max-count=10git是command。log是action,表示查看git的提交历史。--max-count就是option,表示最多显示N条commit记录。而最后的=10就是argument,表示option的数值,即查看最后10条历史提交记录。所有的Unix命令行工具都遵循这样的一个约定,这里需要主意一下,Argument前面的=在很多命令行工具中是可以省略的。

OptionParser创建一个简单的命令行工具,通常我们只需要创建一个OptionParser的实例instance,然后给这个instance传入一个block,在这个block内部我们就可以使用OptionParser提供的方法来解析命令行参数,特别是用on方法来根据定义捕捉各种参数,并将参数解析成可被使用的Ruby数据,如String,Boolean,Array以及Hash等。而on方法最让人困惑的地方就是它异常灵活参数处理,比如on方法的第一个参数,如果是一个-加一个非空格字符,则把这个参数当作switch来处理,例如on('-n'),如果是一个-开头的字符,后面跟着一个空格外加另外一个字符,那么就把这个参数当作flag处理,例如on('-n NAME')。如果on方法的参数超过两个,并且两个都是String,那么则视这两个参数表示一个意思,例如on('-n NAME', '--name NAME')。如此这般的例子还有很多,如果有更高需求的朋友,我建议你还是直接去啃源代码吧。

下面我创建一个名为my_awesome_command.rb的命令行工具,这个工具直接输出我的命令行参数解析的结果,我用中文注释来说明OptionParser视怎么用的:

#!/usr/bin/env ruby

require 'optparse'

options = {}
option_parser = OptionParser.new do |opts|
  # 这里是这个命令行工具的帮助信息
  opts.banner = 'here is help messages of the command line tool.'

  # Option 作为switch,不带argument,用于将switch设置成true或false
  options[:switch] = false
  opts.on('-s', '--switch', 'Set options as switch') do
    options[:switch] = true
  end

  # Option 作为flag,带argument,用于将argument作为数值解析,比如"name"信息
  opts.on('-n NAME', '--name Name', 'Pass-in single name') do |value|
    options[:name] = value
  end

  # Option 作为flag,带一组用逗号分割的arguments,用于将arguments作为数组解析
  opts.on('-a A,B', '--array A,B', Array, 'List of arguments') do |value|
    options[:array] = value
  end
end.parse!

puts options.inspect

执行结果

$ ruby my_awesome_command.rb -h
here is help messages of the command line tool.
    -s, --switch                     Set options as switch
    -n, --name Name                  Pass-in single name
    -a, --array A,B                  List of arguments

$ ruby my_awesome_command.rb -s
{:switch=>true}

$ ruby my_awesome_command.rb -n Daniel
{:switch=>false, :name=>"Daniel"}

$ ruby my_awesome_command.rb -a Foo,Bar
{:switch=>false, :array=>["Foo", "Bar"]}

希望以上内容能够帮助你掌握写出能解析符合Unix标准的命令行参数的工具,如果要写出易用,对用户友好,跟其他命令行工具互动良好,可测试,可维护,可格式化输出内容的真正awesome的命令行工具,您仍然需要继续努力,加油吧!

 
almost 3 years ago

某个手上的项目是一路从Rails 2.x.x升级到了Rails 3.1.1。在老版本的Rails中,如果需要通过Gmail的SMTP来发送邮件,需要给Ruby的Net::SMTP打个smtp-tls的补丁,或者通过类似的插件如smtp-tls来解决问题。

通过bundle升级过程中发现gem无法找到smtp-tls插件,因为这个插件并没有host在 http://rubygems.org上,而是host在 http://gems.github.com。在这个插件的homepage上说如果Rails >= 2.2 并且Ruby的版本在v1.8.7以上,只需要在你的ActionMailer的config中配置enable_starttls_auto即可。

ActionMailer::Base.smtp_settings = {
  :enable_starttls_auto => true,
  :address => 'smtp.gmail.com',
  :port => 587,
  :domain => 'example.com',
  :authentication => :plain,
  :user_name => 'user',
  :password => 'secret'
}

但是读ActionMailer源码发现,Rails的默认已经打开了enable_starttls_auto这里有一些相关的讨论。所以如果你用上了Rails 3.1.1或者以上版本,你无需作任何特别的设置即可工作。

$ rails c
Loading development environment (Rails 3.1.1)
>> include ActionMailer::DeliveryMethods
=> Object
>> smtp_settings
=> {:address=>"localhost", :port=>25, :domain=>"localhost.localdomain", :user_name=>nil, :password=>nil, :authentication=>nil, :enable_starttls_auto=>true}
>> smtp_settings[:enable_starttls_auto]
=> true
 
almost 3 years ago

针对于新入门的开发者,如何在 Mac 下用 Nginx + Passenger 部署 Rails 的运行环境。

系统需求

  • Mac OSX Lion
  • 梯子

步骤0 安装环境依赖

安装Xcode 4.1,Xcode4.2以及更高的版本在 Lion 仍然存在一些兼容性问题,强烈建议使用XCode 4.1,下载地址:

https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_4.1_for_lion/xcode_4.1_for_lion.dmg

安装RVM

$ bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

配置RVM自动加载,将下面这一行代码添加到~/.bash_profile中,然后退出iTerm并重新启动

[[ -s "/Users/Daniel/.rvm/scripts/rvm" ]] && source "/Users/Daniel/.rvm/scripts/rvm"

安装 ruby-1.9.2-p290

$ rvm install 1.9.2

设置系统默认使用 ruby-1.9.2

$ rvm use 1.9.2 --default

步骤1 安装 Rails

安装Rails

$ gem install rails

Rails安装完成后,创建一个rails项目,假定你的项目叫做:awesome project

$ rails new awesome_project

启动Rails,并访问 http://localhost:3000

$ cd awesome_project
$ rails server

步骤2 安装 Passenger 和 Nginx

首先通过gem安装passenger

$ gem install passenger

因为Nginx不支持动态module载入,所以需要通过Passenger来自动下载,编译,安装由Passenger修改版的Nginx:

安装Passenger + Nginx

$ passenger-install-nginx-module
  1. Yes: download, compile and install Nginx for me. (recommended)
    The easiest way to get started. A stock Nginx 1.0.10 with Passenger
    support, but with no other additional third party modules, will be
    installed for you to a directory of your choice.

  2. No: I want to customize my Nginx installation. (for advanced users)
    Choose this if you want to compile Nginx with more third party modules
    besides Passenger, or if you need to pass additional options to Nginx's
    'configure' script. This installer will 1) ask you for the location of
    the Nginx source code, 2) run the 'configure' script according to your
    instructions, and 3) run 'make install'.

Whichever you choose, if you already have an existing Nginx configuration file,
then it will be preserved.

Enter your choice (1 or 2) or press Ctrl-C to abort: 这里建议选择1

Please specify a prefix directory [/opt/nginx]: /usr/local/nginx

当询问nginx的安装路径的时候,个人建议安装到/usr/local/nginx

当安装完成后,会在console中提示如何配置Nginx

Passenger会自动帮你将下面两行添加到Nginx的配置文件中/usr/local/nginx/conf/nginx.conf(很人性化)

http {
  ...
  passenger_root /Users/Daniel/.rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.10;
  passenger_ruby /Users/Daniel/.rvm/wrappers/ruby-1.9.2-p290/ruby;
  ...
}

server {
  listen 80;
  server_name www.yourhost.com;
  root /somewhere/public;   # <--- be sure to point to 'public'!
  passenger_enabled on;
}

请不要忘记将nginx命令行程序连接到/usr/local/sbin

$ sudo ln -s /usr/local/nginx/sbin/nginx /usr/sbin/

步骤3 配置Nginx + Passenger + Rails

关于Nginx的配置,请参考Nginx的官方网站以及Passenger的官方网站

修改hosts文件,给你的项目一个本地域名, 比如awesome_project.local

$ sudo vim /etc/hosts
127.0.0.1 awesome_project.local

测试hosts

$ ping awesome_project.local
PING awesome_project.local (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.054 ms

继续配置Nginx, 这里我给出一个最小可运行的Nginx配置文件

vim /usr/local/nginx/conf/nginx.conf

nginx.conf

worker_processes  1;

events {
  worker_connections  1024;
}

http {
  passenger_root /Users/Daniel/.rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.10;
  passenger_ruby /Users/Daniel/.rvm/wrappers/ruby-1.9.2-p290/ruby;

  include       mime.types;
  default_type  application/octet-stream;
  sendfile      on;
  keepalive_timeout  65;

  server {
    listen 80;
    server_name awesome_project.local;
    root /Users/Daniel/awesome_project/public;
    passenger_enabled on;
    rails_env development;
  }
}

测试Nginx的配置文件语法是否正确

$ sudo nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

启动Nginx

$ sudo nginx

如何在修改Nginx的配置文件后,让Nginx载入新配置

$ sudo nginx -s reload

如何停止Nginx

$ sudo nginx -s stop

如何在不停Nginx的情况下,重新启动Passenger

$ cd path/to/your/awesome/project
$ touch tmp/restart.txt

好了,这个时候你可以打开浏览器,访问你的awesome_project网站了

http://awesome_project.local

最后,希望你能够在Rails的开发中找到快乐!
 
almost 3 years ago

差不多有有一年多没有写Blog了,最近又动了心思想要继续写下去,为什么就不解释了。

原来的Blog用WordPress搭建,曾经数次打算想要好好认真研究一下WordPress本身,最终都无疾而终,对WordPress始终爱不起来。看了台湾的xdite的两篇关于Octopress的介绍,就忍不住动手试了下。

alt Octopress

迁移过程出奇的顺利,现在这个Blog直接使用GithubPages,免费,原来WordPress里面的文章全部都迁移过来了,评论部分我按照Xdite的说法试着通过DISQUS迁移,不过没有成功,不过问题不大,原来的评论就算了不要了。

作为程序员,对Markdown格式都比较有爱,Octspress不使用数据库,两条命令直接deploy到Github上,非常流畅。其他关于Octopress的好处和如何迁移等内容,xdite已经写的非常详细了,我这就在一一叙述了。