Setting Up Your React Project Is Easier Than You Think

I have heard some ones complaining that setting up a Webpack + React project is not an easy job. It’s only partly true. Webpack is not well documented at this moment, and has many powerful features and plugins. This makes configuration on Webpack seem difficult to new developers. To make things even worse, there are tons of ‘starter kit’ projects over there, most of which are lack of maintainance.

Yeoman

Indeed, it is super easy to setup a brand new Webpack + React project using Yeoman.

Yeoman is a scaffolding tool for web developers. With diffrent generators we can generate different kinds of web projects quickly by lines of command.

Here’s how it is done:

1
2
3
4
5
6
7
8
# install Yeoman command and generator
npm install -g yo
npm install -g generator-react-webpack

# generate project
mkdir test-project
cd test-project
yo react-webpack

Thanks to generator-react-webpack, after answering some questions from yo you will get your project setup in seconds.

Easy and quick, right? Here’s what we have got out of the box:

  • a project with Webpack and React configured;
  • a solution for diffrent environments both for runtime and webpack configuration;
  • a flux project structure;
  • an optional PostCSS setup

Also this generator can help generate React components via command line.

为什么写不出博客了

近几年以来,我写博客的数量每况愈下,从几个月一篇,到几个季度一篇,现在已经将近一年一篇了。。 (✖╭╮✖)

今天也是无意间提起,让我思考这个问题

为什么越来越不写博客了呢?

  1. 首先想到的是「懒」+「忙」

    但其实「没时间」是一个最不成立的理由,所谓的「没时间」往往是「优先级不高」的幌子罢了。很多时候不忙,心里知道有很多事情应该去做,却什么也不想做。阳明先生 说没有「知易行难」这件事,不「行」是由于「知」得还不彻底。是的,我没有在自己心里把写博客重视起来当一回事,当成一定非完成不可的的事情。

  2. 追求的事物已发生了转移?

    是以前对技术更感兴趣一些,现在不感兴趣了吗?似乎也不是,现在每天起来之后也会阅读技术文章。对技术反而比以前更执着了。

  3. 害怕写不好

    以前反而大胆一些,也不怕误导别人;现在反而一些就想写很多,想写一个系列,系统地传授一系列的知识,系统地引导别人掌握一个东西。想做的事情太大,又没有足够的精力去做,于是就这样了。

原因算是找到了。

Done better than perfect

写得差总比不写要好,其实我的 blog 访问量那么低,也误导不了几个人。何况我最近也想明白了,没有所谓的「误导」,每个人都需要有自己的判断力,看到别人的论点需要有甄别的能力。阅读是自己的权利,判断也是自己的义务。坚持推荐我认为「好」的事物,就算因此让别人走了弯路,相信这种弯路也会是一种积累的。

所以我决定,从这篇开始,要多写写一些短小的技术文章了。即便没有人看也没关系,回归当初写 blog 的心境,记录自己的成长。

为什么 Limits 不生效

最近遇到一个奇怪的问题,无法将服务器的最大文件打开数量提高。

服务器是 Ubuntu Server 14.04

1
2
3
4
5
6
$lsb_release -a
No LSB modules are available.
Distributor ID:   Ubuntu
Description:  Ubuntu 14.04.2 LTS
Release:  14.04
Codename: trusty

正常情况下, /etc/security/limits.conf 的改动,应该在下次访问时就生效才对。 但是总是没生效,查了很多资料,尝试了很多修改,终于成功了

记录一下填的坑:

1. 确保 pam 生效

/etc/pam.d/login 中,存在:

1
session required pam_limits.so

2. 确保 ssh 使用 pam

/etc/pam.d/sshd 中,存在:

1
session required pam_limits.so

/etc/ssh/ssd_config 中, 存在:

1
UsePAM yes

3. limits.conf 建议不要使用星号

官方 manual 以及网上的教程有很多都用了 * 符号,然而不是所有系统都认的,我最后发现问题就是在这里!

1
2
3
4
5
6
7
8
9
10
# 不兼容方式:
* soft nofile 51200
* hard nofild 51200

# 兼容方式
root soft nofile 51200
root hard nofile 51200

qhwa soft nofile 51200
qhwa hard nofile 51200

如何确认问题之所在

查看当前用户的 limits

1
ulimit -a

查看另外一个用户的 limits

1
2
# 注意 ulimit 不是命令,而是 shell 方法
sudo -u <USER> sh -c 'ulimit -a'

经过 ssh 查看用户的 limits

1
ssh example.com 'ulimit -a'

Capistrano-hostmenu: 只操作指定的服务器

