Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1684 lines (1209 sloc) 100 KB
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[程序猎人的博客]]></title>
<link href="http://programus.github.com/atom.xml" rel="self"/>
<link href="http://programus.github.com/"/>
<updated>2012-06-17T16:09:33+09:00</updated>
<id>http://programus.github.com/</id>
<author>
<name><![CDATA[程序猎人]]></name>
<email><![CDATA[programus@gmail.com]]></email>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[东京玩具展2012]]></title>
<link href="http://programus.github.com/blog/2012/06/17/tokyo-toys-show/"/>
<updated>2012-06-17T14:38:00+09:00</updated>
<id>http://programus.github.com/blog/2012/06/17/tokyo-toys-show</id>
<content type="html"><![CDATA[<p>东京玩具展(東京おもちゃショー)是一年一度的日本玩具界活动,本年度的玩具展是2012-06-14到2012-06-17,前两天是商家活动,后两天是对民众开放。</p>
<p>昨天,我跟妻子一起去看了一下,总的感觉是眼睛不够、时间不够、体力不够、钱不够……</p>
<!--more-->
<p>玩具展的地点在東京ビッグサイト(这地方中文叫什么呢?)。一进门,看到的就是东京玩具展的大招牌——</p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1dtzv6gatp8j.jpg" title="东京玩具展的大招牌" ></p>
<p>接下来,进到里面,还没看到什么玩具呢,就被人山人海的阵势吓了一跳</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1dtzvbycngbj.jpg" title="人山人海" ></p>
<h3>アンパンマン</h3>
<p>走到里面,先看到的是一些可爱的玩具。</p>
<p>比较让人印象深刻的是这个アンパンマン的真实版。为什么说是真实版呢?因为アンパンマン本来就是一只面包人,是豆沙馅面包人。而这里展出的正是一块大面包,闻起来味道还蛮香的。</p>
<p><img src="http://ww2.sinaimg.cn/bmiddle/4b8d30b3jw1du0dw24kkvj.jpg" title="アンパンマン真实版" ></p>
<p>同时,那里也展出了面包师傅和アンパンマン角色的灯笼。</p>
<p><img src="http://ww3.sinaimg.cn/bmiddle/4b8d30b3jw1du0dxb7zxxj.jpg" title="アンパンマン角色灯笼" ></p>
<p><img src="http://ww2.sinaimg.cn/bmiddle/4b8d30b3jw1du0dxilvcbj.jpg" title="面包师傅" ></p>
<h3>拼图</h3>
<p>再往前走,看到了一种新式拼图,拼成后会成为城市地图,与众不同的是,拼出的地图不仅仅是平面的,一些高大的标志性建筑也会出现在图中。</p>
<p><img src="http://ww4.sinaimg.cn/large/417fff62jw1du01kq4f8lj.jpg" title="城市拼图" ></p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0atwv455j.jpg" title="城市拼图侧面拍" ></p>
<h3>拼装玩具</h3>
<p>之后,看到了一种叫<a href="http://www.laq.co.jp/">LaQ</a>的拼装玩具,随手拍了一张图。</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0avxxiucj.jpg" title="LaQ" ></p>
<h3>LEGO</h3>
<p>乐高在那里也占了不小一片地方。组装了若干个乐高世界,下面是其中之一。</p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0axa41dbj.jpg" title="乐高世界之一" ></p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0axvl9qoj.jpg" title="乐高世界之二" ></p>
<h3>口袋妖怪</h3>
<p>走着走着,进入了口袋妖怪的世界,场地中摆了几辆车,实在是可爱至极。</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0ayj5sh2j.jpg" title="皮卡丘汽车背面" ></p>
<p><img src="http://ww4.sinaimg.cn/large/417fff62jw1du0azbocq2j.jpg" title="皮卡丘汽车正面" ></p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0azxy5blj.jpg" title="皮卡丘汽车侧面" ></p>
<p>其中,还有一个皮卡丘的朋友的车。</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0b0uvivtj.jpg" title="口袋妖怪车" ></p>
<h3>变形金刚</h3>
<p>走过口袋妖怪地带,就进入了变形金刚的领地了。</p>
<p>日本正在热播《Transformers Prime》,所以那里站了一个巨大的Prime版的擎天柱。</p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du03b0ecebj.jpg"></p>
<p>进去之后,看到了即将推出的MasterPiece系列之12、13、14的介绍</p>
<p>MP-12的横炮相关,可以看<a href="http://www.tfg2.com/read.php?tid-41812.html">这篇帖子</a>。</p>
<p>MP-13的声波,因为是灰模,拍摄效果又不好,就不在这里贴了,可以去我的微博上找。</p>
<h3>超级英雄皮</h3>
<p>之后,看到了卖超级英雄皮的地方。</p>
<p>先是一个性感的女蜘蛛侠皮。</p>
<p><img src="http://ww4.sinaimg.cn/large/417fff62jw1du0c1ylt5nj.jpg"></p>
<p>然后是各种常见的英雄服。</p>
<p><img src="http://ww4.sinaimg.cn/large/417fff62jw1du0c7i04n5j.jpg"></p>
<h3>机器人和鸭子</h3>
<p>再走一段,看到一个纸质的机器人。</p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0c9bcwuij.jpg"></p>
<p>和一群遥控鸭子。</p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0caaj8fwj.jpg"></p>
<h3>奥特曼、高达、海盗王</h3>
<p>终于进入了另一大玩具商BANDAI的领地。少不了高达和海盗王。在那之前,先上一张一人高的奥特曼图。</p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0cbczdq1j.jpg"></p>
<p>然后是海盗王玩具。</p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0d74gg7qj.jpg"></p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0d7r3pboj.jpg"></p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0d74gg7qj.jpg"></p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0d7r3pboj.jpg"></p>
<p>和高达的玩具。</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0dzfsg4tj.jpg"></p>
<h3>マジンガーZ</h3>
<p>这是日本曾经一度很有名的机器人动画片的主角,只是在中国罕有人知。出了一版靓丽无比的玩具。</p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0cyznjlwj.jpg"></p>
<p><img src="http://ww4.sinaimg.cn/large/417fff62jw1du0cztmn9qj.jpg"></p>
<h3>圣斗士星矢</h3>
<p>其实我本人不怎么迷圣斗士,但微博里有人喜欢,所以就顺手拍了几张照片出来。</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0d10rmxdj.jpg"></p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0d4w6lnqj.jpg"></p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0d5v6435j.jpg"></p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0d6gqphnj.jpg"></p>
<h3>汉字变形玩具</h3>
<p>BANDAI大概在两三年前推出了一款汉字变形玩具(もじバケる)。在今年的玩具展上也将全系列的内容展示了出来。</p>
<p>第一套</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0cqd9am6j.jpg"></p>
<p>第二套</p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0cqx642ej.jpg"></p>
<p>第三套</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0criy3d2j.jpg"></p>
<p>第四套</p>
<p><img src="http://ww1.sinaimg.cn/large/417fff62jw1du0cs83cssj.jpg"></p>
<p>第五套(这一套目前还能买到,我就买了一套)</p>
<p><img src="http://ww2.sinaimg.cn/large/417fff62jw1du0ctae8wwj.jpg"></p>
<p>另外,还有一些新系列,因为网上广告挺多的,我就没拍。</p>
<p>最后放一张动物变形玩具变形后的场景图。</p>
<p><img src="http://ww3.sinaimg.cn/large/417fff62jw1du0cu150t3j.jpg"></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[在Windows上使用mocha对基于Node.JS的服务器端CoffeeScript进行测试并通过jscoverage生成覆盖率报告]]></title>
<link href="http://programus.github.com/blog/2012/05/26/coffee-mocha-coverage-node-under-windows/"/>
<updated>2012-05-26T22:00:00+09:00</updated>
<id>http://programus.github.com/blog/2012/05/26/coffee-mocha-coverage-node-under-windows</id>
<content type="html"><![CDATA[<p>最近在捣鼓一个基于<a href="http://nodejs.org" title="Node.JS">Node.JS</a>的东西,语言自然是JavaScript了。但后来发现了<a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>,发现确实简便很多,于是变节到了<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>阵营。</p>
<p>写了两个小模块,忽然想到要测试。最初找到了<a href="http://pivotal.github.com/jasmine/" title="Jasmine">Jasmine</a>,后来又发现了<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>。经过一番比较斟酌,觉得既然配咖啡(<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>),自然还是得摩卡(<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>)。所以最终决定使用<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>来做测试工具。</p>
<p>好吧,实际原因是在<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>的主页上看到它支持代码覆盖率检查。后来经过各种折腾才总算搞定了这个覆盖率检查以及报告的查看问题。</p>
<p>其实,这一切在Linux上应该是非常简单的,但我手上只有Windows,所以一波三折。介于网上相关的信息有些零散,并且不够傻瓜,这里做个总结,也算给自己留个笔记吧。</p>
<!-- more -->
<h2>安装必需品</h2>
<p>要做这一切,首先上面提到的各种需要的软件是一定要先安装的,下面就一个一个来介绍一下。</p>
<h3>Node.JS</h3>
<p>这个不消说,一切都是基于<a href="http://nodejs.org" title="Node.JS">Node.JS</a>之上的,所以绝对是第一等必需品。<a href="http://nodejs.org" title="Node.JS">Node.JS</a>发展到今天,安装也很简单,从主页上下载Windows版,安装即可。</p>
<p>安装后,打开命令行窗口,执行</p>
<pre><code>node --version
</code></pre>
<p>看看是不是正确输出了版本号?如果是,恭喜,安装成功了!如果没有……自己上网找找解决方案吧。这个不在本文的讨论范围之内。</p>
<h4>NPM</h4>
<p>另外,<a href="http://nodejs.org" title="Node.JS">Node.JS</a>的包管理工具<a href="http://search.npmjs.org/" title="NPM Registry">NPM</a>也应该随着上面的安装一同安装好。可以执行以下命令来验证。</p>
<p>查看版本号</p>
<pre><code>npm --version
</code></pre>
<p>或者,查看帮助</p>
<pre><code> npm --help
</code></pre>
<h3>CoffeeScript</h3>
<p><a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>是一种对JavaScript的简化,它极大程度上地减少了JavaScript里面的各种括号和分号。最终还是要编译成JavaScript执行的。</p>
<p>具体的区别和例子,可以到<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>的主页上去瞅瞅。一目了然。至于安装,在那里也写得<a href="http://coffeescript.org/#installation">一清二楚</a>。介于都是英文,这里还是做一下简单的介绍吧。</p>
<p>首先,<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>是<a href="http://nodejs.org" title="Node.JS">Node.JS</a>的一个包,所以就可以用<a href="http://search.npmjs.org/" title="NPM Registry">NPM</a>来很方便地进行安装。命令如下:</p>
<pre><code>npm install -g coffee-script
</code></pre>
<p>这里为什么有个<code>-g</code>?<code>-g</code>的意思是<code>install globally</code>,也就是全局安装。其实说白了就是安装后的包中所带的命令可以直接在命令行执行而不需要去指定所在的目录。比如,按照上面所写的带有<code>-g</code>参数的命令安装后,可以在命令行的任意目录下执行</p>
<pre><code>coffee --version
</code></pre>
<p>来查看版本。如果上面命令执行成功了,恭喜你!<a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>已经安装好了。</p>
<h3>mocha</h3>
<p><a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>是针对JavaScript的一个测试工具。具体细节请自行参看主页。英文啃不动,请自行寻找辞典。外语学习等有空我另撰文来讨论。</p>
<p>言归正传,<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>也是<a href="http://nodejs.org" title="Node.JS">Node.JS</a>的一个包,所以……对了,你很聪明,用<a href="http://search.npmjs.org/" title="NPM Registry">NPM</a>来解决!(什么?你没想到?没关系,跟我一样有前途。)</p>
<pre><code>npm install -g mocha
</code></pre>
<p>因为<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>也是个需要执行命令的工具,所以我们继续加上<code>-g</code>。</p>
<p>这里顺便说一下<strong><code>-g</code>什么时候用</strong>。如果所要安装的包是以命令的方式被使用,也就是会通过命令行调用其中已经做好的工具命令,就最好加上<code>-g</code>。这样,在命令行下随时可以使用那些命令。如果所要安装的包是需要在代码中被包含,也就是<code>require('xxx')</code>,则不要加<code>-g</code>,而且必须在你的程序所在的目录或者上级的某个目录下执行<code>npm install</code>。</p>
<p>跑题了,现在跑回来。验证<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>安装成功的方法……或许你已经猜到了,就是下面这个带有万能验证参数<code>--version</code>的命令。</p>
<pre><code>mocha --version
</code></pre>
<p>至此,你已经可以写<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>代码并进行测试了。执行</p>
<pre><code>mocha --compilers coffee:coffee-script
</code></pre>
<p><a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>就会自动运行<code>test</code>目录下的测试用例代码进行测试,并给出漂亮的结果报告。</p>
<p>对不起,我说早了。如果你写了测试用例,或者手快,从这个博客的下面的内容里复制了测试用例,那么运行上述命令后,十有八九是会出现类似下面这样的错误。</p>
<pre><code>node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot find module 'chai'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
........(以下省略若干行)
</code></pre>
<p>原因是我们缺少测试时必须的检验库(assertion library),下面就来说一下检验包的安装。(如果你没有写任何测试用例,上面的命令不会出错,但为了后面不折腾,还是跟着下面的内容安装一下为好。)</p>
<h3>should.js / expect.js / chai</h3>
<p>使用<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>进行测试,需要自备数据检验库。数据检验库的作用是用来验证期待值与实际值一致与否的。<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>支持的检验库有如下三种:</p>
<ul>
<li><a href="https://github.com/visionmedia/should.js">should.js</a>:使用 <em>被检验值</em>.should.<em>检验方法</em>(<em>期待值</em>) 的语法进行验证。</li>
<li><a href="https://github.com/LearnBoost/expect.js">expect.js</a>:使用 expect(<em>被检验值</em>).to.<em>检验方法</em>(<em>期待值</em>) 的语法进行验证。</li>
<li><a href="http://chaijs.com/">chai</a>:支持以上两种语法和 assert.<em>检验方法</em>(<em>被检验值</em>, <em>期待值</em>) 的语法。</li>
</ul>
<p>在网上查找的结果发现should.js有诸多不便,又因为chai包罗万象,所以我决定采用chai来进行自己的测试。</p>
<p>安装方法在各个检验库的主页中都有所描述。而且,既然是基于<a href="http://nodejs.org" title="Node.JS">Node.JS</a>的库,自然需要祭出<a href="http://search.npmjs.org/" title="NPM Registry">NPM</a>这一神器。以chai为例,命令如下:</p>
<pre><code>npm install chai
</code></pre>
<p>因为我们在测试用例代码中要引入这个库(或者说包),所以这里没有加<code>-g</code>参数。另外,执行此命令的目录要在项目的根目录下,这样就可以在项目下的任何目录的代码中引入它了。</p>
<p>安装成功后,会在目录下建一个名为<code>node_modules</code>的目录。这个目录是专门用来放各种依赖包的。要确认安装了哪些包,可以用</p>
<pre><code>npm list
</code></pre>
<p>来查看。执行上面命令,如果你看到类似<code>chai@1.0.1</code>的内容,说明安装成功了。<code>@</code>后面是版本号。</p>
<p>另外,为了便于管理依赖包,也可以使用<code>package.json</code>文件进行记录和管理,然后使用不带参数的<code>npm install</code>命令自动安装所有缺失的包。</p>
<p>我们这个例子的<code>package.json</code>文件如下:</p>
<figure class='code'><figcaption><span>package.json </span><a href='https://github.com/programus/coffee-mocha-nodejs-coverage-windows-example/blob/master/package.json'>github-source </a></figcaption> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;coffee-mocha-nodejs-coverage-windows-example&quot;</span>
</span><span class='line'> <span class="p">,</span> <span class="nt">&quot;version&quot;</span><span class="p">:</span> <span class="s2">&quot;0.0.1&quot;</span>
</span><span class='line'> <span class="p">,</span> <span class="nt">&quot;private&quot;</span><span class="p">:</span> <span class="kc">true</span>
</span><span class='line'> <span class="p">,</span> <span class="nt">&quot;dependencies&quot;</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'> <span class="nt">&quot;chai&quot;</span><span class="p">:</span> <span class="s2">&quot;1.0.1&quot;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>其中<code>dependencies</code>中就记录了依赖的包和版本号。</p>
<p>装好了<code>chai</code>,测试应该就不成问题了。</p>
<p><strong>然而!</strong></p>
<p>然而我们的重点在于覆盖率如何得到。我最初就是卡在这里了。<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>的文档中虽然提到一个<code>html-cov</code>的<strong>reporter</strong>,但执行<code>mocha --compiler coffee:coffee-script -R html-cov</code>得到的覆盖率永远都是0%,而且所执行过的代码也没有标记。仔细看看文档,发现有这么一段话:</p>
<blockquote><p>The library being tested should first be instrumented by <a href="https://github.com/visionmedia/node-jscoverage" title="node-jscoverage">node-jscoverage</a>, this allows Mocha to capture the coverage information necessary to produce a single-page HTML report.</p></blockquote>
<p>也就是说,要想导出覆盖率报告,那么被测试的代码必须是被<a href="https://github.com/visionmedia/node-jscoverage" title="node-jscoverage">node-jscoverage</a>“蹂躏”过的。跟着链接看了一眼node-jscoverage,居然是个需要在Linux下面编译的东西。这可让我这个在Windows下忍辱负重的家伙如何是好?</p>
<p>没关系,<a href="https://github.com/visionmedia/node-jscoverage" title="node-jscoverage">node-jscoverage</a>再强大,充其量不过是<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">jscoverage</a>的加强版。所以,我们直接去找它的本家<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">jscoverage</a>去。到下载页一瞧,本家果然够意思,已经有了编译好的Windows压缩包。那么现在说一下下一个需要安装的软件——</p>
<h3>JSCoverage</h3>
<p>仔细读读文档,会发现<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">JSCoverage</a>的工作原理是把你写好的JavaScript程序给加一层“壳”,加壳的程序的执行结果与原本的程序相同,但这层壳会在代码执行时记录执行过的代码,从而最终统计出代码覆盖率。</p>
<p>又跑题了,拉回来,继续说安装的事儿。在<a href="http://siliconforks.com/jscoverage/download.html">下载页</a>的下半部分可以找到编译好的Windows执行程序。下载之,然后解压缩到一个适当的目录(为了减少未来的麻烦,不建议目录名中带有空格)。再接下来,为了可以在命令行中执行<code>jscoverage</code>,将可执行文件所在的目录加入环境变量的<code>PATH</code>里面。系统环境变量的修改,请参考<a href="http://baike.baidu.com/view/95930.htm">这里</a>的<a href="http://baike.baidu.com/albums/95930/95930/0/0.html#0$d019d2bf676f0a3418d81f09">这个图</a>。</p>
<p>做好上述工作后,在命令行窗口里执行我们的万能验证命令</p>
<pre><code>jscoverage --version
</code></pre>
<p>什么?出错了?我没说修改了环境变量之后,需要重新打开新的命令行窗口才有效吗?没说?真的没说?那好吧,现在重新启动命令行窗口再试试。</p>
<h2>组织代码</h2>
<h3>业务代码</h3>
<p>至此,需要的软件都安装好了。下面就该写我们的代码了。为了今后处理方便,建议将代码写在<code>lib</code>目录下。原因?据说就是一种约定俗成(据说是因为从网上查到的,不是我说的)。</p>
<p>比如,我写了如下代码(一个问候者):</p>
<figure class='code'><figcaption><span>lib/models/index.coffee </span><a href='https://github.com/programus/coffee-mocha-nodejs-coverage-windows-example/blob/master/lib/models/index.coffee'>github-source </a></figcaption> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="k">class</span> <span class="nx">Greeter</span>
</span><span class='line'> <span class="nv">constructor: </span><span class="nf">(@lang) -&gt;</span>
</span><span class='line'> <span class="k">if</span> <span class="o">!</span><span class="nx">@lang</span><span class="o">?</span>
</span><span class='line'> <span class="vi">@lang = </span><span class="s">&#39;en&#39;</span>
</span><span class='line'>
</span><span class='line'> <span class="nv">sayHello: </span><span class="nf">(name) -&gt;</span>
</span><span class='line'> <span class="k">switch</span> <span class="nx">@lang</span>
</span><span class='line'> <span class="k">when</span> <span class="s">&#39;jp&#39;</span>
</span><span class='line'> <span class="c1"># nameさん、こんにちは。</span>
</span><span class='line'> <span class="s">&quot;</span><span class="si">#{</span><span class="k">if</span> <span class="nx">name</span><span class="o">?</span> <span class="k">then</span> <span class="s">&quot;</span><span class="si">#{</span><span class="nx">name</span><span class="si">}</span><span class="s">\u3055\u3093\u3001&quot;</span> <span class="k">else</span> <span class="s">&quot;&quot;</span><span class="si">}</span><span class="s">\u3053\u3093\u306B\u3061\u306F\u3002&quot;</span>
</span><span class='line'> <span class="k">when</span> <span class="s">&#39;zh&#39;</span>
</span><span class='line'> <span class="c1"># name,你好!吃了没?</span>
</span><span class='line'> <span class="s">&quot;</span><span class="si">#{</span><span class="k">if</span> <span class="nx">name</span><span class="o">?</span> <span class="k">then</span> <span class="s">&quot;</span><span class="si">#{</span><span class="nx">name</span><span class="si">}</span><span class="s">\uFF0C&quot;</span> <span class="k">else</span> <span class="s">&quot;&quot;</span><span class="si">}</span><span class="s">\u4F60\u597D\uFF01\u5403\u4E86\u6CA1\uFF1F&quot;</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="s">&quot;Hello</span><span class="si">#{</span><span class="k">if</span> <span class="nx">name</span><span class="o">?</span> <span class="k">then</span> <span class="s">&quot;, </span><span class="si">#{</span><span class="nx">name</span><span class="si">}</span><span class="s">&quot;</span> <span class="k">else</span> <span class="s">&quot;&quot;</span><span class="si">}</span><span class="s">!&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="nv">exports.Greeter = </span><span class="nx">Greeter</span>
</span></code></pre></td></tr></table></div></figure>
<p>放在<code>lib/models</code>下,并命名为<code>index.coffee</code>。命名为index,在引入模块的时候可以直接以目录名来引入。</p>
<h3>测试用例代码</h3>
<p>代码写好了,接下来写一下测试用例的代码。之前提过,<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>会自动处理<code>test</code>目录下的内容,所以测试用例代码我们都放在<code>test</code>目录下。</p>
<p>我们的<code>Greeter</code>类有两个函数:构造函数和<code>sayHello()</code>函数。而<code>sayHello()</code>函数的参数又是可选的。因此测试代码如下:</p>
<figure class='code'><figcaption><span>test/models.test.coffee </span><a href='https://github.com/programus/coffee-mocha-nodejs-coverage-windows-example/blob/master/test/models.test.coffee'>github-source </a></figcaption> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="c1"># Test suites for all models</span>
</span><span class='line'><span class="nv">chai = </span><span class="nx">require</span> <span class="s">&#39;chai&#39;</span>
</span><span class='line'><span class="nv">should = </span><span class="nx">chai</span><span class="p">.</span><span class="nx">should</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="nv">models = </span><span class="nx">require</span><span class="p">(</span><span class="s">&#39;../req&#39;</span><span class="p">)</span> <span class="s">&#39;models&#39;</span>
</span><span class='line'><span class="nv">Greeter = </span><span class="nx">models</span><span class="p">.</span><span class="nx">Greeter</span>
</span><span class='line'>
</span><span class='line'><span class="nv">langs = </span><span class="p">[</span>
</span><span class='line'> <span class="s">&#39;zh&#39;</span>
</span><span class='line'> <span class="s">&#39;en&#39;</span>
</span><span class='line'> <span class="s">&#39;jp&#39;</span>
</span><span class='line'><span class="p">]</span>
</span><span class='line'>
</span><span class='line'><span class="nv">hellos =</span>
</span><span class='line'> <span class="nv">zh: </span><span class="s">&#39;你好!吃了没?&#39;</span>
</span><span class='line'> <span class="nv">en: </span><span class="s">&#39;Hello!&#39;</span>
</span><span class='line'> <span class="nv">jp: </span><span class="s">&#39;こんにちは。&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="nv">hellosWithName =</span>
</span><span class='line'> <span class="nv">zh:</span>
</span><span class='line'> <span class="nv">name: </span><span class="s">&#39;老舍&#39;</span>
</span><span class='line'> <span class="nv">greet: </span><span class="s">&#39;老舍,你好!吃了没?&#39;</span>
</span><span class='line'> <span class="nv">en:</span>
</span><span class='line'> <span class="nv">name: </span><span class="s">&#39;Jack&#39;</span>
</span><span class='line'> <span class="nv">greet: </span><span class="s">&#39;Hello, Jack!&#39;</span>
</span><span class='line'> <span class="nv">jp:</span>
</span><span class='line'> <span class="nv">name: </span><span class="s">&#39;武蔵&#39;</span>
</span><span class='line'> <span class="nv">greet: </span><span class="s">&#39;武蔵さん、こんにちは。&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="nx">describe</span> <span class="s">&#39;Greeter&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="nx">describe</span> <span class="s">&#39;#constructor()&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="nx">it</span> <span class="s">&#39;should have language set&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="k">new</span> <span class="nx">Greeter</span><span class="p">(</span><span class="nx">l</span><span class="p">).</span><span class="nx">lang</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">l</span><span class="p">)</span> <span class="k">for</span> <span class="nx">l</span> <span class="k">in</span> <span class="nx">langs</span>
</span><span class='line'> <span class="nx">it</span> <span class="s">&#39;should become default language, en&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="k">new</span> <span class="nx">Greeter</span><span class="p">().</span><span class="nx">lang</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="s">&#39;en&#39;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'> <span class="nx">describe</span> <span class="s">&#39;#sayHello()&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="nx">it</span> <span class="s">&#39;should greet without name&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="k">new</span> <span class="nx">Greeter</span><span class="p">(</span><span class="nx">l</span><span class="p">).</span><span class="nx">sayHello</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">hellos</span><span class="p">[</span><span class="nx">l</span><span class="p">])</span> <span class="k">for</span> <span class="nx">l</span> <span class="k">in</span> <span class="nx">langs</span>
</span><span class='line'> <span class="nx">it</span> <span class="s">&#39;should greet with name&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
</span><span class='line'> <span class="k">new</span> <span class="nx">Greeter</span><span class="p">(</span><span class="nx">l</span><span class="p">).</span><span class="nx">sayHello</span><span class="p">(</span><span class="nx">hellosWithName</span><span class="p">[</span><span class="nx">l</span><span class="p">].</span><span class="nx">name</span><span class="p">).</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">hellosWithName</span><span class="p">[</span><span class="nx">l</span><span class="p">].</span><span class="nx">greet</span><span class="p">)</span> <span class="k">for</span> <span class="nx">l</span> <span class="k">in</span> <span class="nx">langs</span>
</span></code></pre></td></tr></table></div></figure>
<p>细心的人可能发现我最上面写的测试目标的部分和常规写法不太一样。通常会写作</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nv">models = </span><span class="nx">require</span> <span class="s">&#39;../lib/models&#39;</span>
</span></code></pre></td></tr></table></div></figure>
<p>而我这里写成了</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nv">models = </span><span class="nx">require</span><span class="p">(</span><span class="s">&#39;../req&#39;</span><span class="p">)</span> <span class="s">&#39;models&#39;</span>
</span></code></pre></td></tr></table></div></figure>
<p>为什么呢?这就涉及到我为<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>的测试写的一个小工具——</p>
<h3>测试辅助工具</h3>
<p>这个工具其实很简单,没几行:</p>
<figure class='code'><figcaption><span>req.coffee </span><a href='https://github.com/programus/coffee-mocha-nodejs-coverage-windows-example/blob/master/req.coffee'>github-source </a></figcaption> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nv">module.exports = </span><span class="nf">(path)-&gt;</span>
</span><span class='line'> <span class="k">try</span>
</span><span class='line'> <span class="nx">require</span> <span class="s">&quot;./lib-cov/</span><span class="si">#{</span><span class="nx">path</span><span class="si">}</span><span class="s">&quot;</span>
</span><span class='line'> <span class="k">catch</span> <span class="nx">e</span>
</span><span class='line'> <span class="nx">require</span> <span class="s">&quot;./lib/</span><span class="si">#{</span><span class="nx">path</span><span class="si">}</span><span class="s">&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>作用就是检查在<code>./lib-cov</code>目录下是否有我们所需的模块,如果有,则导入之,如果没有,则导入<code>./lib</code>下的响应模块。</p>
<p>刚才说过,要把代码组织到<code>lib</code>目录(因为req.coffee放在项目根目录下,<code>.</code>又是当前目录,所以这里的<code>lib</code>跟上文的<code>./lib</code>是一会儿事儿)下。我们又说过,<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">JSCoverage</a>计算覆盖率的做法是将代码加个壳,然后让测试程序调用加壳后的代码。这个加壳后的代码就被存储在<code>lib-cov</code>目录下(当然是可以自己指定的目录)。所以,使用这个<code>req.coffee</code>就可以让<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>在有<code>lib-cov</code>目录的情况下使用<code>lib-cov</code>下的内容进行测试,从而为生成覆盖率报告做好准备。</p>
<p>在网上搜索,可以看到大多数解决方案是让设置一个环境变量<code>XXX_COV</code>为1来达到上述目的。而且是以类似下面的样子,将其写到<code>makefile</code>里面</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='make'><span class='line'> <span class="nv">XXX_COV</span><span class="o">=</span>1 mocha --compilers coffee:coffee-script -R html-cov &gt; coverage.html
</span></code></pre></td></tr></table></div></figure>
<p>同时在测试代码中检查环境变量</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'> <span class="nv">models = </span><span class="nx">require</span> <span class="k">if</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">XXX_COV</span> <span class="k">then</span> <span class="s">&#39;../lib-cov/models&#39;</span> <span class="k">else</span> <span class="s">&#39;../lib/models&#39;</span>
</span></code></pre></td></tr></table></div></figure>
<p>但经试验,如上<code>makefile</code>中设定环境变量的语句在Windows下无效。故此追加了这样一个测试辅助工具。同时,在测试时保证每次生成完覆盖率报告都删除<code>lib-cov</code>目录,就可以正常使用了。</p>
<h2>进行测试</h2>
<p>到这里,我们的准备工作都基本完成了,下一步就是开始进行测试了。</p>
<p>比起上面的各种折腾,测试倒是显得非常容易。</p>
<h3>单纯测试</h3>
<p>先说一下单纯的测试。</p>
<p>其实上面也曾提到过,只需要执行如下一条命令,就可以得到结果:</p>
<pre><code>mocha --compilers coffee:coffee-script
</code></pre>
<p>用这条命令测试<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>的代码,不需要格外进行编译。(据说老版本的<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>里自动支持<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>,不需要指定后面的一长串参数。)</p>
<p>测试后,应该会有如下输出:</p>
<div style="background-color:black;font-family:monospace;font-size:13px;">
<code>
<div></div>
<div style="color:#848284">&#160;&#160;....</div>
<div></div>
<div>&#160;&#160;<span style="color:#0f0">✔</span><span style="color:#008200"> 4 tests complete </span><span style="color:#848284">(6ms)</span></div>
<div></div>
</code>
</div>
<p>测试成功!</p>
<h3>代码覆盖率报告</h3>
<p>接下来说一下重头戏——如何产生代码覆盖率的报告。</p>
<p><a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>生成报告只需要指定相应的reporter即可。但前提是需要使用<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">JSCoverage</a>处理一下代码。</p>
<p><a href="http://siliconforks.com/jscoverage/" title="JSCoverage">JSCoverage</a>,顾名思义,只能处理JavaScript的代码,对<a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>目前还是视而不见的。故而,我们首先要将<a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>编译成JavaScript:</p>
<pre><code>coffee -o lib-js -c lib
</code></pre>
<p>这条命令,会递归地将<code>lib</code>目录下的所有<a href="http://coffeescript.org/" title="CoffeeScript">coffee</a>代码编译成js文件,并以同样的目录结构存储到<code>lib-js</code>目录中。</p>
<p>比如,我的<code>lib</code>目录中的文件目前如下(使用<code>tree /f</code>命令生成的结果,根目录内容有部分删节):</p>
<pre><code>\coffee-mocha-nodejs-coverage-windows-example\lib
└─models
index.coffee
</code></pre>
<p>编译后的<code>lib-js</code>目录则如下:</p>
<pre><code>\coffee-mocha-nodejs-coverage-windows-example\lib-js
└─models
index.js
</code></pre>
<p>当然,你也可以去掉<code>-o lib-js</code>的部分,将js文件编译到<a href="http://coffeescript.org/" title="CoffeeScript">coffee</a>文件的身边。我倾向于把他们分清楚,所以分了一下目录。</p>
<p>有了js文件,就可以使用<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">JSCoverage</a>了。命令如下:</p>
<pre><code>jscoverage --no-highlight lib-js lib-cov
</code></pre>
<p>这样就将<code>lib-js</code>目录里的js文件处理好并放进了<code>lib-cov</code>目录。至于那个<code>--no-highlight</code>参数,是为了去掉代码高亮的。如果不去掉,控制高亮的HTML代码会在最终报告中被转义,导致代码严重不可读。(不服你自己试试就知道了。)</p>
<p>现在,我们生成覆盖率报告的材料都准备好了,执行最终的测试命令:</p>
<pre><code>mocha --compilers coffee:coffee-script -R html-cov &gt; coverage.html
</code></pre>
<p>结束后,打开<code>coverage.html</code>文件,就可以看到最终的报告了。</p>
<p>如果你的代码是从上面的博客内容中复制的,那么覆盖率应该是100%。覆盖率报告的样子应该与此大致相同:<a href="http://programus.github.com/downloads/pages/coverage.html">coverage.html</a></p>
<p>如果你对报告有所怀疑,可以将测试用例中的一部分<code>it</code>改成<code>xit</code>(<code>xit</code>代表此条不进行测试),然后再次运行上述命令试试看。</p>
<p>至此,我们实现了在Windows上使用<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>对基于<a href="http://nodejs.org" title="Node.JS">Node.JS</a>的服务器端<a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>进行测试并通过<a href="http://siliconforks.com/jscoverage/" title="JSCoverage">JSCoverage</a>生成覆盖率报告。</p>
<p><strong>但是……</strong></p>
<p>会不会觉得上面那么多命令,每次敲来敲去会让手指发疼,关节发麻,有腱鞘炎倾向?没关系,我们仍然有更好的解决方案——</p>
<h2>使用make</h2>
<p><code>make</code>是Linux下面的著名命令。用来方便地批量地完成编译、安装等一系列工作。说实话,我对<code>make</code>也不是了解太多,虽然以前有所接触,但基本都是这次研究这个测试问题才开始真正学习了一些。</p>
<p>如果你要搜索<a href="http://visionmedia.github.com/mocha/" title="mocha">mocha</a>的覆盖率测试,估计很多结果中都提到了在<code>makefile</code>中如何动手脚的文章。由于不懂<code>make</code>到底是什么,被误导了不少,后来才明白,文中提到的<code>makefile</code>其实都是他们自己写的文件。有了那神奇的<code>makefile</code>,就可以用非常简单的命令来批量执行上面的各种或长或短的命令了。</p>
<h3>安装make</h3>
<p>我使用的是<a href="http://gnuwin32.sourceforge.net/packages/make.htm">GNU Make for Windows</a>。</p>
<p>为了使用<a href="http://octopress.org" title="Octopress">Octopress</a>这套博客系统,之前安装了<a href="http://www.ruby-lang.org" title="Ruby">Ruby</a>,里面自带了<code>make</code>,所以我就没有安装。</p>
<p>如果你要独立安装<code>make</code>,可以到<a href="http://gnuwin32.sourceforge.net/packages/make.htm">主页</a>上寻找安装程序,然后安装之。再然后,用万能确认命令确认:</p>
<pre><code>make --version
</code></pre>
<p>如果出错,很可能是环境变量的<code>PATH</code>没有设置好,手动设置一下即可。</p>
<h3>撰写makefile</h3>
<p>安装好<code>make</code>后,就需要撰写<code>makefile</code>了。这是个挺麻烦的事儿。因为很多Windows的命令都不被支持。好在你看到这篇文章时,已经有我这个大善人写好了一份现成的<code>makefile</code>了。你只需复制或者下载即可,内容如下:</p>
<figure class='code'><figcaption><span>makefile </span><a href='https://github.com/programus/coffee-mocha-nodejs-coverage-windows-example/blob/master/makefile'>github-source </a></figcaption> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
</pre></td><td class='code'><pre><code class='make'><span class='line'> <span class="nv">REPORT_FILE</span><span class="o">=</span>coverage.html
</span><span class='line'> <span class="nv">RMJS</span><span class="o">=</span>rmdir.js
</span><span class='line'> <span class="nv">RMV</span><span class="o">=</span>node <span class="k">$(</span>RMJS<span class="k">)</span>
</span><span class='line'><span class="c"> # change this if using linux or others</span>
</span><span class='line'> <span class="nv">BROWSE</span><span class="o">=</span>start
</span><span class='line'>
</span><span class='line'> all: | <span class="nb">test</span>-all coverage
</span><span class='line'>
</span><span class='line'><span class="c"> # use this to cross platform</span>
</span><span class='line'> <span class="k">$(</span>RMJS<span class="k">)</span>:
</span><span class='line'> @echo <span class="s1">&#39;var f=require(&quot;fs&quot;),t=require(&quot;path&quot;);var r=function(a){var b=f.readdirSync(a);for(var c=0;c&lt;b.length;c++){var d=b[c];if(d!==&quot;.&quot;&amp;&amp;d!=&quot;..&quot;){d=t.join(a,d);if(f.statSync(d).isDirectory()){r(d)}else{try{f.unlinkSync(d)}catch(e){}}}}try{f.rmdirSync(a)}catch(e){}};try{r(process.argv[2])}catch(e){}&#39;</span> &gt; <span class="k">$(</span>RMJS<span class="k">)</span>
</span><span class='line'>
</span><span class='line'><span class="c"> # use this to cross platform</span>
</span><span class='line'> rmtools:
</span><span class='line'> @coffee -e <span class="s2">&quot;require(&#39;fs&#39;).unlink &#39;rmdir.js&#39;&quot;</span>
</span><span class='line'>
</span><span class='line'> <span class="nb">test</span>-all:
</span><span class='line'> @echo <span class="s1">&#39;Testing...&#39;</span>
</span><span class='line'> @mocha --compilers coffee:coffee-script
</span><span class='line'>
</span><span class='line'> compile-coffee: <span class="k">$(</span>RMJS<span class="k">)</span>
</span><span class='line'> @<span class="k">$(</span>RMV<span class="k">)</span> lib-js
</span><span class='line'> @echo <span class="s1">&#39;CoffeeScript -&gt; JavaScript compiling...&#39;</span>
</span><span class='line'> @coffee -o lib-js -c lib
</span><span class='line'> @<span class="k">$(</span>RMV<span class="k">)</span> -p
</span><span class='line'>
</span><span class='line'> clean-compile:
</span><span class='line'> @<span class="k">$(</span>RMV<span class="k">)</span> -p
</span><span class='line'>
</span><span class='line'> jscoverage:
</span><span class='line'> @jscoverage --no-highlight lib-js lib-cov
</span><span class='line'>
</span><span class='line'> mocha-html-cov:
</span><span class='line'> @echo <span class="s1">&#39;Testing and generating coverage report...&#39;</span>
</span><span class='line'> @mocha --compilers coffee:coffee-script -R html-cov &gt; <span class="k">$(</span>REPORT_FILE<span class="k">)</span>
</span><span class='line'>
</span><span class='line'> clean-coverage:
</span><span class='line'> @<span class="k">$(</span>RMV<span class="k">)</span> lib-js
</span><span class='line'> @<span class="k">$(</span>RMV<span class="k">)</span> lib-cov
</span><span class='line'>
</span><span class='line'> open-coverage:
</span><span class='line'> @echo <span class="s1">&#39;Openning report in your default browser...&#39;</span>
</span><span class='line'> @<span class="k">$(</span>BROWSE<span class="k">)</span> <span class="k">$(</span>REPORT_FILE<span class="k">)</span>
</span><span class='line'>
</span><span class='line'> clean-report:
</span><span class='line'> @<span class="k">$(</span>RMV<span class="k">)</span> <span class="k">$(</span>REPORT_FILE<span class="k">)</span>
</span><span class='line'>
</span><span class='line'> compile: | <span class="k">$(</span>RMJS<span class="k">)</span> compile-coffee rmtools
</span><span class='line'>
</span><span class='line'> coverage: | <span class="k">$(</span>RMJS<span class="k">)</span> compile-coffee jscoverage mocha-html-cov clean-coverage rmtools open-coverage
</span><span class='line'>
</span><span class='line'> clean: | <span class="k">$(</span>RMJS<span class="k">)</span> clean-compile clean-coverage clean-report rmtools
</span><span class='line'>
</span><span class='line'> .PHONY: all <span class="nb">test</span>-all compile-coffee clean-compile jscoverage mocha-html-cov clean-coverage open-coverage clean-report compile coverage clean rmtoos
</span></code></pre></td></tr></table></div></figure>
<p>虽说我是个大善人,但毕竟也是第一次写<code>makefile</code>,如果你是懂行的,发现了不足之处,请一定留言告诉我。</p>
<p>这里,有几点简单说明一下。</p>
<ul>
<li><p>一个是<code>RMJS</code>这个东西。这是一个递归删除目录的js文件,在<code>makefile</code>中通过<code>echo</code>命令将内容写到文件中,并在用完后删除。之所以这么做,是因为无论是Windows的<code>rd</code>命令还是Linux的<code>rm</code>命令(当然是for Windows版本)都无法很好地删除目录。Windows的<code>rd</code>命令的问题是,<code>make</code>貌似不识别它,即便识别了,也会在<code>/s /q</code>(静默递归删除)参数上出现错误。而<code>rm</code>命令的问题更有趣。其它目录都没有问题,但在编译<a href="http://coffeescript.org/" title="CoffeeScript">Coffee</a>的命令执行后,如果在编译期间创建了新的目录则会产生一个名为<code>-p</code>的目录。估计是内部使用了Linux下的<code>mkdir -p</code>命令造成的。当使用<code>rm -rf -p</code>来删除时,<code>-p</code>会被识别为<code>rm</code>命令的参数而导致失败。故而,制造了这个<code>RMJS</code>。同时还可以达到跨平台的目的。同样,删除文件也用的是<a href="http://coffeescript.org/" title="CoffeeScript">coffee</a>脚本完成的。</p></li>
<li><p>另一个是<code>BROWSE</code>,在Windows下<code>start</code>命令可以使用默认打开方式打开文件,所以将查看HTML报告的命令定义为<code>start</code>。基本来讲,这里的<code>makefile</code>是可以跨平台移植的,当移植到其他平台时,需要修改此命令的定义。</p></li>
<li><p>最后一点,也是最重要的一点:<strong>文件名必须是小写的<code>makefile</code></strong>。如果大小写不对,会导致<code>make</code>无法识别。</p></li>
</ul>
<h3>使用make</h3>
<p>那么,写好了这个<code>makefile</code>,怎么用呢?</p>
<p>只要到有<code>makefile</code>的目录下,执行<code>make</code>命令即可。</p>
<p>单纯的测试,执行:</p>
<pre><code>make test-all
</code></pre>
<p>编译<a href="http://coffeescript.org/" title="CoffeeScript">CoffeeScript</a>,执行:</p>
<pre><code>make compile
</code></pre>
<p>代码覆盖率报告,执行:</p>
<pre><code>make coverage
</code></pre>
<p>先测试,然后出覆盖率报告:</p>
<pre><code>make
</code></pre>
<p>清理现场(删除中间生成的目录、文件):</p>
<pre><code>make clean
</code></pre>
<h2>全部内容</h2>
<p>本文中提到的所有代码,都被提交到了<a href="https://github.com" title="GitHub">GitHub</a>上,工程名字叫<a href="https://github.com/programus/coffee-mocha-nodejs-coverage-windows-example">coffee-mocha-nodejs-coverage-windows-example</a>。(好吧,我知道名字长了点……)</p>
<p>有兴趣的话,可以到那里取得所有的代码。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Draw Something]]></title>
<link href="http://programus.github.com/blog/2012/03/26/draw-something/"/>
<updated>2012-03-26T21:11:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/26/draw-something</id>
<content type="html"><![CDATA[<p>昨天晚上经介绍,认识了一个新游戏——Draw Something。当时就迷上了。现在还在玩……</p>
<!--more-->
<p>Draw Something是一款移动平台游戏,类似你画我猜。在android和iOS上都有。与大多数你画我猜类游戏不同的是没有限时的异步绘画。</p>
<p>这个游戏,对于我,可以说是很早以前就一直期盼着的游戏类型。昨晚玩上就有一种相见恨晚的感觉。就跟当年学编程,第一次接触面向对象的思想时一样……一种“我X!这么多年想要的就是这个!”的感觉。(跑题了……)</p>
<p>最早接触你猜我画类游戏,是在大概十年前左右的Yahoo。上面有跟现在各大网站流行的你猜我画一般无二的游戏。虽然是英文的,但是判断基准比现在国内社交网站上的更智能些。(比如复数错了都算猜对,意思或拼写上接近的单词会给予颜色提示。)</p>
<p>确切地说,在接触Yahoo的网上游戏之前,早在我上中小学的时候,就跟亲戚的小孩自行发明了类似的游戏。当时我们几个人都比较喜欢乱涂乱画,虽然画得都不咋地,但都自命不凡。为了一较高下,我们发明了画名人猜名字的游戏——一个人画一个名人,大家猜叫什么名字。中小学课本上的插图成了我们当时参考的蓝本,不过游戏规定必须背着画,所以冒出了看不出来到底是马克思还是恩格斯的大胡子(最后由作者爆料——那是达·芬奇)、长得像洋葱头的列宁、神似形不似的鲁迅……</p>
<p>作为一个小时候发明此类游戏的人员之一,看到我画你猜类游戏,怎能不兴奋?于是在十多年前,我就忍着4级险过的英语水平,一边开着词典网站,一边在Yahoo上玩起了那个游戏。
后来,工作忙了,就把游戏放下了。多年之后,人人之类的网站上出现了你猜我画,再次兴奋了一把。不过偏门词汇太多,网络速度有时不给力,实在让人爱恨交加。</p>
<p>还有一点最不爽的,从Yahoo的游戏到你猜我画都有的问题,就是——限时!这实在是太让人抓狂了。把一个东西画明白,我很有自信,但快速把画画完,我可没那个功力。所以,常常自己的想法没办法很好的表现出来。</p>
<p>直到昨天晚上看到Draw Something,我震惊了!居然没有时限!居然可以擦了重画,居然猜的人还可以看到绘图过程!</p>
<p>这意味着,我可以慢慢精雕细琢,我可以用动画来表现内容,我可以……发挥无限的想象!(说这个游戏跟你猜我画无异的人恐怕都没理解到绘画表现力的精髓啊……)</p>
<p>于是,作为一个喜欢猜画类游戏的人,Draw Something是我目前见过的最好的游戏!没有“之一”。</p>
<p>如果说缺点,就是网络状况不佳时会有些数据传送的bug;免费版没有金币就没有更多的颜色(为了画个绿色,那黄色跟蓝色抹了半天也没出来效果,好在猜的人懂三原色,居然猜到了)……但这些都是瑕不掩瑜的问题。</p>
<p>最后,我再次开始痛恨自己的英文词汇量不足……为了游戏,一定要努力学习英语!(谁说游戏耽误学习的?)</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Javascript游戏——Memory]]></title>
<link href="http://programus.github.com/blog/2012/03/25/js-game-memory/"/>
<updated>2012-03-25T17:47:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/25/js-game-memory</id>
<content type="html"><![CDATA[<p>前段时间看了《JavaScript语言精粹》,一直想实践一下。于是利用了3、4天的时间做了一个记忆力游戏——<a href="http://programus.appinn.me/jsgames/memory-single.html">Memory</a>。</p>
<!--more-->
<p>先在这里放一个直接可玩的版本:</p>
<iframe id="memory-game" width="350" height="380" src="http://programus.appinn.me/jsgames/memory-single.html"></iframe>
<p>看书的时候随随便便就看过去了,实际做的时候才总是感到书到用时方恨少。书中提到的一些常见问题还真是很常见,在实际编写程序的过程中很多都碰上了。</p>
<p>游戏最终在网上发布时使用的文件——<a href="http://programus.appinn.me/jsgames/memory-single.html">memory-single.html</a>——是一个整合了所有内容的文件,直接转存到本地后,单独一个文件就可以展现整个游戏内容。甚至包括所有的声音和图片。</p>
<p>对于图片,使用了<a href="http://en.wikipedia.org/wiki/Data_URI_scheme">Data URI Schema</a>的技术,将图像的二进制内容转码成Base64编码,以文本形式写在html文件中。不过,本游戏中一共就用了两个图片:作为favicon的图标和下拉列表框的自定义箭头。</p>
<p>对于声音,使用了Pedro Ladaria做的<a href="http://www.codebase.es/riffwave/">代码生成声音的库</a>。我在<a href="http://www.csser.com/" title="CSSer">CSSer</a>上放了一个<a href="http://www.csser.com/board/4f6ab5086e9e4f0817000066#/post/4f6ab6462af57d0a17000020/more">贴板</a>,里面贴了声音库的使用相关链接。</p>
<p>最后,说一下自定义风格的下拉列表框。参考了<a href="http://bavotasan.com/2011/style-select-box-using-only-css/">这篇文章</a>,使用外框的标签来切断<code>&lt;select&gt;</code>的内容,将右边的箭头排除到外框标签的外面并遮挡住。其中不同的是,我在这里使用的外框是<code>&lt;span&gt;</code>而不是<code>&lt;div&gt;</code>。最初使用<code>&lt;span&gt;</code>的时候,没有成功,后来在网上查询才知道只有<code>block</code>级的标签才有效,而<code>&lt;span&gt;</code>不是。但加上<code>display:inline-block</code>就可以如<code>block</code>级标签一般工作了。</p>
<p>说了这么多,最后附上代码。</p>
<div title="其实,我是个按钮" style="cursor:pointer;" onclick="if($('#source-block').css('display') === 'none') {$('#source-block').css('display', 'block');$('#show-hide').text('隐藏');}else{$('#source-block').css('display','none');$('#show-hide').text('显示');}">[<span id="show-hide">显示</span>代码部分]</div>
<div style="display:none;" id="source-block">
<div><script src='https://gist.github.com/2192639.js?file='></script>
<noscript><pre><code>table.matrix,tbody.matrix {
border-style:double;
border-width:3px;
border-color:#000000;
border-spacing:0;
padding:0;
margin:0;
background-color:#aaffff;
}
table.matrix td {
border-style:solid;
border-width:1px;
border-color:#AAAAAA;
padding:0;
margin:0;
width:1.5em;
height:1.5em;
}
table.matrix td.action {
background:#ffffff;
cursor:pointer;
}
table.matrix td.fading {
background:#ffffff;
}
table.matrix td.action:hover {
background:#ccffcc;
cursor:pointer;
}
table.matrix td.error {
border-color:#ff0000;
}
table.matrix td.error-action {
border-color:#aaff00;
}
table.matrix td.show {
background:#dddddd;
}
#matrix-size-span {
width:3em;
overflow-x:hidden;
display:inline-block;
background-image:url(&quot;../select-arrow.png&quot;);
background-repeat:no-repeat;
background-position:center right;
border: 1px solid #ccc;
}
#matrix-size-span.disabled {
background-color:#777777;
}
#matrix-size {
color:#777777;
width:5em;
background: transparent;
border: 0px;
cursor:pointer;
}
#matrix-size:disabled {
color:#444444;
text-decoration:none;
text-shadow:1px 1px 0px #aaaaaa;
cursor:default;
}
#score.updating {
color:#00aa00;
background:#dddd88;
}
#score.stable {
background:inherit;
}
#score-panel {
margin-top:0.3em;
margin-bottom:0.2em;
}
#score-bar {
padding:2px;
}
#score-bar.finished {
color:#444444;
background:#edcfaa;
}
#score-bar.normal {
color:#777777;
}
#game-panel {
}
#start-action {
background-color:#ededed;
border:1px solid #dcdcdc;
display:inline-block;
color:#777777;
font-family:arial;
font-size:11px;
font-weight:bold;
padding:2px 5px;
text-decoration:none;
cursor:pointer;
}
#start-action:disabled {
background-color:#777777;
color:#444444;
border:1px solid #444444;
text-decoration:none;
text-shadow:1px 1px 0px #aaaaaa;
cursor:default;
}
#start-action:enabled:hover {
background-color:#dfdfdf;
}
#start-action:enabled:active {
position:relative;
top:1px;
}
h1 {
text-align:center;
color:#444444;
text-decoration:none;
text-shadow:1px 1px 0px #aaaaaa;
}
body {
background-color:#ededed;
}
</code></pre></noscript></div>
</div>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[在programus.appinn.me上放了两个软件]]></title>
<link href="http://programus.github.com/blog/2012/03/14/put-two-softwares-onto-appinn-dot-me/"/>
<updated>2012-03-14T22:57:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/14/put-two-softwares-onto-appinn-dot-me</id>
<content type="html"><![CDATA[<p>大概两个星期前,跟<a href="http://appinn.com" title="小众软件">小众软件</a>申请了<a href="http://appinn.me">appinn.me</a>的域名——<a href="http://programus.appinn.me">programus.appinn.me</a>。</p>
<p>说起来有点对不住<a href="http://appinn.com" title="小众软件">小众</a>,因为各种原因,一直没腾出时间去打点。今天终于抽出了点时间,去放了两个以前做的软件——</p>
<!--more-->
<ul>
<li><a href="http://programus.appinn.me/softwares/ptray/">PTray</a></li>
<li><a href="http://programus.appinn.me/softwares/screenruler/">ScreenRuler</a></li>
</ul>
<p>小众提供的模板还是不错的,不需要自己修改HTML,只要把对应的文件内容做下更改就行。不过,可惜这些对应的文件的扩展名是<code>.txt</code>,导致在里面编写<code>HTML</code>的时候无法代码高亮。
等再有时间的时候,研究一下如何改成<code>.html</code>扩展名的。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[为Octopress追加数据表格的CSS]]></title>
<link href="http://programus.github.com/blog/2012/03/07/add-table-data-css-for-octopress/"/>
<updated>2012-03-07T21:56:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/07/add-table-data-css-for-octopress</id>
<content type="html"><![CDATA[<p>昨天恢复了一个<a href="http://programus.github.com/blog/2010/11/15/tiny-countdown/">旧博客</a>,里面包含了一个数据表格。</p>
<p>但<a href="http://octopress.org" title="Octopress">Octopress</a>的默认表格是不具有边框的,在看数据表格时会很难看。</p>
<p>于是,对<a href="http://octopress.org" title="Octopress">Octopress</a>做了一番剖析,追加了针对数据表格的CSS格式,并允许在博客的内容文件中选择是否使用数据表格。</p>
<!--more-->
<p>要做到这一点,首先得先找到原始CSS的所在,在Firefox中<a href="http://getfirebug.com/" title="Firebug">Firebug</a>的帮助下,很顺利地找到了位于<code>source/stylesheets</code>的<code>screen.css</code>文件。</p>
<p>打开一看,傻眼了。居然是紧凑型CSS,都堆在一起了。于是上网找了个CSS格式化工具——<a href="http://www.cssportal.com/online-css-editor/">http://www.cssportal.com/online-css-editor/</a>。</p>
<p>当CSS格式化后,查看了一下里面对<code>table</code>、<code>th</code>、<code>td</code>的格式定义。发现确实将<code>border-width</code>设置成了<code>0</code>。</p>
<p>看来只有覆盖它们了。于是写了一个<code>data-table.css</code>,同样放在<code>source/stylesheets</code>下面。</p>
<div><script src='https://gist.github.com/1993032.js?file=data-table.css'></script>
<noscript><pre><code>table {
border-style:solid;
border-width:1px;
border-color:#e7e3e7;
}
th, td {
border-style:dashed;
border-width:1px;
border-color:#e7e3e7;
padding-left: 3px;
padding-right: 3px;
}
th {
border-style:solid;
font-weight:bold;
background: url(&quot;/images/noise.png?1330434582&quot;) repeat scroll left top #F7F3F7;
}
</code></pre></noscript></div>
<p>(感谢<a href="http://chen.yanping.me/" title="陈堰平的个人网站">陈堰平</a>在评论中的指正,CSS文件修正了。修正详情在文章最后。)</p>
<p>然后找到引入<code>screen.css</code>的文件,在其后引入<code>data-table.css</code>。这个文件是<code>source/_includes/head.html</code>。但为了保证没有数据表格的博客还能继续使用原本的表格风格,在里面加了少许条件。</p>
<div><script src='https://gist.github.com/1993032.js?file=head.html'></script>
<noscript><pre><code> &lt;link href=&quot;{{ root_url }}/stylesheets/screen.css&quot; media=&quot;screen, projection&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;&gt;
{% if page.styles contains 'data-table' %}
&lt;link href=&quot;{{ root_url }}/stylesheets/data-table.css&quot; media=&quot;screen, projection&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
{% endif %}
</code></pre></noscript></div>
<p>这样,只要在博客文件的<a href="https://github.com/mojombo/jekyll/wiki/yaml-front-matter"><code>yaml front matter</code></a>部分里面加入<code>styles: [data-table]</code>,就可以让数据表格用的表格风格生效了。</p>
<p>要在博客里插入表格,则可以使用如下格式。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>左对齐表头 | 中间对齐表头 | 右对齐表头
</span><span class='line'>:----------|:------------:|----------:
</span><span class='line'>左对齐数据 |中间对齐数据 |右对齐数据
</span><span class='line'>第二行数据 |也是第二行 |还是第二行
</span><span class='line'>懒得写了...|..... |.....
</span><span class='line'>长数据,以便看出表头的对齐|长数据,以便看出表头的对齐|长数据,以便看出表头的对齐
</span></code></pre></td></tr></table></div></figure>
<p>就会得到如下表格:</p>
<table>
<thead>
<tr>
<th align="left">左对齐表头 </th>
<th align="center"> 中间对齐表头 </th>
<th align="right"> 右对齐表头</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">左对齐数据 </td>
<td align="center">中间对齐数据 </td>
<td align="right">右对齐数据</td>
</tr>
<tr>
<td align="left">第二行数据 </td>
<td align="center">也是第二行 </td>
<td align="right">还是第二行</td>
</tr>
<tr>
<td align="left">懒得写了&#8230;</td>
<td align="center">&#8230;.. </td>
<td align="right">&#8230;..</td>
</tr>
<tr>
<td align="left">长数据,以便看出表头的对齐</td>
<td align="center">长数据,以便看出表头的对齐</td>
<td align="right">长数据,以便看出表头的对齐</td>
</tr>
</tbody>
</table>
<p>其中的左右对齐,取决于表头下面一行中的冒号的位置。</p>
<hr />
<h3>2012-03-19补充:</h3>
<p>感谢<a href="http://chen.yanping.me/" title="陈堰平的个人网站">陈堰平</a>的指正,之前的CSS会连同代码框都改掉。查看了一下HTML代码,发现代码框的代码有如下特点:</p>
<ul>
<li>嵌在<code>&lt;div class="highlight"&gt;</code>或<code>&lt;div class="gist-highlight"&gt;</code>中</li>
<li><code>&lt;td&gt;</code>标签有<code>code</code>或<code>gutter</code>这两个class</li>
<li><code>&lt;td&gt;</code>的子标签是<code>&lt;pre&gt;</code></li>
</ul>
<p>本想用以上中的某种来区分对待CSS,但CSS中不支持通过排除某class(CSS3中有了<code>:not()</code>,但CSS3未被全部浏览器支持)或指定父标签来进行选择。由于对CSS也不是很熟悉,所以重新有学习了一次CSS选择器相关的知识,其中发现一个<code>+</code>选择器,可以通过在同级出现的标签来指定。这时发现了代码框的又一个特点:</p>
<ul>
<li><code>&lt;table&gt;</code>标签是孤独的(没有兄弟节点)</li>
</ul>
<p>但数据表格必然是配合文章出现的,前后通常都会有些兄弟标签。(即使没有也可以自己追加一个空<code>&lt;span&gt;</code>或<code>&lt;div&gt;</code>来解决。)于是,使用<code>* + table</code>来做选择器选出非代码的表格——也就是数据表格——从而完成了数据表格特定CSS的实现。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[为Octopress追加[分享到微博]按钮]]></title>
<link href="http://programus.github.com/blog/2012/03/04/share-weibo-button/"/>
<updated>2012-03-04T20:11:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/04/share-weibo-button</id>
<content type="html"><![CDATA[<p>昨天加上了<a href="http://weibo.com" title="新浪微博">微博</a>的侧边栏。今天发现每篇微博下面还有一个<code>Tweet</code>按钮。还是那句话,在<a href="http://zh.wikipedia.org/wiki/%E9%98%B2%E7%81%AB%E9%95%BF%E5%9F%8E" title="叹息之墙">墙</a>后面,<a href="http://twitter.com/" title="Twitter">Twitter</a>用不到,所以<a href="http://weibo.com" title="新浪微博">微博</a>的分享按钮不可缺少。</p>
<!--more-->
<p>有了昨天的经验,今天也就没去网上查什么资料。直接去<a href="http://weibo.com" title="新浪微博">新浪微博</a>找到<a href="http://open.weibo.com/sharebutton" title="新浪微博分享按钮">分享按钮</a>的页面,生成一个按钮代码。</p>
<div><script src='https://gist.github.com/1972564.js?file='></script>
<noscript><pre><code>&lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
(function(){
var _w = 86 , _h = 50;
var param = {
url:location.href,
type:'6',
count:'1', /**是否显示分享数,1显示(可选)*/
appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
title:'', /**分享的文字内容(可选,默认为所在页面的title)*/
pic:'', /**分享图片的路径(可选)*/
ralateUid:'1098907490', /**关联用户的UID,分享微博会@该用户(可选)*/
language:'zh_cn', /**设置语言,zh_cn|zh_tw(可选)*/
rnd:new Date().valueOf()
}
var temp = [];
for( var p in param ){
temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
}
document.write('&lt;iframe allowTransparency=&quot;true&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&amp;') + '&quot; width=&quot;'+ _w+'&quot; height=&quot;'+_h+'&quot;&gt;&lt;/iframe&gt;')
})()
&lt;/script&gt;</code></pre></noscript></div>
<p>仔细看看生成的按钮,虽然上面写了一大堆JavaScript,但实际有用的就是最后一行<code>document.write('...')</code>里面的HTML代码。但这里HTML的主要部分都是用JavaScript生成的。于是祭出网页分析神器——<a href="http://getfirebug.com/" title="Firebug">Firebug</a>,直接分析<a href="http://open.weibo.com/sharebutton" title="新浪微博分享按钮">分享按钮</a>页面里例子的HTML代码。从中提取出如下URL(为了便于阅读,格式做了调整)——</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>http://hits.sinajs.cn/A1/weiboshare.html?
</span><span class='line'> url=http%3A%2F%2Fopen.weibo.com%2Fsharebutton&
</span><span class='line'> appkey=&
</span><span class='line'> type=6&
</span><span class='line'> ralateUid=1098907490&
</span><span class='line'> language=zh_cn</span></code></pre></td></tr></table></div></figure>
<p>其中各个参数的意思,参看生成的JavaScript的注释,基本一目了然了。下一步,嵌入代码到博客下面的分享栏里。</p>
<p>经过一番查找,分享栏的代码存储在<code>source/_includes/post/sharing.html</code>中。果断加入代码:</p>
<div><script src='https://gist.github.com/1972657.js?file=sharing.html'></script>
<noscript><pre><code>&lt;div class=&quot;sharing&quot;&gt;
{% if site.weibo_share %}
&lt;span&gt;
&lt;iframe
width=&quot;86&quot;
scrolling=&quot;no&quot;
height=&quot;16&quot;
frameborder=&quot;0&quot;
src=
&quot;http://hits.sinajs.cn/A1/weiboshare.html?url={{ site.url }}{{ page.url }}&amp;amp;type=6&amp;amp;{% if site.weibo_uid %}ralateUid={{ site.weibo_uid }}&amp;amp;{% endif %}language=zh_cn&quot; allowtransparency=&quot;true&quot;&gt;
&lt;/iframe&gt;
&lt;/span&gt;
{% endif %}
{% if site.twitter_tweet_button %}
&lt;a href=&quot;http://twitter.com/share&quot; class=&quot;twitter-share-button&quot; data-url=&quot;{{ site.url }}{{ page.url }}&quot; data-via=&quot;{{ site.twitter_user }}&quot; data-counturl=&quot;{{ site.url }}{{ page.url }}&quot; &gt;Tweet&lt;/a&gt;
{% endif %}
{% if site.google_plus_one %}
&lt;div class=&quot;g-plusone&quot; data-size=&quot;{{ site.google_plus_one_size }}&quot;&gt;&lt;/div&gt;
{% endif %}
{% if site.facebook_like %}
&lt;div class=&quot;fb-like&quot; data-send=&quot;true&quot; data-width=&quot;450&quot; data-show-faces=&quot;false&quot;&gt;&lt;/div&gt;
{% endif %}
&lt;/div&gt;
&lt;hr style=&quot;border-bottom:1px dotted #bdbabd;height:1px;border-top:0px;border-left:0px;border-right:0px;&quot; /&gt;
</code></pre></noscript></div>
<p>其中最后一行是自己调整了一下看着顺眼的格式。</p>
<p>同时要在<code>_config.yml</code>文件中加入<code>weibo_share</code>的设定。</p>
<div><script src='https://gist.github.com/1972657.js?file=_config.yml'></script>
<noscript><pre><code># Weibo
# Please refer to http://weibo.com/tool/weiboshow to get your uid and verifier.
weibo_uid: 1098907490
weibo_verifier: abd54ad9
weibo_fansline: 0 # How many lines for the fan list
weibo_show: true # Whether you want your weibo content to be shown
weibo_pic: true # Whether you want the pictures in weibo to be shown
weibo_skin: 10 # Please refer to http://weibo.com/tool/weiboshow
weibo_share: true # Whether show the sharing button
</code></pre></noscript></div>
<p>最后,为了更换主题时不会出问题,对<code>.theme/&lt;所用的主题名&gt;/source/_includes/post/sharing.html</code>也做了相应的修改。</p>
<p>最后,生成网页、启动服务器测试——</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>rake generate
</span><span class='line'>rake preview</span></code></pre></td></tr></table></div></figure>
<p>查看预览网页<a href="http://localhost:4000">http://localhost:4000</a>……<strong>按钮加入成功</strong>!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[为Octopress博客追加新浪微博侧栏]]></title>
<link href="http://programus.github.com/blog/2012/03/03/add-weibo-sidebar-into-octopress/"/>
<updated>2012-03-03T21:43:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/03/add-weibo-sidebar-into-octopress</id>
<content type="html"><![CDATA[<p>配置好了基于Octopress的博客后,发现其右侧边栏(位置自然根据主题会有不同)上有<a href="http://twitter.com/" title="Twitter">Twitter</a>等内容。因为<a href="http://zh.wikipedia.org/wiki/%E9%98%B2%E7%81%AB%E9%95%BF%E5%9F%8E" title="叹息之墙">墙</a>的原因,<a href="http://twitter.com/" title="Twitter">Twitter</a>始终使用起来不够方便,所以还是<a href="http://weibo.com" title="新浪微博">新浪微博</a>用的更多。于是,就想在博客里弄一个<a href="http://weibo.com" title="新浪微博">微博</a>的侧边栏。</p>
<!--more-->
<p><img class="left" src="http://programus.github.com/images/posts/octopress-weibo.png" title="" ></p>
<h3>实现设想</h3>
<p><a href="http://weibo.com" title="新浪微博">微博</a>本身提供了一个嵌入侧边栏的工具,叫<a href="http://weibo.com/tool/weiboshow" title="微博秀">微博秀</a>。于是便想是不是简单嵌入即可呢?</p>
<h3>其他尝试</h3>
<p>但对稍有点费劲的工作,当然首先是上网搜索一下是否有现成方案。果然找到了一篇文章——《<a href="http://blog.tingkun.com/blog/2011/11/05/xin-lang-wei-bo-ce-lan-widgetding-zhi-octopress/">新浪微博侧栏widget定制</a>》。</p>
<p>仔细查看了一下代码,发现作者好粗心,代码里居然没有用变量,而是把自己的用户名直接硬写进去了。另外,貌似只实现了<code>微博名片</code>和<code>关注按钮</code>。而且因为使用了<a href="http://weibo.com" title="新浪微博">微博</a>的JavaScript API,所以需要一个APP ID。</p>
<h3>回归</h3>
<p>我是懒人,不想搞得那么复杂。于是继续回到自己最初的方案——嵌入<a href="http://weibo.com/tool/weiboshow" title="微博秀">微博秀</a>。</p>
<p>先到<a href="http://weibo.com/tool/weiboshow" title="微博秀">微博秀</a>里面生成自己的微博秀嵌入代码。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;iframe</span>
</span><span class='line'> <span class="na">width=</span><span class="s">&quot;100%&quot;</span>
</span><span class='line'> <span class="na">height=</span><span class="s">&quot;550&quot;</span>
</span><span class='line'> <span class="na">class=</span><span class="s">&quot;share_self&quot;</span>
</span><span class='line'> <span class="na">frameborder=</span><span class="s">&quot;0&quot;</span>
</span><span class='line'> <span class="na">scrolling=</span><span class="s">&quot;no&quot;</span>
</span><span class='line'> <span class="na">src=</span><span class="s">&quot;http://widget.weibo.com/weiboshow/index.php?</span>
</span><span class='line'><span class="s"> width=0&amp;</span>
</span><span class='line'><span class="s"> height=550&amp;</span>
</span><span class='line'><span class="s"> fansRow=1&amp;</span>
</span><span class='line'><span class="s"> ptype=1&amp;</span>
</span><span class='line'><span class="s"> speed=0&amp;</span>
</span><span class='line'><span class="s"> skin=2&amp;</span>
</span><span class='line'><span class="s"> isTitle=0&amp;</span>
</span><span class='line'><span class="s"> noborder=1&amp;</span>
</span><span class='line'><span class="s"> isWeibo=1&amp;</span>
</span><span class='line'><span class="s"> isFans=1&amp;</span>
</span><span class='line'><span class="s"> uid=1098907490&amp;</span>
</span><span class='line'><span class="s"> verifier=abd54ad9&amp;</span>
</span><span class='line'><span class="s"> dpc=1&quot;</span><span class="nt">&gt;</span>
</span><span class='line'><span class="nt">&lt;/iframe&gt;</span>
</span></code></pre></td></tr></table></div></figure>
<p>仔细一看,其实就是一个大URL,尝试一下各种参数。最终写了一个<code>weibo.html</code>,放到了<code>source/_includes/asides</code>下面。貌似在<code>.theme/&lt;所用的主题名&gt;/source/_includes/asides</code>下面放着更好,因为切换主题时不会丢失。</p>
<div><script src='https://gist.github.com/1966517.js?file=weibo.html'></script>
<noscript><pre><code>{% if site.weibo_uid %}
&lt;section&gt;
&lt;h1&gt;新浪微博&lt;/h1&gt;
&lt;ul id=&quot;weibo&quot;&gt;
&lt;li&gt;
&lt;iframe
width=&quot;100%&quot;
height=&quot;550&quot;
class=&quot;share_self&quot;
frameborder=&quot;0&quot;
scrolling=&quot;no&quot;
src=&quot;http://widget.weibo.com/weiboshow/index.php?width=0&amp;height=550&amp;ptype={% if site.weibo_pic %}1{% else %}0{% endif %}&amp;speed=0&amp;skin={{weibo_skin}}&amp;isTitle=0&amp;noborder=1&amp;isWeibo={% if site.weibo_show %}1{% else %}0{% endif %}&amp;isFans={{weibo_fansline}}&amp;uid={{site.weibo_uid}}&amp;verifier={{site.weibo_verifier}}&quot;&gt;
&lt;/iframe&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
{% endif %}
</code></pre></noscript></div>
<p>同时,在<code>_config.yml</code>中加入相关设定——</p>
<div><script src='https://gist.github.com/1966517.js?file=_config.yml'></script>
<noscript><pre><code># 其它内容....
default_asides: [asides/recent_posts.html, asides/weibo.html, asides/github.html, asides/[Twitter][].html, asides/googleplus.html]
# 其它内容....
# Weibo
# Please refer to http://weibo.com/tool/weiboshow to get your uid and verifier.
weibo_uid: 1098907490
weibo_verifier: abd54ad9
weibo_fansline: 0 # 粉丝显示多少行
weibo_show: true # 是否显示最近微博内容
weibo_pic: true # 是否显示微博中的图片
weibo_skin: 10 # 使用哪种配色风格,数字为从1开始的微博秀风格序号
# 其它内容....</code></pre></noscript></div>
<p>其中的<code>weibo_uid</code>和<code>weibo_verifier</code>是从<a href="http://weibo.com/tool/weiboshow" title="微博秀">微博秀</a>生成的代码中取得的,其它则是显示设定。</p>
<p>至此,搞定了微博的嵌入。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[第一篇用Octopress生成的日志]]></title>
<link href="http://programus.github.com/blog/2012/03/01/first-post-by-octopress/"/>
<updated>2012-03-01T21:31:00+09:00</updated>
<id>http://programus.github.com/blog/2012/03/01/first-post-by-octopress</id>
<content type="html"><![CDATA[<p>先是在知乎上发现了一个叫<a href="http://www.zhihu.com/people/lv-kun" title="吕坤的知乎主页">吕坤</a>的人,顺着简介发现了<a href="http://lvkun.github.com/" title="吕坤的github博客">他的博客</a>。然后了解到可以使用github来写博客,觉得很好玩,就打算试一试。</p>
<!--more-->
<p>我不像<a href="http://www.zhihu.com/people/lv-kun" title="吕坤的知乎主页">吕坤</a>对<a href="http://www.ruby-lang.org" title="Ruby">Ruby</a>那么大意见,所以装了运行Jekyll的一切必需品,现学<a href="http://git-scm.com/" title="Git">Git</a>(这个早就听说了,一直没用到所以没学)、<a href="https://github.com/mojombo/jekyll/wiki" title="Jekyll">Jekyll</a>、<a href="http://daringfireball.net/projects/markdown/" title="Markdown">Markdown</a>……折腾了一个星期天,终于可以显示博客了。正高兴呢,忽然发现服务器不正常了。</p>
<p>经过一番查找,发现是因为<a href="https://github.com/mojombo/jekyll/wiki" title="Jekyll">Jekyll</a>运行时使用默认环境的地区编码信息,所以不识别UTF-8编码中的中文。从网上搜到Windows下可以使用<code>chcp</code>来改变语言编码,于是试了试<code>chcp 65001</code>,发现运行</p>
<pre><code>jekyll --pygments --safe --auto --server
</code></pre>
<p>之后没有任何显示。遂放弃。</p>
<p>继续查,发现哪怕在Windows下,只要设置<code>LANG</code>和<code>LC_ALL</code>后,便可以设置的编码运行。于是</p>
<div><script src='https://gist.github.com/1972602.js?file='></script>
<noscript><pre><code>set LANG=zh_CN.UTF-8
set LC_ALL=zh_CN.UTF-8</code></pre></noscript></div>
<p>然后再次运行<code>jekyll</code>,<strong>正常了!</strong></p>
<p>于是才有了此篇日志。</p>
<hr />
<p>本来用<a href="https://github.com/mojombo/jekyll/wiki" title="Jekyll">Jekyll</a>的日志已经做好了,但是发现无法从手机上正常浏览。又懒得去研究怎么调整HTML、CSS那一套东西。正一筹莫展之际,发现了<a href="http://octopress.org" title="Octopress">Octopress</a>这个东西。于是毅然变节,改投<a href="http://octopress.org" title="Octopress">Octopress</a>,于是有了现在的后半段。</p>
<p><a href="http://octopress.org" title="Octopress">Octopress</a>比较有趣,会把代码放在<code>source</code>目录下,把生成的网站放在<code>_deploy</code>目录下。(当然所有目录都可以通过设置修改。)然后会自动将<code>_deploy</code>目录作为github page的<code>master</code>分支,将包含source在内的部分作为github page的<code>source</code>分支。</p>
<p>对<a href="http://git-scm.com/" title="Git">Git</a>还不是特别了解,或许领会不到如此分离的真谛,但隐约能感觉到这样做会使Repository里的结构清晰很多。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[这是什么机器?]]></title>
<link href="http://programus.github.com/blog/2011/01/24/what-is-this-machine/"/>
<updated>2011-01-24T21:32:00+09:00</updated>
<id>http://programus.github.com/blog/2011/01/24/what-is-this-machine</id>
<content type="html"><![CDATA[<p>请看视频(记得开声音):</p>
<!--more-->
<p><embed src="http://player.youku.com/player.php/sid/XMjM1NDk2Njgw/v.swf" allowFullScreen="true" quality="high" width="480" height="400" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash"></embed></p>
<p><a href="http://v.youku.com/v_show/id_XMjM1NDk2Njgw.html">视频地址</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[TinyCountdown - 小巧纯粹的倒计时软件]]></title>
<link href="http://programus.github.com/blog/2010/11/15/tiny-countdown/"/>
<updated>2010-11-15T23:37:00+09:00</updated>
<id>http://programus.github.com/blog/2010/11/15/tiny-countdown</id>
<content type="html"><![CDATA[<p>自从工作以后,一直面临一个问题:开会超时。而作为IT公司的会议,又有一个常见的情况——共享或投影屏幕。</p>
<p>基于此,在上周的一次会议上,我在共享屏幕的同时,挂上了一个倒计时程序。效果还不错。毕竟发言者在众目睽睽之下超时实在很没面子。但是,那个计时程序上面的很多按钮实在太占地方,在共享屏幕的时候常常遮挡住必要内容,导致不得不移来移去。于是乎,作为IT从业人员,再次热血沸腾,写了一个更加小巧的倒计时软件。既然小巧是最主要特点,于是便命名为——</p>
<p><strong>TinyCountdown</strong></p>
<!--more-->
<p><img src="http://i141.photobucket.com/albums/r60/programus/software/TinyCountdown_preview.gif" title="screenshot" ></p>
<p>程序运行后,没有任何累赘的标题栏、按钮、菜单等。唯一有效内容之外的累赘就是一个为了方便改变窗口大小的窗口边框。(好吧,我承认技术上去掉这个也是可能的,我在偷懒……)</p>
<p>有君问:啥都没有,怎么操控?</p>
<p>答曰:鼠标 或 键盘,组合更悠然。</p>
<p>操作说明窗口会在鼠标飘过主窗口时出现,为了软件的通用性,做成了英文的。</p>
<p>现将中文概述如下:</p>
<table>
<thead>
<tr>
<th align="left">功能 </th>
<th align="left"> 鼠标操作 </th>
<th align="left"> 键盘操作 </th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">开始倒计时 </td>
<td align="left">单击左键 </td>
<td align="left">空格</td>
</tr>
<tr>
<td align="left">暂停倒计时 </td>
<td align="left">单击左键 </td>
<td align="left">空格</td>
</tr>
<tr>
<td align="left">重置倒计时 </td>
<td align="left">单击左键(停止时)</td>
<td align="left">Backspace</td>
</tr>
<tr>
<td align="left">设置倒计时时间 </td>
<td align="left">单击右键 </td>
<td align="left">回车</td>
</tr>
<tr>
<td align="left">关闭程序 </td>
<td align="left">将主窗口缩至极小</td>
<td align="left">ESC</td>
</tr>
<tr>
<td align="left">帮助窗口显示/隐藏 </td>
<td align="left">单击中键 </td>
<td align="left">F1</td>
</tr>
<tr>
<td align="left">设置主窗口总在最前</td>
<td align="left"> </td>
<td align="left">HOME</td>
</tr>
<tr>
<td align="left">取消主窗口总在最前</td>
<td align="left"> </td>
<td align="left">END</td>
</tr>
<tr>
<td align="left">全屏幕切换 </td>
<td align="left"> </td>
<td align="left">M</td>
</tr>
</tbody>
</table>
<p>其他隐藏操作,请自行摸索。</p>
<p>为保证程序的小巧和绿色,程序不会保存任何设置,默认倒计时时间为15分钟,默认开启帮助窗口,默认程序出现在窗口右上角,默认程序为总在最前。程序内置内存优化机制,平均占用内存在1MB左右,内存占用峰值不足2MB。</p>
<p>希望有习惯整洁小巧的人士喜欢。</p>
<p>忘了说了,本程序只能在Windows平台上运行,对不起Mac和Linux等的兄弟了。望不要介意。</p>
<p>下载地址:</p>
<p><a href="http://code.google.com/p/tinytool/downloads/detail?name=TinyCountdown.exe">http://code.google.com/p/tinytool/downloads/detail?name=TinyCountdown.exe</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[NXTRockBoy上的第二个原创游戏——Drop Ball]]></title>
<link href="http://programus.github.com/blog/2009/12/25/nxtrockboy-dropball/"/>
<updated>2009-12-25T11:21:00+09:00</updated>
<id>http://programus.github.com/blog/2009/12/25/nxtrockboy-dropball</id>
<content type="html"><![CDATA[<p>前一段时间工作很忙,本职工作很忙。后来,集中精力制作了第二款NXTRockBoy的游戏——Drop Ball。顺便说一下,第一款游戏在<a href="http://programus.github.com/blog/2009/10/25/nxtrockboy-bball/">这里</a>。</p>
<!--more-->
<p>Drop Ball是一款什么样的游戏呢?</p>
<p>大家记得有一个男人系列的游戏吧。其中有一个是“是男人就下一百层”。好,Drop Ball就是这个游戏的摇摆游戏机版。只不过把那个倒霉的小男孩换成了我们的主角——小黑球。玩那个游戏按箭头按到手疼的同学们,现在可以换一种方式了。</p>
<p>在这个游戏中,继续发挥了NXTRockBoy的摇摆操作特性,小球仍然是受到重力的影响,而且依旧具有惯性。不要以为你把NXTRockBoy往左转,小球就往左跑,如果他之前有一个右方向的速度,他是要先进行减速的。所以,游戏一如既往地很有难度,不过可玩性提高了很多。</p>
<p>另外,作为第二个游戏,总要有些突破。这个游戏中增加了两个重要特性——</p>
<p>第一个——背景音乐支持!在不影响小球那个滴滴答答的音效的基础上增加了背景音乐支持。系统中内置了三段音乐——Final Countdown、Smooth Criminal、变形金刚动画片主题曲。如果你想要自定义,只要读读我的<a href="http://code.google.com/p/nxtprojects/wiki/NXTUtils">帮助</a>中的音乐支持部分,自己写一段代码即可。</p>
<p>第二个——环境光支持!如果有人组装了NXTRockBoy,一定对上面安装的颜色/光传感器和超声传感器感到奇怪。因为上一个游戏压根没用到这两个东西。这一款游戏用到了颜色/光传感器(虽然超声传感器还是遭到冷遇)。游戏中平台的相关参数会根据游戏机所在地的环境光的强度改变。</p>
<p>游戏有两个模式——<code>|</code>模式和<code>-</code>模式。<code>|</code>模式下,光线强,则平台间距小;<code>-</code>模式下光线强,则平台宽度大。</p>
<p>好,看了这么多,大家一定期待着看视频了。那就如期上视频!</p>
<p><embed src="http://player.youku.com/player.php/sid/XMTM5OTEwNDA0/v.swf" allowFullScreen="true" quality="high" width="480" height="400" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash"></embed></p>
<p><a href="http://v.youku.com/v_show/id_XMTM5OTEwNDA0.html">视频网址</a></p>
<p>最后,也是最重要的:</p>
<p>搭建图及程序下载和说明,请看这里:</p>
<p><a href="http://code.google.com/p/nxtprojects/wiki/DropBall">http://code.google.com/p/nxtprojects/wiki/DropBall</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[NXTRockBoy - 原创游戏机 & BBall - 其上的第一个游戏]]></title>
<link href="http://programus.github.com/blog/2009/10/25/nxtrockboy-bball/"/>
<updated>2009-10-25T22:26:00+09:00</updated>
<id>http://programus.github.com/blog/2009/10/25/nxtrockboy-bball</id>
<content type="html"><![CDATA[<p>利用近一个月的业余时间,终于成功地用NXT 8547搭建起了原创的摇摆游戏机NXTRockBoy,并开发完了世上第一款NXTRockBoy游戏——BBall。</p>
<!--more-->
<p>NXTRockBoy是我用LEGO Mindstorms NXT 8547机器人套装搭建而成的一款原创游戏机。他最大的特点是通过摇摆来作为主要输入。有人说Wii的输入也是通过摇摆,而且是三维方向的摇摆,NXTRockBoy有什么创新?Wii也是我很喜欢的游戏机,在玩NXT之前,常常在玩,但是当你玩Wii的时候,只是在摇摆游戏棒,电视不会跟着摇。而我的NXTRockBoy是要摇晃包含显示屏在内的游戏机本体,所以产生的效果就是屏幕及屏幕里面的内容都会随着摇摆进行运动。</p>
<p>那么,这种输入特性有什么好玩的?</p>
<p>那就得谈谈这第一款游戏——BBall了。BBall,要是一定要有一个中文名字的话,我认为“蹦蹦球”应该是个很贴切的名字。因为游戏的主角就是一个弹力超强的小球。弹力强到什么程度呢?如果放任不管地让他去上下弹啊弹,他会越蹦越高,最大速度(势能最低时)越来越快。这球要是出现在现实世界中,恐怕永动机就不再是神话了。好吧,实际上,这其实是程序在微分运算时的精度不足引起的,所以我在程序里面加入了极限能量控制。小球动能和势能的总和不能超过一个既定的值,如果超过,则强行降低速度。</p>
<p>一不小心把话扯远了,现在拉回来。这个小球就这么欢快地在屏幕里面蹦啊蹦,碰到屏幕的边缘便反弹,碰到任何黑色的障碍也反弹。小球最喜欢的就是去吃豆豆。屏幕里总会有一个不一定在什么位置的豆豆等着他。但是小球自己无法有意识地向豆豆移动,所以他需要玩家的帮助。</p>
<p>小球除了反弹,在运动过程中只受到一个力——重力。就是物理书里面说的那个竖直向下的重力,那个不会随着屏幕的旋转而旋转的重力!好,现在就要说到摇摆输入的好玩之处了:比如说,当小球在天空中,达到了最高点,势能最大,动能为零(也就意味着速度为零),这时我们旋转了游戏机本体,那么小球就不会再沿着与屏幕侧边平行的方向下落,而是沿着与自然界的竖直方向相同的方向下落,而相对于屏幕,就是一个倾斜的方向。当然,举一个动能为零的例子是为了简化说明,如果动能不为零,那么就是抛物线的一个局部。这样,当小球与屏幕底边接触时,它的速度方向与底边的夹角就不再是直角,而是某个特定的角度。那么反弹的时候就会向相对于底边倾斜的方向反弹(与入射角的和为180°)。通过这样的角度变化实现了小球运动方向与速度的调节。</p>
<p>另一方面,因为小球的坐标及速度是与屏幕黏着在一起的,所以小球实际上是无法实现能量守恒的,这样速度常常会出现极具戏剧性的变化。</p>
<p>好吧。我知道我上面说了那么多,恐怕你也不一定能看懂。毕竟我写自然坐标与屏幕坐标变换模块的时候,自己也头疼了好几天,草纸也用了好几张,早已忘记很久的正弦定理和余弦定理又被我记熟了。所以,对不懂的人,我非常理解和同情,故此我留下了下面的视频。希望能帮助大家理解和愉悦各位的身心。</p>
<p><embed src="http://www.tudou.com/v/9-eFmGmNbZ8/&resourceId=0_05_02_99/v.swf" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" wmode="opaque" width="480" height="400"></embed></p>
<p><a href="http://www.tudou.com/programs/view/9-eFmGmNbZ8/?resourceId=0_06_02_99">土豆网链接</a></p>
<p>独乐乐不如与人乐乐,与人乐乐不如众乐乐。在此共享一下详细的构件图和源代码:</p>
<ul>
<li><a href="http://code.google.com/p/nxtprojects/wiki/BBall">http://code.google.com/p/nxtprojects/wiki/BBall</a></li>
</ul>
]]></content>
</entry>
</feed>