那年榕树下

有的朋友看到这标题,肯定感叹,哎呀妈呀,这哥们也堕落了,开始写软文了。好吧,就算是软文吧,我决定东拉西扯地码一些字,不同于上周五的工作指标,这次完全是自觉自愿的。

榕树下,25号,重新上线。

我接触互联网稍微晚了一些,2002年冬天周五的一个下午(不出意外是2002年12月20号),家里终于接上了网线。我打开的第一个网站是搜狐,第二个是华军,第三个不记得了。那时已经听说了榕树下,途径大约是每季度一期的贝塔斯曼广告和天天都有的广播小说节目。

贝塔斯曼当时似乎和榕树下还是合作关系,后来收购了榕树下,这也是榕树的不幸。每一期的目录都会有热门的网络小说,那时还都是以榕树下的为主。我记得曾经买过陆幼青的书,红极一时的。

广播每晚十点左右多会有读小说的节目,安妮宝贝的文字常常是DJ们的偏好。到现在具体的内容早就不记得,但是暖暖的感觉还是值得常常温故。

这是我对互联网最初的认识,虽然我那时也不看网络小说,但在印象里,网络小说是互联网生活很重要的一部分。现在要是问我知名的作者我可能几乎都说不上来,但是看到今天网站上列出来那些名字,诸如李寻欢、安妮、宁财神、慕容雪村这样的,还是有一分陌生的熟悉。

有一天,daf同学说她看小说,我说我只知道榕树下。于是顺手搜到已经忘记了的网址,漫长的载入过程还是以一个空白的窗口告终。这些年这种场面也见得多了,没有什么大惊小怪的:大概是太久远了吧。

今天上午看路金波的博客,突然发现榕树下被收购的新闻,转而打开网站,看到了这个让人想流眼泪的页面和这个让人想擦眼泪的留言板。人们对于榕树下的情感,绝不是现在充斥着肾上腺素的起点之流可以相比的。我说这是我加入以来公司做得最好的一件事情,也希望从此榕树下对大家不再只是一些回忆而已。

那年榕树下

词:叶风 曲:马条 演唱:马条
那年榕树下 我们肩头披着细碎阳光
总会有个枝头 将青春安放
那年榕树下 我们亲手种下快乐忧伤
可以要风穿过发梢的那一种飞扬
榕树下 那年萤火虫一样飞舞盘旋
每道光的背后是怎样的世界
榕树下 那年谁其实都在谁的身边
闭上眼的云端 却是秋千
……

那年榕树下 我们肩头披着细碎阳光
总会有个枝头 将青春安放
那年榕树下 我们亲手种下快乐忧伤
可以要风穿过发梢的那一种飞扬
榕树下 那年萤火虫一样飞舞盘旋
每道光的背后是怎样的世界
榕树下 那年谁其实都在谁的身边
闭上眼的云端 却是秋千
……
……
榕树下 那年萤火虫一样飞舞盘旋
每道光的背后是怎样的世界
榕树下 那年谁其实都在谁的身边
闭上眼的云端 却是秋千
……
也许只字片语 一个不经意的标点 都能送给曾经些许温暖
……
……

上海图书馆

发现南图的资源很不错之后就想着到了上海以后要办一张借书证,眼看住这半年了,险些忘了这件事。上上周六去办证不料比截止时间晚了三分钟,白白跑了一趟。上周六正好约samson去,终于一切顺利。

办证的系统很强大,我第一次见识了二代身份证的高级功能。读卡器可以直接读取身份证里的相应信息,简化填申请表的过程。普通外借功能只需要一年100元押金即可,没有额外的收费。一次可以借5本书,还书的期限是4周。

比较遗憾的是发现在衡山路附近的上图的外借馆非常小,可选的书相对也少。计算机类的书架大约有两个,这个数量和南图是差不多的,但是长度远远没有南图的长,就是说比起南图的外借馆这个规模实在是很可怜。除去一些初级的、质量较低的书,想要刨出理想的计算机图书还是很难的,考虑到这图书馆serve着如此大的一个城市,这也是很正常的,恐怕真要看准时机才能找到目标。

幸运的是这次借到了一直没舍得买的《Unix编程艺术》,当时看到它躺在书架上时几乎是直接抢过来抓在手里的,生怕被别人捷足先登。另外找到一本Jolt获奖的《Code Quality》的译本,也是没翻先拿在手上再说。还有一本《空间数据库》是早先想买但是各大网站纷纷缺货的书,现在拿到有点物是人非的感觉。而另外一本CSS的书是拿回来用来放松的。

临走的时候还拿了一本IBM郭士纳的自传,想来咱也曾经蓝过|||

有意思的是借书的时候还被搭讪要名片。我就想起年初在南图借书的时候也有意无意地看周围的人手上拿的书,借此推断一个人的技术和背景。

从上图坐1号线一站到常熟路换新开的7号线回张江还是非常方便的,可以避开人多的人民广场站。

Using Yan in Ruby Web Application

I will show you the usage of Yan captcha service. In this tutorial, it’s based on a simple ruby web application of the Sinatra web framework.

Before we start to use the service, it is necesary to get Yan running. Download the code from the project page, then build and run it with maven:
mvn jetty:run

To enable the application to use Yan, we have to register our application to get an API Key. If you use Yan 0.3, there is a secret registration page at http://localhost:8080/yan/reg.jsp The page is protected by HTTP Basic Authentication, the username and password are store in ‘realm.properties’ which is considered to locate in the root directory. Open the file you can see the plain text username and password. If you are running the latest development version, there is no long any UI for API Key creation, but restful interface. This won’t be hard to you, pickup your tools such as curl or poster (a firefox extension) to send a HTTP request. Take curl as example, do it like this:
curl -X PUT “http://localhost:8080/yan/apikey/” -d “SinatraTestApp” -u “username:password”