想必大家都用过 capistrano 了,部署代码到多个服务器上的神器。 不过使用中有个不太方便的地方是,某些情况下只想部署到其中几台。

例如我们新增了一个功能,但只想找一台服务器先部署上去测试一下,成功后再部署到整个集群。

有好几种方式可以做到:

  1. 临时修改发布脚本(config/deploy.rbconfig/deploy/***.rb
  2. 使用 HOST 命令行环境变量,或者 --host 参数, 比如 HOST=example.com cap production deploy

第1种不好重用,第2种在 capistrano v3.3 以上版本已经失效了。

我把我们项目中用的 task 抽出来做成了一个 gem: capistrano-hostmenu 功能实在是太简单,没有什么好描述的。。

一图顶千言:

如何开发 Ruby 命令行工具

最近 @季子乌 的 花瓣账号 突然被封,苦心采集的很多图片一下子全部看不到了!

后来虽然联系客服重新开通了账号,但还是心有余悸,觉得还是 pinterest 靠谱一些,准备把图片全部迁移到 pinterest 上。由于图片数量比较多,我就用 ruby 写了一个工具,将花瓣上的图片下载下来。(不过 pinterest 不能批量上传,这些图片也只是备份到本地,这是后话了)

这个项目已开源,地址是: https://github.com/qhwa/huaban_exporter

这个工具下载后,可以用执行 huaban 命令

1
huaban export boards --of qhwa

执行效果:

preview

我喜欢用命令行工作,之前做的几个 gem(lfd, fdlint)也都提供了命令行。这次就趁这个项目总结了一下怎样用 ruby 开发友好的命令行工具。

I. 初期怎样提高开发效率?

在写了基础的一些逻辑 model 后,我写个简单的 rake 文件,初期用 rake 来作为入口,边开发边测试。

一开始我只建了这样的一个 rake 任务,来调试获取画板列表功能:

1
rake boards         # 列出一个用户的所有画板 (user=用户名 rake boards)

后来随着功能不断完成,逐渐增加了几个新的任务,最后是完整的任务:

1
2
3
4
rake boards         # 列出一个用户的所有画板 (user=用户名 rake boards)
rake export_board   # 导出一个画板的所有图片到本地 (board_id=画板id  rake export_board)
rake export_boards  # 导出用户所有的画板图片到本地 (user=用户名 rake export_boards)
rake pins           # 列出一个画板所有的采集 (board_id=画板id rake pins)

II. 项目后期功能稳定后,怎么做命令行入口

rakefile 很适合自己用,但是要分发别人用,用 rakefile 就不方便了。做成带命令行脚本的 gem 更加方便。

  • 把你的脚本放到 bin 目录下
  • 加上执行权限(chmod a+x)
  • 加上 shebang, 比如 #!/usr/bin/env ruby

这一步我以前是用下面提到的 gli 来自动进行,但后来改成手动做了。因为 gli 生成了一些额外的文件,和 bundle 有点冲突。

III. 怎样让命令变得友好?

有个超棒的 gem 叫做 gli,帮你很容易实现出类似 git 这样风格的脚本

IV. 项目完成后,怎么用做成一个 gem,分享给别人?

bundler 提供了生成 gem 的功能

  1. bundle gem <name> 生成目录结构
  2. 修改 gemspec
  3. rake install 先在本地安装这个 gem 进行测试
  4. rake build 以最新的文件重新打包成 gem
  5. gem push pkg/<版本>.gem 将gem上传到 https://rubygems.org

SSL Notes With Ruby on Rails

上周我给小兔盒加上了https,比想象中容易许多。记录一下

免费的CA:

之前一直以为 CA 是需要每年付一笔不菲的费用的,但其实也有免费的,例如:

按照网站指示,可以获得由其颁发的证书 有两个文件(certification & key) 是后面工作的基础。

在开发环境中使用 https (无需nginx)

1
thin start --ssl --ssl-key-file YOUR_SSL_KEY_FILE --ssl-cert-file YOUR_CERT_FILE

一些 tips

url helpers

不管当前 request 是 https 还是 http,rails 的 url_helper 都使用 http

1
2
3
4
5
6
# file: config/routes:
root to: 'home#index'

# in views or helpers:
root_url                    #=> http://YOUR_SITE/
root_url protocol: 'https'  #=> https://YOUR_SITE/

使用 protocol 无关的 assets 地址

1
2
3
4
5
6
# file: config/environments/production.rb
Rails.application.configure do
  ...
  config.action_controller.asset_host = '//YOUR-CDN.com'
  ...
end

用工具检查

有很多工具可以检查 https 部署情况,例如这个: https://www.sslshopper.com/ssl-checker.html

石潭村游记

石潭村

石潭村在安徽省黄山市徽州区歙县霞坑镇,距离黄山市50多公里,离杭州余杭150公里多点。我们在杭州,所以过去很近,非常适合清明小长假自驾游。从杭州往西,一路有天目山、青山湖这样的景区,即便是在高速公路上,也能看到路边的小风景。

杭徽高速西行大约120km,之后是省道。经过一段蜿蜒刺激的盘山公路,又穿过几个热闹的小镇子,就来到了车子能开到的尽头——石潭村。啰嗦几句,实际上还能往前开,只是政府封了路,里面的路外地车进不去,只能乘坐当地的小商务车,一辆车可以坐八个人。网上也有老的攻略说石潭村里面还有十几个村子,进去需要徒步。其实我们发现已经修了不少水泥路进去,车子可以一直开到5公里深的大龙湾。一路依山傍水,满目都是油菜花,黑砖白墙的村落点缀其间,风景很美。

路边

我们是当天下午到的石潭村,在村口打尖吃饭,人还挺少的。暗自庆幸来对了地方,是个清净的地方。吃完饭再往前走了1公里路,正式进了村子,才发现实际情况稍微糟糕了一点。人虽然没有比肩继踵,但是已经把小村子挤得满满的了,很困难地找了车位,然后随便选了一家名字看上去比较正规的旅店住下,放了行李。

这家旅店的名字有好几部分组成,我们只记得第一部分“游侠客”,后面那部分所有人都没记住,因为看到“游侠客”三个字,有人就兴奋地说认识游侠客的创始人,我们就决定入住这家亲切的旅店了。

有了地方住,心里就踏实了。放下行李,我们一行人就兴致勃勃地出发,往村里头扑去。

石潭村

村子依山傍水而建。山并不高,一条小溪从山间穿过,将石潭村一分为二。溪水不算多,但是很清澈。河滩上有许多青色的鹅卵石。

玩水

我们在小溪边玩了一会,然后就开始随便行走。

村边菜地里面油菜花还开着一些,但是也有许多已经谢了,结了果实。还有葱,一些蚕豆,长得都很粗壮。村子里面很安静。房子都很老,但历史并不长。清一色的白墙,绘了深色的花边,古色古香。有几间木结构的老屋子,看上去很精致。

村子

村子

村子

村子

走着走着,就出了村子,来到了后山。看上去有路往里面走,据说是到另外的村子去的。这时候太阳已经快下山了,我们就决定不继续往前了。但是往回走又心有不甘,才没出来多久呢!边上的小山上,菜地里面中了很多油菜花,好像可以上去的样子,可是又不见有路。这时鸽子mm 咻咻地爬了上去,大家也跟着,顺着隐隐约约的小坎,爬了上去。

上去之后,穿过齐头高的油菜田,来到另一边山头,美景就这么突然映入眼帘。山下的村子,小溪,对面的林子、夕阳,和清新的空气一起,像画一样。

山坡

夕阳

放下野餐毯,大家坐在一起,吃吃喝喝,好不快活!

慢慢地太阳从对面的山头下去了。我们也下山,继续沿着小溪散步。然后就回到旅店稍作休息,便开始找吃晚饭的地方。

晚饭在另外一家本地的食宿一体的旅店吃的。菜很好吃,加上大家有点累,都觉得很美味。回到杭州后鸽子还认真地在大众点评上给了4星好评,这是后话了。

吃完饭已经不早,大家稍微讨论了一下第二天的行程,小朋友不喜欢坐船,所以坐船的方案就被否定了。但去哪里还是没什么定论。累了一天,大家纷纷洗澡睡了。

大龙湾

天刚蒙蒙亮,村子就已经苏醒了。出门务农的人,开张的早餐店,准备上山的摄友……外面逐渐嘈杂起来。

我6点时候醒了,躺在床上看了一下地图,觉得可以沿着小溪往下游走。看到小溪在一个叫做“大龙湾”的地方拐了大弯,有山有水有桥,觉得可以去看看。

其他同伴们还在睡觉中,我决定独自起来看看。

吃过白粥配咸菜的早餐,买了根刚炸出来的油条,在村外随便散步起来了。

早晨的景色果然更美。山中雾气缭绕,空气有点清凉,提神。远处的树林是一层层的,像水彩画一般。透过片片的油菜花,此时的村子最像水墨画了。

早上出门没有带相机,只有一个效果很差、不能自动对焦的手机。。。你们勉强感受一下好了。。

早晨

早晨

早晨

早晨

早晨

信步所至,都是美景。去哪应该都不错。

散步回来,大家已经陆续起床了。然后一起退房、出门,吃早饭,散步。简单商量了一下,就拦了一辆车往大龙湾去了。

沿途都是都是油菜花、小桥流水人家,大家都啧啧赞叹。

路上车、人都很少。开车的师傅说,我们是他见过的第一批去大龙湾的游客,上车的时候,他还以为我们是家在那边的呢!

大约10分钟不到,车子就飞驰到了目的地。

下车的地方,是村子外的桥头。清澈的山泉水沿着村民搭的竹筒子流在路上,又流进小溪。桥下的溪水很清,很多小鱼在不停嬉戏。

大龙湾

穿过桥就到了村子里面。

村子里静悄悄的,偶尔才看到骑自行车的小朋友从我们身边路过。后面还跟着一只小黄狗,好奇地嗅嗅我们,然后迅速追向主人了。

村里人少,但是很热情。我们在一位大爷家门口的长石板凳上坐下休息,和大爷聊天。

吃了点零食,稍微填了一点肚子。然后继续探索。

水边有一颗上了年纪的樟树,落下的叶子盖满了竹林间的小路。

樟树竹林

中饭本来想在水边烧烤的,但是走的时候没有带吃的东西,于是问一位采茶归来的老奶奶买了一些粽子、年糕,准备烧烤。不过老奶奶听说我们要生火,很是担心安全。。最后我们在老奶奶家借了灶,生火做饭。菜都是问老奶奶买的。

人们开始大秀厨艺。。

做中饭

吃了一顿饱饱的中饭,时间也是下午两点了,刚好适合回杭州。打电话叫上午送我们过来的师傅来接我们回去,不过他有事,另外找了一个师傅来接我们。

这是我们在等车:

等车

回到石潭村,我们就心满意足地开车回杭州啦!

Chinese_number: 一个解析汉字数字的 ~rubygem

ruby-china 上有人问到怎么优雅地解析汉字数字,比如 二十五 解析成 25 。我第一反应是应该查一下有没有这样的 gem,因为是一个很普遍的需求,说不定已经有人实现了呢!

不过很可惜,不知道是不是我没有查到,还是真的没有,总之没有找到。所以我就写了一个这样的 gem — chinese_number

用法很简单:

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

ChineseNumber.trans '今天二十万'
#=> "今天200000"

ChineseNumber.find "一年有十二个月三百六十五天"
#=> [{"一"=>1}, {"十二"=>12}, {"三百六十五"=>365}]

ChineseNumber.extract "今天二十晚"
#=> [20]

ChineseNumber::Parser.new.parse '二零一四'
#=> 2014

ChineseNumber::Parser.new.parse '一万三千'
#=> 13000

《代码的未来》 阅读笔记(1)—— JavaScript/CoffeScript/ruby 局部变量处理的异同

…… 身为 JavaScript 程序员的好友说,对 javascript 的不满之一,就是它的变量声明和作用域。 —— Mats

JavaScript

js 可以通过有无var来选择是否创建一个新的作用域:

1
2
3
4
5
6
7
var a = "out";
(function() {
  var a = "in";
})();

alert( a );
// "out"
1
2
3
4
5
6
7
a = "out";
(function() {
  a = "in";
})();

alert( a );
// "in"

很灵活。不幸的是,不小心漏写最外层 var 之后,变量有可能会变成全局变量,这是容易滋生 bug 的地方。

CoffeeScript

CoffeeScript 中统一去掉了var,全部按局部变量处理,和 js 不同的是,不再创建内层作用域了。

1
2
3
4
5
6
a = "out"
(() ->
  a = "in"
)()
alert( a )
// "in"

Ruby

1
2
3
4
5
6
7
# version 1
a = "out"
(lambda do
  a = "in"
end).call
puts a
#=> "in"
1
2
3
4
5
6
7
8
# version 2
# ruby 1.9+
a = "out"
1..2.each do |i; a| #<= 注意这里的分号
  a = "in"
end
puts a
#=> "out"

Ruby 中默认和 CoffeeScript 一致,使用外层作用域。但多了一种“作用域声明”,允许人工制定创建一个作用域。

结论

语言 同名局部变量处理策略
js 内部优先,可以通过去掉 var 向上查找
coffeescript 外部
ruby 外部优先,可以通过声明强制使用内部

编程原则

  1. in/out 原则:签入的代码一定要比签出时更好
  2. 变量命名选用最贴切、最短的
  3. 只为当前的需求考虑设计
  4. 出现当前设计无法满足的新需求时,重构设计,使用最合理的,而不是满足需求的设计