If it works, you will get a line of json:
{“apikey”:”b251b0dc2eed31cac38555b61d4fa6a453923bfd”,”appName”:”SinatraTestApp”}
Save this apikey.

Sinatra is generally considered to be the world’s lightest and smallest web framework. And our application is rather simple. Just check the code:

require "rubygems"
require "sinatra"
require "net/http"
require "yaml"

apikey='b251b0dc2eed31cac38555b61d4fa6a453923bfd'

get '/' do
	conn = Net::HTTP.new('localhost', 8080)
	q = "ip=#{@env['REMOTE_ADDR']}&apikey=#{apikey}&alt=yaml&mode=0"
	resp, data = conn.get("/yan/ticket?#{q}")
	@ticket = YAML::load(data)
	haml :sinatra_captcha
end

post '/' do
	conn = Net::HTTP.new('localhost', 8080)
	q = "ip=#{@env['REMOTE_ADDR']}&apikey=#{apikey}&key=#{params['key']}&code=#{params['captcha']}"
	resp, data = conn.get("/yan/validate?#{q}")
	data
end

use_in_file_templates!
__END__

@@ sinatra_captcha
%html
	%head
		%title Yan Captcha on Sinatra
	%body
		%form{:action=>"/", :method=>"post"}
			%p
				Username:
				%input{:name=>"username", :type=>"text"}
			%p
				Password:
				%input{:name=>"password", :type=>"password"}
			%p
				Captcha:
				%img{:src=>@ticket['url']}
				%br
				%input{:name=>"captcha", :type=>"text"}
				%input{:name=>"key", :type=>"hidden", :value=>@ticket['key']}
				%input{:type=>'submit'}

There are two parts of this application: ruby code and haml. I just use in-file-template for convenience. We define a get handler and a post handler on the path ‘/’. The get handler will request a ticket from Yan which contains captcha image url and ticket key. The post handler will extract user input and submit the Yan’s validator and return user the result. And the HAML code is template for page rendering after GET request.

Maybe you need to install sinatra and some dependency:
sudo gem install sinatra haml

Run the code with a build-in WEBrick
ruby sinatra-yan.rb

Browse to the default url, test it:

For another similar tutorial using python, check Yan’s wiki page:
http://bitbucket.org/sunng/yan/wiki/SampleCode

Thank you for your support. btw, today is my dear girl friend’s birthday, I just wish her happy everyday.

Yan 0.3

经过一周的重构和开发,我的开源项目,验证码服务,打上了0.3的tag,算是一个release吧。

This release has been focusing on support for different types captcha generators. Now Yan is not only able to provide image/jpeg captcha, but also text/plain and any others.

Changeset:

  • Internal API Changes:
    • Cache API improved: new CacheItemIdentity and CacheIdStrategy were introduced in to provide convertion between cache item name and captcha info model;
    • Captcha Generator API improved: Add CaptchaGeneratorInfo to define some meta information on captcha providers (such as mode code, captcha type);
    • Captcha Data Model(CaptchaInfo) and Ticket Data Model improved: configuration parameters are separated from required parameters and has been more generic for different types of generator algorisms;
  • External API Changes:
    • Rename /image url to /captcha for better literal accuracy;
    • /ticket now supports multiple types of applicable format (plain text / json / xml / yaml);
    • /ticket now returns the mime type of the captcha generator;
    • Add a Simple-Plain-Text generator as a sample for those whose mime type is other than image/jpeg;
  • Other changes:
    • I created a new branch for next version of Yan. So the code in repository now has multiply branches, you can use ‘hg update branch-name’ to switch between difference branches;
    • The sample test page for the service (index.jsp) has been adopt to the new protocol and totally restyled.

Again, grab code from the development repository:
hg clone https://sunng@bitbucket.org/sunng/yan/

If you don’t use mercurial/hg, you can also download the tagged version from the page:
http://bitbucket.org/sunng/yan/downloads/

Just use maven to resolve dependency, build and run the project:
mvn jetty:run

Feel free to report issue :)

A button by GIMP

Inspired by a “Call to action” tutorial of Photoshop, I’d like to do the same thing with free and open source software, GIMP.

Before we start the journey, it’s better to get well prepared. Take a look at the powerful plugin “Layer Effects“. It provides you complete functionality as “Layer Style” in Photoshop does which is of critical importance in such kind of image manipulation. Follow the installation guide and I won’t spend many words about this step.

Open your gimp, create a new canvas with custom size (which makes you feel comfortable)

New a layer, use selection tool to create a rounded rectangle selection area.

Fill the selection with gradient color.

Resize the canvas. Glow the selection with 3px by menu command “selection->glow”. The crop the canvas by selection using “image->crop by selection”.

Then we apply some layer effects to beauty the button.

  • layer effects: inner glow, white 75% opacity, size 5
  • layer effects: stroke 1px

Place some text on the button such as “Sign Up”. Bold sans font(DejaVu sans or Helvetica bold) is recommended here.

Use gradient overlay effect on the text. As the layer effects plugin doesn’t support gradient overlay on text, we have to do it in a different way. First, create a new layer. Then convert text to selection with context menu command. Gradient fill the selection and hide the original text layer at last. Now we got:

Don’t stop here. Add layer effects on the new layer:

  • inner shadow, black, size 2px, distance 2px, angle 90
  • drop shadow, light, size 2px, distance 2px, angle 90

You can tune the color schema by yourself along the process :)