The Kai Way
I am writing about my experiences as a naval navel-gazer.
2023-01-19T00:00:00Z
https://thekaiway.com/
Kai
kai@thekaiway.com
AGI 曙光 ChatGPT
2023-01-19T00:00:00Z
https://thekaiway.com/2023/12/05/the-dawn-of-agi-chatgpt/
<h2 id="chatgpt" tabindex="-1">ChatGPT 是什么? <a class="header-anchor" href="https://thekaiway.com/2023/12/05/the-dawn-of-agi-chatgpt/">#</a></h2>
<p>GPT,全称Generative Pre-trained Transformer,是基于Transformer模型训练的通用人工智能。当卷积网络的参数量达到数以亿计时,它展现出惊人的智能水平,已经能完成人类相对初级的工作。更令我震撼的是,这么强大的智能居然在 2022 年底被OpenAI发布出来,让普通大众触手可及。</p>
<p>智能的涌现仍是个谜。毕竟,Transformer 神经网络本质上只是人类大脑的仿制品,而人类对大脑的运作原理也远未完全理解。训练是一种无监督学习,通过吸收海量文本数据进行自我学习,就像人类智慧被注入机器大脑一样。以文字承载的知识是人类对世界的理解,在这种训练下 GPT 在某种程度上按照人类的理解在认识世界。甚至 GPT 的思维方式都与人相近,比如思维链、承诺给予高额小费,让它回答前做深呼吸等技巧,可以提升回答质量。</p>
<p>我从 OpenAI 工程师 Andrej Karpathy 的视频,<a href="https://www.youtube.com/watch?v=zjkBMFhNj_g">《大语言模型简介》</a>和<a href="https://www.youtube.com/watch?v=kCc8FmEb1nY">《从头构建 GPT》</a> 获得对底层技术脉络的基本理解,特别推荐给好奇 GPT 怎么被构建出来的朋友观看。</p>
<h2 id="gpt" tabindex="-1">GPT 改变了什么? <a class="header-anchor" href="https://thekaiway.com/2023/12/05/the-dawn-of-agi-chatgpt/">#</a></h2>
<p>当我工作和生活开始大量使用 ChatGPT 后,切身感受到了人类文明的巨轮正在转动,新一代的工业革命已经来临。</p>
<p>之前需要为问题定义解决方案的每个步骤,而人工智能采用之后可以将问题直接抛给他们来解决,比如生活中最常见的智能驾驶,给出目的地,而不再需要自己去转方向盘踩油门刹车。</p>
<p>过去数千年,人类社会的发展基本上依赖于众多独立个体的相互协作。而现在,人工智能已经参与到诸如疫苗制造和城市规划决策等方面。</p>
<p>人类需要从出生到能做某种智能要求的工作,需要一二十年的培训,培训过程本身并非各体自我完成,也需要占用其他个体的时间来达成。并且不是全人类每个个体都拥有对这个世界的所有知识,水平也参差不齐,能贡献到文明发展仅是少数。</p>
<p>同样,人类协同效率其实也挺低的,因为信息传递需要通过文字,并且输出输入都存在损耗,你不能理解我想表达什么是常态。人类每天还需要 8 小时的睡眠,分布在不同时区有不同的作息,全球化的大规模协同需要不少时间来回做信息传递和校准,进而逐步降低效率。</p>
<p>人工智能与此不同,可以无需干预地进行学习,并且智能可直接 1:1 无损复制。它们无需休息,工作效率上比起人类高出几个量级,能短时间处理大量信息,通过现有的 API 操作数据,而无需人类那般通过界面来手动操作。</p>
<h2 id="" tabindex="-1">我想象的未来是怎样 <a class="header-anchor" href="https://thekaiway.com/2023/12/05/the-dawn-of-agi-chatgpt/">#</a></h2>
<p>尽管 AGI 的到来还需时间,除了技术上还有不少难点需要突破,也因为人类社会本身存在不小的内耗,并不是把 100% 资源用在文明发展上。</p>
<p>单看各家科技企业都在研制自己的人工智能,资源被分散,全球算力资源肯定不足以支持成百上千家企业各自作出巨大推动。当然也存在一定重合共同推动部分,大部分企业用开源的 Llama 作为底座进行训练,这很像当初 Yahoo 开源 Hadoop 的局面。</p>
<p>假如人类社会遇到巨大危机,能像三体或者星际穿越那样,将所有资源集中到一处,去掉竞争的冗余成本,去掉非必要的人文开销,可能达到 AGI 时代不需要太久,但明显不现实。</p>
<p>作为一个软件工程师(或者称我为程序员),我相信很快不再是个人从头一行行代码手工实现,而是需要提高抽象层次做程序架构设计。编程语言的层出不穷可能会到此为止,因为这种面向人类友好的计算机操作定义方式肯定没有直接告诉人工智能你要做什么,大概怎么做来的更高效。</p>
<p>我猜会像现在高度集成的硬件一样,不再有人类手工打造的冰箱洗衣机,相应地,人力转到维修(Debug)这一环节上。</p>
<p>而且,不仅是编程语言会停止进化,人类第二语言学习也可能会大幅减少。随着人工智能在语音识别方面的准确率和处理速度达到极高水平,我们可能再也不需要为了交流而学习第二语言。当前我面对大量英文时候,都会直接用 OpenAI 给出翻译结果,并通过中文进行提问。</p>
<p>人类的知识学习方式也将随之转变,实现个性化教育并不是遥不可及的。像 <a href="https://github.com/JushBJJ/Mr.-Ranedeer-AI-Tutor">Mr.-Ranedeer-AI-Tutor</a> 这套 Prompt 能完全基于个人的母语,知识水平,学习习惯和教学接受度,生成完全个性化定制的教学内容和测试题。它甚至能依据你当前的环境背景,修改教学材料,同时根据实际情况随时调整教学的难度和深度。</p>
<p>我认为新时代将对人类个体产生深远影响,赋予更多知识工作者更大的能力,进而拓宽他们的技能边界。在人类社会中,大规模的协作是实现更伟大目标的必要途径。然而,随着协作人数的增多,沟通和理解的成本也相应升高,这使得仅仅增加人手并不一定能带来产出的直接提升。因此,“超级个体”,10x 工程师的概念应运而生,这种情况下,一个或少数几个人能实现原来十倍的工作量,但降低团队规模也消除不少沟通损耗。或许,有一天我们会见到新一批的全能超人,他们就像文艺复兴时期的达芬奇一样,10x 工作效率和产出引领文明发展。</p>
<p>等待 AGI 降临后,整个人类文明能更高速往前发展,但这时人类本身呢?人类能和自己创造的智能体和平共处吗?如果机器得到人类的完整智慧还需要只会消耗资源的人类吗?如果机器延续的是人类的文明,那他延续生存下去的动机是什么?</p>
<p>在等待 AGI 降临之后,整个人类文明将以更快的速度向前发展,但是人类自身会发生什么变化呢?我们能和自己创造的智能体和平共处吗?如果机器得到了人类的全部智慧,会不会还需要我们这样只会消耗资源的人类?如果说机器延续的是人类的文明,那么它的生存动机又是什么呢?</p>
<p>可能等待我们的是像天网一样毁灭人类的科技巨兽,也有可能是像阿童木一样守护人类的友善机器人。</p>
10 Years Since Last Post
2023-01-19T00:00:00Z
https://thekaiway.com/2023/01/19/blog-over-a-decade/
<p>距离上一篇博文已经是10年之前,归来的这一篇对这中间空缺时间做个回顾总结。</p>
<p><strong>大环境变化</strong></p>
<p>生活的环境里,近10年中国疯狂基建,伴随移动互联网化,带来的生活变革非常明显,自我浅显地感觉是一二线中国城市可能是全世界最便利的。当然这是在严重牺牲隐私为代价下达成的,个人信息在政府机关和私人企业中并未得到较好保护。</p>
<p>这个博客用的 octopress 在 2015 年后已经停止了更新,SSG 这个概念已经被广泛使用在现代前端工程中,Github Pages 的原生 Jekyll 也做出不少限制。Markdown 这个语言的扩展能力得到充分进化,基于模版引擎的代码高亮方案已被扔进垃圾桶,即使复杂图表也有足够解决方案,这个格式成为现代笔记软件基石。后续可能会把博客迁移到 Hugo 生成方案,再换个主题让读者明显感受变化。</p>
<p>之前写蛮多的 Ruby on Rails 相关内容,现在这个技术栈的热度和使用范围已比当年低几个量级,后续会再补充一篇作为系列收尾。当代技术栈在我视野里,用于业务开发中 Golang 为年轻主流,Java 为稳定的最多沉淀的选择,还有前端崛起与客户端日渐式微。伴随技术栈的变化,部署方案也完全进入 k8s 时代,或者基于应用全托管方案,很少有看到手工脚本管理集群,交付开箱即用的 docker 镜像成为软件说明的必备信息。</p>
<p>加密数字货币吸引足够多的目光,在世界范围的金融世界已掀起风波,如何去为世界创造更多积极影响可能是下一步。AI 进入日常生活领域,编程补全助手能极大提高效率,简单创作需求(如插图绘画)已被支持,还有打磨书写的工具。</p>
<p><strong>自身变化</strong></p>
<p>从创业退出到加入一家加密数字货币初创公司持续增长完成两轮融资。在一个前沿并且特别有争议的领域,虽然很难被大众理解,对我来说是个很有意思的事情。</p>
<p>作为团队领导者加入,一边构建产品技术支撑,一边管理团队,能遇到在一个发展中的团队成为核心角色的机会并不多见,我有幸遇到并参与其中,这个过程中也得到不小的成长。当前中文技术社区,提到团队领导力和带着产品思维商业思维去思考文档的内容不多,可能可以成为我未来的写作素材。</p>
<p>伴随三年疫情公司也逐渐变成远程优先的方式,很长一段时间在家办公。到办公室上班会天然伴随着场地切换而形成一种节奏,远程工作需要切换并制定一种新的工作生活节奏。像我比较幸运,家里有个房间作为书房,可布置为一个舒适的远程办公环境。</p>
<p>当有小孩子并且住进自己攒钱买的房子后,心态上会倾向于寻求长远稳定的工作生活节奏。</p>
<p><strong>回到持续输出状态</strong></p>
<p>学习是一个捕获,整理,输出的循环。正如费曼所说的「one can only learn by teaching.」,通过内容输出去尝试讲解或教会别人是一种有效检验自我学习的方式。</p>
<p>当代信息保障的时代,这几年间可以获取信息/知识的渠道非常多,特别是知识付费已经不是一个需要鼓吹的事情,某种程度上已经是常识,会写能写的输出者们已经创造大量的 Newsletter 和音视频教程内容。新一代的笔记软件结合间歇性日记,大纲式笔记,卡片式笔记,这些方法论,有效降低了捕获输入的心智负担。</p>
<p>输出在当前时代的意义,我理解在于留下更多个人思想痕迹,小岛秀夫想在死后成为游戏AI,硬核科幻英剧也描述人死后通过生前各种记录可以按照各种材料重塑一个与原来一样思考和反应的AI</p>
<p>整理升级这个博客过程,才发现之前发布 100 多篇博客,超出我对自我表达欲的认知,最早记得是在一个论坛的博客系统里写作,后面由于关闭托管没了,后续托管到 Github Pages。这几年公司团队成长之后,我在内部日常一直有组织信息输出内容,但可能会和具体工作内容相关。</p>
<p>爱因斯坦说的「Everything should be made as simple as possible, but not simpler.」,输出过程中把事情讲得简单通透不是件容易的事情,对于复杂事物需要配合清晰合理的示意图做解释。</p>
<p>2023 年重启博客写作,这里公开立个目标,平均每月能发布一篇,来年的总结会做个回顾总结。</p>
<p>完。</p>
Read and Write ActiveRecord Attribute
2013-09-08T00:00:00Z
https://thekaiway.com/2013/09/08/read-write-activerecord-attribute/
<p>上一节讲完了ActiveRecord的对象怎么从是数据库里取出来,但距离数据最终的读写其中还有不少的处理过程。比如模型的属性在读取时需要做出一些相应的转换,同理在修改了模型属性之后回写数据库的时候也需要做转换。另外ActiveRecord使用了Ruby的动态特性为所有的属性读写都生成了与属性名相对应的方法,让开发者能更加便捷地访问所需要的属性值。</p>
<h2 id="" tabindex="-1">原始数据 <a class="header-anchor" href="https://thekaiway.com/2013/09/08/read-write-activerecord-attribute/">#</a></h2>
<p>首先来看看数据库取出的数据怎样存放到对象中,以下是相应的代码,<code>instantiate</code>方法的解释请参考<a href="https://thekaiway.com/2013/07/26/assemble-ar-object">Assemble ActiveRecord Object</a></p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token comment"># file: active_record/persistence</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">instantiate</span></span><span class="token punctuation">(</span>record<span class="token punctuation">,</span> column_types <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
column_types <span class="token operator">=</span> <span class="token keyword">self</span><span class="token punctuation">.</span>decorate_columns<span class="token punctuation">(</span>column_types<span class="token punctuation">.</span>dup<span class="token punctuation">)</span>
klass<span class="token punctuation">.</span>allocate<span class="token punctuation">.</span>init_with<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">'attributes'</span></span> <span class="token operator">=></span> record<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">'column_types'</span></span> <span class="token operator">=></span> column_types<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token comment"># file: active_record/core_</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">init_with</span></span><span class="token punctuation">(</span>coder<span class="token punctuation">)</span>
<span class="token variable">@attributes</span> <span class="token operator">=</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span>initialize_attributes<span class="token punctuation">(</span>coder<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'attributes'</span></span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment"># 其他初始化过程 bla bla bla</span>
<span class="token keyword">self</span>
<span class="token keyword">end</span></code></pre>
<p>可以看到,数据库里的每条记录从数据库查出来之后,会直接塞进每个对象的<code>@attributes</code>实例变量中,这里包括了所有的字段的名字和值。这个原始的记录数据是个以属性名为键,原始内容为值的哈希表。</p>
<p>ActiveRecord提供了接口可以直接访问原始数据,这种方式就是直接对<code>@attributes</code>进行读取。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">Post<span class="token punctuation">.</span>first<span class="token punctuation">.</span>attributes_before_type_cast <span class="token comment"># 读取所有原始数据</span>
Post<span class="token punctuation">.</span>first<span class="token punctuation">.</span>read_attribute_before_type_cast<span class="token punctuation">(</span><span class="token symbol">:id</span><span class="token punctuation">)</span> <span class="token comment"># 读取ID字段的原始数据</span>
Post<span class="token punctuation">.</span>first<span class="token punctuation">.</span>id_before_type_cast <span class="token comment"># 同上,ActiveModel::AttributeMethods生成的DSL</span></code></pre>
<h2 id="-1" tabindex="-1">读取属性 <a class="header-anchor" href="https://thekaiway.com/2013/09/08/read-write-activerecord-attribute/">#</a></h2>
<p>通常我们不会直接访问原始数据,而是访问已经转化好的数据。ActiveRecord提供了几种形式来访问处理过属性</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">post <span class="token operator">=</span> <span class="token class-name">Post</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token symbol">name</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"First Post"</span></span><span class="token punctuation">)</span>
post<span class="token punctuation">.</span>name
post<span class="token punctuation">[</span><span class="token symbol">:name</span><span class="token punctuation">]</span>
post<span class="token punctuation">.</span>attributes<span class="token punctuation">[</span><span class="token symbol">:name</span><span class="token punctuation">]</span>
post<span class="token punctuation">.</span>read_attribute<span class="token punctuation">(</span><span class="token symbol">:name</span><span class="token punctuation">)</span> <span class="token comment">#=> "First Post"</span></code></pre>
<p>以上几种的模型属性访问其实都通过同一个入口进行访问,这个入口就是<code>read_attribute</code>。以上几个属性读取的实现有兴趣可以自行翻查源码,我们来重点讲解<code>read_attribute</code>。</p>
<p><code>read_attribute</code>的基本逻辑如以下代码所示,这里是精简过的代码</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># file: active_record/attribute_methods/read.rb</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">read_attribute</span></span><span class="token punctuation">(</span>attr_name<span class="token punctuation">)</span>
name <span class="token operator">=</span> attr_name<span class="token punctuation">.</span>to_s
column <span class="token operator">=</span> <span class="token variable">@column_types</span><span class="token punctuation">[</span>name<span class="token punctuation">]</span>
value <span class="token operator">=</span> <span class="token variable">@attributes</span><span class="token punctuation">.</span>fetch<span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> block_given<span class="token operator">?</span> <span class="token operator">?</span> <span class="token keyword">yield</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">nil</span>
<span class="token punctuation">}</span>
column<span class="token punctuation">.</span>type_cast value
<span class="token keyword">end</span></code></pre>
<ol>
<li>查找对应对应的数据库字段(AR::ConnectionAdapters::Column)实例,即获得该属性在数据库里对应的类型</li>
<li>从原始数据<code>@attributes</code>里查找出对应的值</li>
<li>使用对应的字段类型来转换该属性的原始值</li>
</ol>
<h2 id="-2" tabindex="-1">类型转换 <a class="header-anchor" href="https://thekaiway.com/2013/09/08/read-write-activerecord-attribute/">#</a></h2>
<p>数据库表与AR对象的映射会在对应的章节里讲解,本篇只讲解和字段读写相关的部分,以下是类型转换的实现</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">type_cast</span></span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token keyword">nil</span> <span class="token keyword">if</span> value<span class="token punctuation">.</span><span class="token keyword">nil</span><span class="token operator">?</span>
<span class="token keyword">return</span> coder<span class="token punctuation">.</span>load<span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token keyword">if</span> encoded<span class="token operator">?</span>
klass <span class="token operator">=</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token keyword">class</span>
<span class="token class-name">case</span> type
<span class="token keyword">when</span> <span class="token symbol">:string</span><span class="token punctuation">,</span> <span class="token symbol">:text</span> <span class="token keyword">then</span> value
<span class="token keyword">when</span> <span class="token symbol">:integer</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>value_to_integer<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">when</span> <span class="token symbol">:float</span> <span class="token keyword">then</span> value<span class="token punctuation">.</span>to_f
<span class="token keyword">when</span> <span class="token symbol">:decimal</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>value_to_decimal<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">when</span> <span class="token symbol">:datetime</span><span class="token punctuation">,</span> <span class="token symbol">:timestamp</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>string_to_time<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">when</span> <span class="token symbol">:time</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>string_to_dummy_time<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">when</span> <span class="token symbol">:date</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>value_to_date<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">when</span> <span class="token symbol">:binary</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>binary_to_string<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">when</span> <span class="token symbol">:boolean</span> <span class="token keyword">then</span> klass<span class="token punctuation">.</span>value_to_boolean<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token keyword">else</span> value
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>我们看到除了字符串和文本之外的类型都需要根据其逻辑类型,进行转换的方法主要是解析内容并实例化到对应的类型。</p>
<p>写入属性的情况与读取属性的逻辑基本相同,并且Column里有一个与<code>type_cast_for_write</code>对应的<code>type_cast_for_write</code>方法,用来处理写入的类型转换。</p>
<p>在扩展性方面,Postgres的链接代码重写了类型转换方法以支持它丰富的数据类型。</p>
<h2 id="-3" tabindex="-1">自定义序列化字段 <a class="header-anchor" href="https://thekaiway.com/2013/09/08/read-write-activerecord-attribute/">#</a></h2>
<p>ActiveRecord支持将Ruby对象直接序列化到数据库中,并且可以制定序列化的方式,默认使用的是YAML。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># file: active_record/attribute_methods/serialization.rb</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">serialize</span></span><span class="token punctuation">(</span>attr_name<span class="token punctuation">,</span> class_name <span class="token operator">=</span> <span class="token builtin">Object</span><span class="token punctuation">)</span>
<span class="token keyword">include</span> Behavior
coder <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token symbol">:load</span><span class="token punctuation">,</span> <span class="token symbol">:dump</span><span class="token punctuation">]</span><span class="token punctuation">.</span>all<span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token operator">|</span>x<span class="token operator">|</span> class_name<span class="token punctuation">.</span>respond_to<span class="token operator">?</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token punctuation">}</span>
class_name
<span class="token keyword">else</span>
Coders<span class="token double-colon punctuation">::</span><span class="token class-name">YAMLColumn</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span>class_name<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">self</span><span class="token punctuation">.</span>serialized_attributes <span class="token operator">=</span> serialized_attributes<span class="token punctuation">.</span>merge<span class="token punctuation">(</span>attr_name<span class="token punctuation">.</span>to_s <span class="token operator">=></span> coder<span class="token punctuation">)</span>
<span class="token keyword">end</span></code></pre>
<p>在实现上通过Coder这种形式来在属性的读写时,调用Coder的<code>load</code>与<code>dump</code>方法进行预先处理。</p>
<p>这里指定的Coder并不需要特定的类型,它只需要实现接受一个参数的<code>load</code>和<code>dump</code>方法就可以作为一个Coder。</p>
<h2 id="-4" tabindex="-1">属性方法的动态生成 <a class="header-anchor" href="https://thekaiway.com/2013/09/08/read-write-activerecord-attribute/">#</a></h2>
<p>ActiveRecord模型利用Ruby的元编程能力,在运行时生成与数据库字段名相对应的读写方法。具体的方式就是使用<code>method_missing</code>和<code>respond_to?</code>,在找不到对应的方法时,ActiveRecord会在以上的两个方法里调用<code>define_attribute_methods</code>去生成<strong>所有的属性</strong>读写方法。</p>
<p>这个<code>define_attribute_methods</code>方法有两个定义,其中一个定义在ActiveRecord::AttributeMethods,另一个定义在ActiveModel::AttributeMethods模组中,其中实质性的定义是在ActiveModel中,ActiveRecord继承并在这之上加了一些线程安全和方法是否已经生成的标记。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># file: active_model/attribute_methods</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">define_attribute_methods</span></span><span class="token punctuation">(</span><span class="token operator">*</span>attr_names<span class="token punctuation">)</span>
attr_names<span class="token punctuation">.</span>flatten<span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token punctuation">{</span> <span class="token operator">|</span>attr_name<span class="token operator">|</span> define_attribute_method<span class="token punctuation">(</span>attr_name<span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token keyword">end</span></code></pre>
<p>ActiveRecord里无需参数的定义主要作用只是代理,将所有的字段名字传入到ActiveModel里的<code>define_attribute_methods</code>。然后遍历所有的属性名,将每个属性都传入<code>define_attribute_method</code>里。<code>define_attribute_method</code>方法比较复杂,基本的思路是遍历所有的AttributeMethodMatcher,并从Matcher拼装出需要调用的方法名。</p>
<p>这里稍微解释一下AttributeMethodMetcher,所有模型的父类ActiveRecord::Base定义了一堆的Metcher,它用来为所有属性添加方法。除了上面的读写方法和原数据访问方法外,ActiveRecord模型还定义了如下一堆属性相关的方法</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">post <span class="token operator">=</span> <span class="token class-name">Post</span><span class="token punctuation">.</span><span class="token keyword">new</span> title<span class="token operator">:</span> <span class="token string-literal"><span class="token string">"Nice Post"</span></span>
post<span class="token punctuation">.</span>title
post<span class="token punctuation">.</span>title<span class="token operator">?</span>
post<span class="token punctuation">.</span>title_before_type_cast
post<span class="token punctuation">.</span>title_changed<span class="token operator">?</span>
post<span class="token punctuation">.</span>title_change
post<span class="token punctuation">.</span>title_will_change<span class="token operator">!</span>
post<span class="token punctuation">.</span>title_was</code></pre>
<p>这类方法的定义就是通过Metcher,举个栗子,<code>{attribute}_before_type_cast</code>是这么定义的</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">attribute_method_suffix <span class="token string-literal"><span class="token string">"_before_type_cast"</span></span>
<span class="token comment">#=> #<ActiveModel::AttributeMethods::ClassMethods::AttributeMethodMatcher:0x007fb36c41ddf0</span>
<span class="token comment"># @method_missing_target="attribute_before_type_cast",</span>
<span class="token comment"># @method_name="%s_before_type_cast",</span>
<span class="token comment"># @prefix="",</span>
<span class="token comment"># @regex=/^(?:)(.*)(?:_before_type_cast)$/,</span>
<span class="token comment"># @suffix="_before_type_cast"></span></code></pre>
<p>通过这样的定义,前文提到的<code>define_attribute_method</code>的时候会调用到上面这个Matcher,然后通过<code>method_missing_target</code>调用<code>attribute_before_type_cast</code>去定义模型的<code>title_before_type_cast</code>。</p>
<p>同时在方法未定义的检查里也是通过遍历所有Matcher,找出是否为预定义的属性方法。</p>
<p>整个方法生成的故事就如是发展,在遇到未定义的方法的时候,ActiveRecord发现该方法是属性相关的方法,那么遍历所有的属性,再嵌套遍历所有的Matcher去生成所有的属性相关方法。</p>
ActionView Safe Buffer
2013-08-17T00:00:00Z
https://thekaiway.com/2013/08/17/actionview-safe-buffer/
<p>为了提高安全性,ActionView的模版在Rails 3中实现了名为SafeBuffer用来减少被<a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS攻击</a>的风险。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分, <a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部实现与设计的一本书, 欢迎阅读</p>
</blockquote>
<h2 id="xss" tabindex="-1">XSS攻击 <a class="header-anchor" href="https://thekaiway.com/2013/08/17/actionview-safe-buffer/">#</a></h2>
<p>XSS,全称为Cross-site Scripting,中文叫跨站脚本攻击。这是通过对目标网页注入脚本(最常见是JavaScript,也可以是VBScript等),然后通过这段脚本来盗取用户cookies或做跨站提交等。</p>
<p>要防止这种攻击,Rails开发者必须非常小心地处理用户输入的内容,本篇讲到SafeBuffer就是帮助开发者减小被攻击的风险。</p>
<h2 id="html-safe" tabindex="-1">HTML Safe <a class="header-anchor" href="https://thekaiway.com/2013/08/17/actionview-safe-buffer/">#</a></h2>
<p>在ActionView的Template中,默认的内容是HTML Unsafe的,HTML Unsafe的内容被拼接时会先用<code>ERB::Utils.html_escape</code>方法先处理一遍。只有两种才会被认为是HTML Safe的</p>
<ul>
<li>Numeric</li>
<li>AS::SafeBuffer的实例对象</li>
</ul>
<p>这里可能会让人出乎意料的是,SafeBuffer的实现放在ActiveSupport的String Extention里,具体定义文件在<code>active_support/core_ext/string/output_safety.rb </code>。</p>
<p>SafeBuffer被定义为String的子类,与普通的String不同是SafeBuffer的<code>html_safe</code>属性为True。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">module</span> <span class="token class-name">ActiveSupport</span> <span class="token comment">#:nodoc:</span>
<span class="token keyword">class</span> <span class="token class-name">SafeBuffer</span> <span class="token operator"><</span> <span class="token builtin">String</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span>
<span class="token variable">@html_safe</span> <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token keyword">super</span>
<span class="token keyword">end</span>
<span class="token comment"># other methods</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>另外,对于其他的对象,通过打开类的方式将Object的html_safe设置为False,而Numeric被设置为True。具体定义如下</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">Object</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">html_safe</span></span><span class="token operator">?</span>
<span class="token boolean">false</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">class</span> <span class="token class-name">Numeric</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">html_safe</span></span><span class="token operator">?</span>
<span class="token boolean">true</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>我们知道String的内容是可变的,同样SafeBuffer的内容也是可变的。出于安全性考虑SafeBuffer会将产生新对象或修改内容本身的方法,比如<code>capitalize</code>,<code>gsub</code>等等,都替换为结果是HTML Unsafe的字符串</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">class_eval <span class="token operator"><<</span><span class="token operator">-</span><span class="token constant">EOT</span><span class="token punctuation">,</span> __FILE__<span class="token punctuation">,</span> __LINE__ <span class="token operator">+</span> <span class="token number">1</span>
<span class="token keyword">def</span> <span class="token comment">#{unsafe_method}(*args, &block)</span>
to_str<span class="token punctuation">.</span><span class="token comment">#{unsafe_method}(*args, &block)</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token comment">#{unsafe_method}!(*args)</span>
<span class="token variable">@html_safe</span> <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">super</span>
<span class="token keyword">end</span>
<span class="token constant">EOT</span></code></pre>
<p>比如替换后的capistalize方法是</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">capitalize</span></span><span class="token punctuation">(</span><span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">&</span>block<span class="token punctuation">)</span>
to_str<span class="token punctuation">.</span>capitalize<span class="token punctuation">(</span><span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">&</span>block<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">capitalize</span></span><span class="token operator">!</span><span class="token punctuation">(</span><span class="token operator">*</span>args<span class="token punctuation">)</span>
<span class="token variable">@html_safe</span> <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">super</span>
<span class="token keyword">end</span></code></pre>
<p>稍微解释一下方法替换的意义,在非<a href="http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist">bang方法</a>中,先调用<code>to_str</code>就将原字符串转化为普通的String,由于除了SafeBuffer外的对象都是unsafe的,通过这么转化本来HTML Safe的内容又变回了HTML Unsafe的状态。</p>
<p>当需要将内容标记为html safe状态的时候,可以调用<code>html_safe</code>方法,这个方法的原理就是构造一个新的SafeBuffer对象,代码如下</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">String</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">html_safe</span></span>
ActiveSupport<span class="token double-colon punctuation">::</span><span class="token class-name">SafeBuffer</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<h2 id="" tabindex="-1">接口 <a class="header-anchor" href="https://thekaiway.com/2013/08/17/actionview-safe-buffer/">#</a></h2>
<p>基本上所有模版语言都放出了,一些回调接口让开发者可以替换掉原有的Buffer实现。ActionView里定义的Template Handler就完成了模版语言Buffer实现的替换,比如这里的<a href="https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_view/template/handlers/erb.rb">对Erb的替换</a>。</p>
<p>一些第三方的模板语言,比如<a href="https://github.com/haml/haml">Haml</a>直接集成了SafeBuffer,<a href="https://github.com/slim-template/slim">Slim</a>通过其依赖的<a href="https://github.com/judofyr/temple">Temple</a>也集成了SafeBuffer。</p>
<h2 id="-1" tabindex="-1">参考 <a class="header-anchor" href="https://thekaiway.com/2013/08/17/actionview-safe-buffer/">#</a></h2>
<ul>
<li>http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/</li>
<li>http://www.railsdispatch.com/posts/security</li>
</ul>
Rails ujs
2013-08-16T00:00:00Z
https://thekaiway.com/2013/08/14/rails-ujs/
<p>UJS是Rails 3引入的JavaScript框架与Rails的抽象层。我们知道Rails一些Helper是依赖于JavaScript框架的,比如Ajax Form,Ajax Link等,并且在Rails 3之前默认集成的JavaScript框架是<a href="http://prototypejs.org/">Prototype</a>,再这之后才换成了社区呼声很高的<a href="http://jquery.org/">jQuery</a>。</p>
<p>如前面所说UJS是个抽象层,它需要在每个框架上实现对应的接口,比如官方实现了<a href="https://github.com/rails/jquery-ujs">jquery-ujs</a>和<a href="https://github.com/rails/prototype-ujs">prototype-ujs</a>。本篇主要以<a href="https://github.com/rails/jquery-ujs">jquery-ujs</a>为例来讲解UJS。jquery-ujs代码很短,只有500行不到,想先浏览一下整个代码可访问<a href="https://github.com/rails/jquery-ujs/blob/master/src/rails.js">Github的jquery-ujs repo</a>。</p>
<h2 id="data-confirm" tabindex="-1">data-confirm <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<p>先从最简单confirm例子入手。比如,在某些链接上让用户在点击链接后再次确认一次,我们一般会这么写</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">link_to <span class="token string-literal"><span class="token string">"Visit Other Site"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"http://www.rubyonrails.org/"</span></span><span class="token punctuation">,</span> <span class="token symbol">data</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">confirm</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"Are you sure?"</span></span> <span class="token punctuation">}</span></code></pre>
<p>这里和普通的链接不同的地方只是在于多加了一个<code>data-confirm</code>属性,然后UJS帮你实现了弹出确认框。这其中的实现方法很简单,写过jQuery的同学都会,就是监听所有链接的<code>click</code>事件,当这个被点击链接上有<code>data-confirm</code>属性时,取出<code>data-confirm</code>中的文本,弹出确认框,并根据用户的操作选择是否中断这个点击事件的处理。</p>
<h2 id="data-method" tabindex="-1">data-method <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<p>然后我们来看看,另外一种比较常见的链接用法,让链接点击时使用非GET方法请求对应的URL,代码如下</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">link_to<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Sign Out"</span></span><span class="token punctuation">,</span> sign_out_path<span class="token punctuation">,</span> <span class="token symbol">method</span><span class="token operator">:</span> <span class="token symbol">:delete</span><span class="token punctuation">)</span></code></pre>
<p>这里传入的<code>method</code>参数,生成HTML时会被转换为<code>data-method="delete"</code>。与前面一个例子一样,UJS在这个链接的click事件上监听,当这个链接有<code>data-method</code>属性时,创建一个隐藏的<code>form</code>标签,并附带上名为<code>_method</code>参数,值为<code>data-method</code>属性值的<code>input</code>标签,最后将这个构造的表单提交。</p>
<p>通过这样的小技巧,Rails开发者就能通过<code><a></code>标签以任何想要的HTTP Method请求对应的链接。</p>
<h2 id="ajax-form" tabindex="-1">Ajax Form <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<p>在Rails 3之后的<code>form_tag</code>和<code>form_for</code>上传入<code>remote: true</code>就能实现表单的Ajax提交,同样这个事情,UJS也是通过监听所有的Form标签的<code>submit</code>事件,然后检测标签上的<code>data-remote</code>属性来实现的。</p>
<p>对于开发者,在传入了<code>remote: true</code>之后要怎样去插入对应的Ajax处理器呢?UJS在对应Ajax提交阶段上触发了Rails自定义的<code>ajax:xxx</code>事件。</p>
<pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function-variable function">beforeSend</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">xhr<span class="token punctuation">,</span> settings</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>settings<span class="token punctuation">.</span>dataType <span class="token operator">===</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
xhr<span class="token punctuation">.</span><span class="token function">setRequestHeader</span><span class="token punctuation">(</span><span class="token string">'accept'</span><span class="token punctuation">,</span> <span class="token string">'*/*;q=0.5, '</span> <span class="token operator">+</span> settings<span class="token punctuation">.</span>accepts<span class="token punctuation">.</span>script<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> rails<span class="token punctuation">.</span><span class="token function">fire</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> <span class="token string">'ajax:beforeSend'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>xhr<span class="token punctuation">,</span> settings<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">success</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">data<span class="token punctuation">,</span> status<span class="token punctuation">,</span> xhr</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
element<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'ajax:success'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>data<span class="token punctuation">,</span> status<span class="token punctuation">,</span> xhr<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">complete</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">xhr<span class="token punctuation">,</span> status</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
element<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'ajax:complete'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>xhr<span class="token punctuation">,</span> status<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">error</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">xhr<span class="token punctuation">,</span> status<span class="token punctuation">,</span> error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
element<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'ajax:error'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>xhr<span class="token punctuation">,</span> status<span class="token punctuation">,</span> error<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>所以基于UJS我们可以这样直接在<code>form</code>元素上绑定上对应的Ajax提交处理代码</p>
<pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#myform"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"ajax:success"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"Post successfully:)"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"ajax:error"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"Post fail:("</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>UJS在Ajax表单提交了之后,还会将该表单中的<code>button</code>和<code>input[type='submit']</code>都加上<code>disable</code>属性,防止用户多次点击引发多次提交。</p>
<p>此外,UJS实现的Ajax Form上还有两个特殊的事件</p>
<ul>
<li><code>ajax:aborted:required</code> 当表单提交的时候,有未填的<code>input</code>标签时会触发这个事件,你可以选择去处理</li>
<li><code>ajax:aborted:file</code> 我们知道通过正常的方式是无法通过AJAX来提交文件的,当表单里包含了文件字段的时候,这个事件会被触发,在这里可以去实现自己的文件提交逻辑。比如<a href="https://github.com/JangoSteve/remotipart">remotipart</a>通过这个事件实现了Ajax Form的文件提交。</li>
</ul>
<h2 id="" tabindex="-1">选择器 <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<p>UJS的所有功能都通过预设的选择器,绑定事件处理逻辑到对应元素上,以下是选择器的定义</p>
<pre class="language-javascript" tabindex="0"><code class="language-javascript">$<span class="token punctuation">.</span>rails <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment">// Link elements bound by jquery-ujs</span>
<span class="token literal-property property">linkClickSelector</span><span class="token operator">:</span> <span class="token string">'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]'</span><span class="token punctuation">,</span>
<span class="token comment">// Button elements boud jquery-ujs</span>
<span class="token literal-property property">buttonClickSelector</span><span class="token operator">:</span> <span class="token string">'button[data-remote]'</span><span class="token punctuation">,</span>
<span class="token comment">// Select elements bound by jquery-ujs</span>
<span class="token literal-property property">inputChangeSelector</span><span class="token operator">:</span> <span class="token string">'select[data-remote], input[data-remote], textarea[data-remote]'</span><span class="token punctuation">,</span>
<span class="token comment">// Form elements bound by jquery-ujs</span>
<span class="token literal-property property">formSubmitSelector</span><span class="token operator">:</span> <span class="token string">'form'</span><span class="token punctuation">,</span>
<span class="token comment">// Form input elements bound by jquery-ujs</span>
<span class="token literal-property property">formInputClickSelector</span><span class="token operator">:</span> <span class="token string">'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])'</span><span class="token punctuation">,</span>
<span class="token comment">// Form input elements disabled during form submission</span>
<span class="token literal-property property">disableSelector</span><span class="token operator">:</span> <span class="token string">'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]'</span><span class="token punctuation">,</span>
<span class="token comment">// Form input elements re-enabled after form submission</span>
<span class="token literal-property property">enableSelector</span><span class="token operator">:</span> <span class="token string">'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled'</span><span class="token punctuation">,</span>
<span class="token comment">// Form required input elements</span>
<span class="token literal-property property">requiredInputSelector</span><span class="token operator">:</span> <span class="token string">'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])'</span><span class="token punctuation">,</span>
<span class="token comment">// Form file input elements</span>
<span class="token literal-property property">fileInputSelector</span><span class="token operator">:</span> <span class="token string">'input[type=file]'</span><span class="token punctuation">,</span>
<span class="token comment">// Link onClick disable selector with possible reenable after remote submission</span>
<span class="token literal-property property">linkDisableSelector</span><span class="token operator">:</span> <span class="token string">'a[data-disable-with]'</span>
<span class="token punctuation">}</span></code></pre>
<p>这提供可配置对应选择器的机会,比方说使用某个jQuery插件,它通过<code>data-remote</code>去标记别的事情。那么在不修改这个插件的情况下想让UJS继续工作,我们可以重新配置UJS的选择器</p>
<pre class="language-javascript" tabindex="0"><code class="language-javascript">$<span class="token punctuation">.</span>rails<span class="token punctuation">.</span>formSubmitSelector <span class="token operator">=</span> <span class="token string">'form([data-ajax-form])'</span><span class="token punctuation">;</span></code></pre>
<h2 id="csrf-token" tabindex="-1">CSRF Token <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<p>UJS还会在每次表单提交上自动附带上CSRF Token。Rails 3之后强制所有的非幂等HTTP请求需要带上CSRF token作安全校验,用来防止XSS攻击。这就要求开发者在每次写Ajax请求的时候,都需要手动把这部分的token带上,UJS也通过jQuery的<a href="http://api.jquery.com/jQuery.ajaxPrefilter/">ajaxPrefilter</a>接口,让每次的Ajax请求都自动附带上CSRF token。</p>
<p>另外,每次UJS初始化时,会为页面上所有表单都加上带有CSRF Token的隐藏<code>input</code>标签,让表单在提交时都能自动带上CSRF Token。</p>
<h2 id="ujs" tabindex="-1">从UJS学到了什么 <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<ul>
<li>jquery-ujs的所有事件绑定都是绑定在document,等到事件触发后再分发到对应的事件处理逻辑里,不需要在初始化时查找对应的元素并绑定事件</li>
<li>充分利用HTML5的<code>data</code>属性来解耦事件处理逻辑,将各种参数序列化到<code>data</code>属性</li>
<li>利用jQuery的自定义事件更好地定制自己的事件处理流程</li>
</ul>
<h2 id="-1" tabindex="-1">参考 <a class="header-anchor" href="https://thekaiway.com/2013/08/14/rails-ujs/">#</a></h2>
<ul>
<li>http://www.alfajango.com/blog/rails-3-remote-links-and-forms/</li>
<li>http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/</li>
<li>http://railscasts.com/episodes/205-unobtrusive-javascript</li>
<li>http://net.tutsplus.com/tutorials/javascript-ajax/using-unobtrusive-javascript-and-ajax-with-rails-3/</li>
</ul>
ActionView基本架构
2013-08-10T00:00:00Z
https://thekaiway.com/2013/08/10/actionview-architect/
<p><code>ActionView</code>是MVC中最后一公里, 将内容拼装完成, 等待服务器将其最终结果传输到用户端。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分, <a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部实现与设计的一本书, 欢迎阅读</p>
</blockquote>
<p><strong>注</strong> 下文中<code>ActionView</code>在作为命名空间时全部简写为<code>AV</code>。</p>
<p>在开发者的角度看来, <code>ActionView</code>的处理过程似乎没有太多值得一提的事情, 大部分情况下需要关心的只是某个Helper要传哪些参数进去。但实际其中ActionView完成的事情并不简单, 这里主要有4个步骤</p>
<ol>
<li>需要将路由生成方法和各种Helper方法绑定到渲染的上下文中, 并绑定在当前Controller中的实例变量</li>
<li>需要有对象负责知道怎么去找到对应的模版。Rails能做到的查找规则极为灵活, 可以查找某个对应Format(如json), 对应Locale(如zh-CN), 从文件系统或数据库里找到这个对应的模版。</li>
<li>找到了这个模版后, 需要知道怎样去编译这个模版。Ruby世界有很多的模板语言, 比如Erb, Builder, Haml和Slim等等, Rails需要找到对应的编译方式去编译它们。</li>
<li>将编译好的模版, 加上之前的渲染上下文, 拼装得到最后的结果。</li>
</ol>
<p>在Controller里调用到ActionView接口只有以下三个</p>
<ul>
<li>AV::Base 维护整个渲染过程的上下文(View Context)</li>
<li>AV::LookupContext 维护模版查找的上下文</li>
<li>AV::Renderer 渲染接口</li>
</ul>
<p>渲染的调用逻辑基本集中在<code>AbstractController::Rendering</code>这个模组, 下图为其中大概的逻辑关系</p>
<p><img src="https://thekaiway.com/images/action_view_arch.png" alt="av"></p>
<p>图中的View Context就是上文提到的<code>AV::Base</code>, View Assigns指的是在Controller中设置的各种实例变量。最后Controller通过调用<code>AV::Renderer#render</code>去渲染出最终的结果。</p>
<p>关于ActionView内部具体的各个机制会在后续章节中一一讲解。</p>
<h2 id="" tabindex="-1">参考 <a class="header-anchor" href="https://thekaiway.com/2013/08/10/actionview-architect/">#</a></h2>
<p>Rails Core Team里的<a href="https://twitter.com/josevalim">José Valim</a>可能是对ActionPack中大部分实现最为熟悉的人之一, 以下列出的书以及Presentation就讲到了这部分内容。</p>
<ul>
<li><a href="http://pragprog.com/book/jvrails2/crafting-rails-4-applications">Crafting Rails 4 Applications</a></li>
<li><a href="http://www.youtube.com/watch?v=TslkdT3PfKc">Rails Conf 2013 You've got a Sinatra on your Rails by José Valim</a></li>
</ul>
配置Git Push策略
2013-07-30T00:00:00Z
https://thekaiway.com/2013/07/30/config-your-git-push-strategy/
<p>我发现大部分人都没有配置过Git Push的策略, 但Git目前给出的默认策略并不是一个友好的机制。</p>
<p>先来看一下所有的Git Push的策略</p>
<ul>
<li>nothing 什么都不干(估计只是测试用的)</li>
<li>matching 本地所有的分支都Push上去, 只是默认的机制</li>
<li>upstream/tracking 当本地分支有upstream(也就是有对应的远程分支)时Push到对应的远程分支</li>
<li>simple 和upstream一样, 但不允许将本地分支提交到远程不一样名字的分支</li>
<li>current 把当前的分支Push到远程的同名分支</li>
</ul>
<p>Git 1.x的默认策略是<code>matching</code>, 在Git 2.0之后<code>simple</code>会成为新的默认策略。另外<code>tracking</code>是<code>upstream</code>的别名, 但已经被标记为deprecated。</p>
<p><code>matching</code>不友好之处在于我们的大部分情况都是同步本地的当前分支到远程,你会看到一长串的本地Branch(如果你本地有二三十个的话那就被刷屏了)。如果除了当前分支外的其他分支有新的内容的话,你会看到好多push fail的提示。</p>
<p><code>simple</code>这个选项是非常安全的选项, 至少能阻止新手误操作覆盖远程分支, 所以Git会在2.0时将其作为默认策略。</p>
<p>大部分情况我们想要做的只是Push当前的分支, 那么最适合的就是upstream。我们可以通过<code>git config</code>去配置采用<code>upstream</code>策略。具体的设置命令如下</p>
<pre class="language-shell" tabindex="0"><code class="language-shell"><span class="token function">git</span> config <span class="token parameter variable">--global</span> push.default upstream</code></pre>
<p><em>注</em> 本文发布时最新的Git是1.8.3.x</p>
<p>参考</p>
<ul>
<li>http://git-scm.com/docs/git-config</li>
<li>https://www.kernel.org/pub/software/scm/git/docs/git-push.html</li>
<li>http://stackoverflow.com/questions/948354/git-push-current-branch</li>
</ul>
写作 积累 快乐
2013-07-28T00:00:00Z
https://thekaiway.com/2013/07/28/write-more/
<p>现在写独立博客的人越来越少, 大家都跑到新媒体上去发表自己的观点。我现在倾向于在独立博客上写, 能保持自己的节奏。现在还一直在创作着的独立博客写作者们, 比如<a href="http://blog.xdite.net/">XDite</a>, <a href="http://www.ruanyifeng.com/blog/">阮一峰的网络日志</a>和<a href="http://coolshell.cn/">酷壳</a>, 我都是非常佩服, 同时也非常羡慕他们有这么多的读者。</p>
<p>最近看到的一个博客<a href="http://www.cnblogs.com/me-sa/">坚强2002</a>, 作者在<a href="http://www.douban.com/group/topic/23688164/">豆瓣上发帖</a>说要坚持写出一千篇以上的Erlang学习笔记, 这也是非常值得敬佩的一个博客作者。</p>
<h2 id="" tabindex="-1">目的 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>最主要的目的, 正如我在<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>中写的, 是积累和沉淀, 在工作了这么些年后总想到能留下什么。</p>
<p>另外一个激发我去写出更多内容的是由于我的同事们, 由于我现在的情况, 会带着几个经验较浅的同事, 他们会遇到好多问题都是没接触过的, 而他们特别的好学, 都会刨根揭底地问我一些蛮细节的问题, 所以我希望能把我知道的知识传播出去,告诉更多人。</p>
<p>写作本身也是个锻炼, 锻炼如何理清思路, 如何清晰地表达。</p>
<p>写作是创作, 创作本身就是件快乐的事。上星期, 有读过我写的<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的朋友, 接着在Gtalk上和我讨论其中的问题。我非常开心, 有人认真看过了我写出来的内容。</p>
<h2 id="-1" tabindex="-1">历史 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>我写独立博客的历史可以追溯到2008年, 那时还是买的<a href="http://dreamhost.com/">Dream Host</a>的服务器, 采用的和邮箱一样, chenk85.com 这个域名。但那时大部分其实还是流水账和一些翻译的内容。当时觉得翻译文章又能学到一些东西, 又能学习英语, 就做了比较多的这方面的事情。</p>
<p>后来工作之后, 没有太多时间花在写博客上, 直到六月份计划写<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>, 才慢慢地特意腾出时间来写博客。</p>
<p>当然我在公司的博客上也写了几篇文章</p>
<ul>
<li><a href="http://easyread.ly/blogs/cros-iframe-autoresize-via-postmessage">使用HTML5 postMessage处理跨域iFrame的高度自适应</a></li>
<li><a href="http://easyread.ly/blogs/qian-yi-redis-shu-ju-ku">迁移Redis数据库</a></li>
<li><a href="http://easyread.ly/blogs/chef">使用Chef自动化服务器配置</a></li>
</ul>
<h2 id="-2" tabindex="-1">选题 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>在工作了这么多年, 我的EverNote上也积累了上千份的笔记, 有好多的题目已经积累了材料可以写出来, 比如下面的这些</p>
<ul>
<li>电子书相关, 如豆瓣Web的实现, EPub及其他电子格式的处理</li>
<li>Git/Vim/Tmux等日常工具的心得</li>
<li>常用Ruby社区工具和Rubygem的用法, 设计与实现, 如Capistrano/Bundler</li>
<li>Redis Schema Design Patterns</li>
<li>冷门但有趣的Rubygem的介绍</li>
<li>Rack middleware教程</li>
<li>单页JavaScript应用开发</li>
<li>Dev-ops相关的介绍和教程</li>
<li>Firefox和Chrome插件开发</li>
<li>Rails社区大牛们的介绍以及八卦</li>
<li>...</li>
</ul>
<p>对于自己工作中用到但还不够熟悉的, 比如Android/iOS开发方面, 或者是我自己爱好但是没有太多实际经验的技术, 我不会贸然去写。我觉得写出内容来是有责任的, 如果你随便写写出来误导了别人, 是个非常大的罪过。</p>
<h2 id="-3" tabindex="-1">速度 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>目前我的博客写作速度很慢, 每篇文章至少要写3个小时以上。写<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>系列的时候, 每篇都要花上一整天的时间。即使本来就已经把脉络里清楚出来了, 重点也写下来了, 就是最终成文的时候, 为了表达上的顺畅和清晰, 需要不断地做修改, 这个过程占据最多的时间。</p>
<p>一些技术分析之类的文章, 把别人写出来的上千行代码总结成精简的原理不是件容易的事。我不喜欢大段贴代码, 在讲述某个事情时大段贴代码, 我觉得是写作者本身并不能用文字去浅显地说明这个中的道理, 甚至写作者本身就没弄明白。基于个中的难度, 写作就是件耗时的事情。</p>
<h2 id="-4" tabindex="-1">学习写作 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>我觉得写作是个技能, 也有科学的学习方法。我订了一个阅读列表, 帮助我学习如何写作</p>
<ul>
<li><a href="http://book.douban.com/subject/1433835/">The Elements of Style</a></li>
<li><a href="http://book.douban.com/subject/1020644/">金字塔原理</a></li>
<li><a href="http://book.douban.com/subject/10544265/">Technical Blogging</a></li>
<li><a href="http://book.douban.com/subject/3169024/">ProBlogger</a></li>
</ul>
<p>这里是这个<a href="http://book.douban.com/doulist/2642048/">豆列</a>。</p>
<h2 id="-5" tabindex="-1">计划和目标 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>对于如何去宣传自己写的内容, 我更希望的是有人看到我写出来的内容后, 自动地去扩散, 去告诉他们的朋友说这里的内容很好, 过来这里看一下。</p>
<p>我给自己订的写作目标是每周至少一篇, 这样一年就可以写出52篇。由于是定量的计划, 如果这周有什么事情, 下周一定会补上。</p>
<h2 id="-6" tabindex="-1">感谢 <a class="header-anchor" href="https://thekaiway.com/2013/07/28/write-more/">#</a></h2>
<p>最后感谢<a href="http://weibo.com/venuscham">我老婆</a>, 她每周末都会牺牲一些让我陪她的时间给我写作。以及要感谢所有阅读我博客的读者们, 你们是我继续写作的巨大推动力。如果有什么问题, 请回复或私下联系我, <a href="https://twitter.com/_kaichen">Twitter</a>或者Gtalk(chenk85 AT gmail.com)都可以。</p>
ActiveRecord 对象的拼装
2013-07-26T00:00:00Z
https://thekaiway.com/2013/07/26/assemble-ar-object/
<p>Rails开发者们写得最多的逻辑,一般在Model这一级, 很多时候就是在操作ActiveRecord对象。这些对象是怎样构造拼装出来的, 它们持有哪些状态,并且怎样持有状态的呢?这就是本文要讨论的内容。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分,<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部实现与设计的一本书,欢迎阅读</p>
</blockquote>
<p><strong>注意</strong> ActiveRecord对象, 在下文都简称为AR对象。</p>
<p>AR对象有两种状态, 要么是已经持久化, 要么还未持久化。它们只通过以下两个入口构造出来</p>
<ul>
<li>initialize</li>
<li>init_with</li>
</ul>
<p>查询的方式得到的结果AR对象, 都是已持久化状态的, 都通过<code>init_with</code>方法构造出来。它的入口基本来自于数据查询的源头<code>find_by_sql</code>方法</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">find_by_sql</span></span><span class="token punctuation">(</span>sql<span class="token punctuation">,</span> binds <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment"># 发送查询到数据库 bla bla bla</span>
result_set<span class="token punctuation">.</span>map <span class="token punctuation">{</span> <span class="token operator">|</span>record<span class="token operator">|</span> instantiate<span class="token punctuation">(</span>record<span class="token punctuation">,</span> column_types<span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token keyword">end</span></code></pre>
<p>这里的<code>instantiate</code>的实现是这么调用的, <code>class.allocate.init_with</code>, 即分配好内存后调用<code>init_with</code>方法构造出对象。</p>
<p>通过<code>new</code>或者是关联对象上的<code>build</code>方法构造出来AR对象, 即未持久化的, 都通过<code>initialize</code>方法构造出来。</p>
<p>这两个不同途径的最大不同就是得到的持久化状态不同。判断是否持久化通过<code>persisted?</code>方法来得到</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">persisted</span></span><span class="token operator">?</span>
<span class="token operator">!</span><span class="token punctuation">(</span>new_record<span class="token operator">?</span> <span class="token operator">||</span> destroyed<span class="token operator">?</span><span class="token punctuation">)</span>
<span class="token keyword">end</span></code></pre>
<p>在AR对象里持久化状态, 由一个名为<code>new_record</code>和一个名为<code>destroyed</code>的布尔型实例变量标记决定。在构造未持久化状态的对象时就是将<code>new_record</code>设置为true, 反之则是false。而无论哪种方式构造出来的对象, 它的<code>destroyed</code>标记都为false, 因为你不可能查询出一个不存在的AR对象, 也不可能创建还未持久化就被删除的AR对象。这个事实反映了<a href="http://www.martinfowler.com/eaaCatalog/activeRecord.html">ActiveRecord</a>这个模式的本质,即对象与数据库记录一一对应。</p>
<p>关于持久化状态的变更, 我们先来说说<code>destroyed</code>。<code>destroyed</code>这个标记, 它的状态变化只通过两个API能改变, <code>delete</code>和<code>destroy</code>(这里省略了<code>destory!</code>, 因为<code>destory!</code>也是调用的<code>destroy</code>的)。在AR对象里, 被标记为<code>destroyed</code>的对象不会马上消失, 只有离开了作用域后才会被回收。</p>
<p>接下来是<code>new_record</code>标记, 它的变更只通过<code>create_record</code>这个API。道理也很浅显, 只有这个对象被写入到数据库后才真正地摆脱new这种状态。而所有的比如<code>save</code>/<code>create</code>这些最外层的API调用的都是<code>create_record</code>。</p>
<p>当然除了持久化之外, AR对象还带上了许多其他的状态, 比如监控属性改变内容的状态, 上下文的事务状态, 是否只读状态等。AR对象出于效率考虑加上缓存, 比如关联对象的缓存, 属性的缓存等。这些状态, 无论怎么途径构建出来, 都会统一通过<code>init_internals</code>去做初始化。</p>
<p>AR对象, 为了实现两次查询出同一条数据库记录可以判等, 它还覆写了<code>==</code>以及<code><=></code>等方法, 全部将其改为对比模型类和数据的主键。也就是只要是同一个模型, 且数据库记录的主键是一致的, 则认为它们是等同的。</p>
<p>最后列出文中提到的几个API的所在模块</p>
<ul>
<li>ActiveRecord::Querying
<ul>
<li><code>initialize</code></li>
<li><code>init_with</code></li>
<li><code>init_internals</code></li>
<li><code>==</code> 和 <code>eql?</code></li>
<li><code><=></code></li>
</ul>
</li>
<li>ActiveRecord::Persistence
<ul>
<li><code>persisted?</code></li>
<li><code>instantiate</code></li>
<li><code>delete</code></li>
<li><code>destroy</code></li>
<li><code>create_record</code></li>
</ul>
</li>
<li>ActiveRecord::Querying
<ul>
<li><code>find_by_sql</code></li>
</ul>
</li>
</ul>
Sass入门
2013-07-23T00:00:00Z
https://thekaiway.com/2013/07/23/getting-start-with-sass/
<p><a href="http://sass-lang.com/">Sass</a>是一个 CSS 方言, 通过编译器实现将 Sass/Scss 编译为CSS。</p>
<p>Sass具有两种语法, 一种是靠缩进去实现层级关系的Sass, 和另一种和CSS一样通过大括号实现层级的Scss。</p>
<h2 id="" tabindex="-1">特性 <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h2>
<h3 id="-1" tabindex="-1">嵌套式语法 <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h3>
<p>啥也不说了, 看代码</p>
<pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token selector">header </span><span class="token punctuation">{</span>
<span class="token property">line-height</span><span class="token punctuation">:</span> 3em<span class="token punctuation">;</span>
<span class="token selector">h1 </span><span class="token punctuation">{</span>
<span class="token property">font-weight</span><span class="token punctuation">:</span> bold<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>这种形式的代码,大大减少了CSS编写层级定义时重复性。</p>
<h3 id="-2" tabindex="-1">变量 <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h3>
<p>Sass可以通过变量去提高样式的可维护性。比如我们一套样式里, 我们常常会使用同样的间距, 但这往往要写到每个具体的元素上, 而当需要做出修改的时候就特别痛苦, 需要在每个用到的地方都去修改, 还不能简单粗暴地使用文本替换, 因为你不知道哪些是我们需要修改的。这种情况使用变量就特别适合, 示例如下</p>
<pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token property"><span class="token variable">$margin</span></span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span>
<span class="token selector">.main-content </span><span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> <span class="token variable">$margin</span><span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> <span class="token variable">$margin</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.sidebar </span><span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> <span class="token variable">$margin</span><span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> <span class="token variable">$margin</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3 id="-3" tabindex="-1">函数 <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h3>
<p>Sass内置了一些很实用的<a href="http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html">函数</a>, 它们为样式编写提供了计算能力, 比如我最喜欢的<a href="http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#lighten-instance_method">lighten</a>, 它能把给出的颜色转换为更亮的颜色。</p>
<p>函数的编写, 不仅可以使用Ruby, 也可以使用Sass本身, 比如下面是我最近写的一个函数</p>
<pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token keyword">@function</span> <span class="token function">opposite-position</span><span class="token punctuation">(</span><span class="token variable">$direction</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">@if</span> <span class="token selector"><span class="token variable">$direction</span> == "left" </span><span class="token punctuation">{</span>
<span class="token keyword">@return</span> <span class="token string">"right"</span>
<span class="token punctuation">}</span> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$direction</span> == "right" </span><span class="token punctuation">{</span>
<span class="token keyword">@return</span> <span class="token string">"left"</span>
<span class="token punctuation">}</span> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$direction</span> == "bottom" </span><span class="token punctuation">{</span>
<span class="token keyword">@return</span> <span class="token string">"top"</span>
<span class="token punctuation">}</span> <span class="token keyword">@else</span> <span class="token punctuation">{</span>
<span class="token keyword">@return</span> <span class="token string">"bottom"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h3 id="mixins" tabindex="-1">Mixins <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h3>
<p>Mixins的思路就是通过把一组样式绑定到一个名字上,然后某个层级样式可以复用这组样式。</p>
<p>这个功能是Sass最强大的功能, 这提供及其强大的代码抽象能力, 让我们可以更好地组织起庞大的样式代码。各种Sass框架就通过Mixins来开放出它们的功能。</p>
<h3 id="import" tabindex="-1">Import <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h3>
<p>Sass通过Import的形式来管理样式间的依赖, 这就像是Node.js的require。通过这个我们就能把样式打包为一个文件, 并且清晰地定义好加载顺序。</p>
<h2 id="-4" tabindex="-1">教程 <a class="header-anchor" href="https://thekaiway.com/2013/07/23/getting-start-with-sass/">#</a></h2>
<p>官方Reference</p>
<p>http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html</p>
<p>Sass缩进语法</p>
<p>http://sass-lang.com/docs/yardoc/file.INDENTED_SYNTAX.html</p>
<p>The Sass Way, 有分为不同阶段的文章</p>
<p>http://thesassway.com/</p>
<p>组织Sass代码的方式</p>
<p>http://thesassway.com/beginner/how-to-structure-a-sass-project</p>
<p>应用</p>
<p>得益于Sass强大的抽象能力和扩展力, 许多的框架基于它开发出来</p>
<p>Compass是一个基于Sass开发的CSS Framework, 集成了许多实用的Mixins</p>
<p>http://compass-style.org/</p>
<p>Twitter Bootstrap Sass, 使用Sass重写Bootstarp的项目</p>
<p>https://github.com/thomas-mcdonald/bootstrap-sass</p>
<p>方便处理Media Query的项目</p>
<p>https://github.com/paranoida/sass-mediaqueries</p>
<p>几个专门处理按钮样式的项目</p>
<ul>
<li>https://github.com/ubuwaits/css3-buttons</li>
<li>https://github.com/alexwolfe/Buttons</li>
</ul>
<p>还有许多Sass的项目, 这里再列出几个, 更多请自行上Github搜索</p>
<ul>
<li>https://github.com/csswizardry/inuit.css</li>
<li>https://github.com/GumbyFramework/Gumby</li>
</ul>
<p>竞争对手有Less.js和Stylus, 对比介绍</p>
<ul>
<li>https://gist.github.com/chriseppstein/674726</li>
<li>http://net.tutsplus.com/tutorials/html-css-techniques/sass-vs-less-vs-stylus-a-preprocessor-shootout/</li>
<li>http://coding.smashingmagazine.com/2011/09/09/an-introduction-to-less-and-comparison-to-sass/</li>
</ul>
Rails Paths
2013-07-12T00:00:00Z
https://thekaiway.com/2013/07/12/rails-paths/
<p>前面的章节提到Rails Engine实现了Rails中著名的<a href="http://en.wikipedia.org/wiki/Convention_over_configuration">Convention over Configuration</a>,其目的就在于统一有序地组织各种方面的代码。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分,<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部实现与设计的一本书,欢迎阅读</p>
</blockquote>
<p>而这个事情主要关心的就是加载路径,也就是让Rails能在对应的路径下找到相应的代码。Rails Engine对目录的配置代码主要如下:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># railties/lib/rails/engine/configuration.rb</span>
paths <span class="token operator">=</span> Rails<span class="token double-colon punctuation">::</span>Paths<span class="token double-colon punctuation">::</span><span class="token class-name">Root</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token variable">@root</span><span class="token punctuation">)</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"*"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/assets"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"*"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/controllers"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/helpers"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/models"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/mailers"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/views"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/controllers/concerns"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"app/models/concerns"</span></span><span class="token punctuation">,</span> <span class="token symbol">eager_load</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"lib"</span></span><span class="token punctuation">,</span> <span class="token symbol">load_path</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"lib/assets"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"*"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"lib/tasks"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"**/*.rake"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config/environments"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">Rails<span class="token punctuation">.</span>env</span><span class="token delimiter punctuation">}</span></span><span class="token string">.rb"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config/initializers"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"**/*.rb"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config/locales"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"*.{rb,yml}"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config/routes.rb"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"db"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"db/migrate"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"db/seeds.rb"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"vendor"</span></span><span class="token punctuation">,</span> <span class="token symbol">load_path</span><span class="token operator">:</span> <span class="token boolean">true</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"vendor/assets"</span></span><span class="token punctuation">,</span> <span class="token symbol">glob</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"*"</span></span>
paths</code></pre>
<p>Rails Application的<code>paths</code>是这样的:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># railties/lib/rails/application/configuration.rb</span>
<span class="token variable">@paths</span> <span class="token operator">||=</span> <span class="token keyword">begin</span>
paths <span class="token operator">=</span> <span class="token keyword">super</span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config/database"</span></span><span class="token punctuation">,</span> <span class="token symbol">with</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"config/database.yml"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"config/environment"</span></span><span class="token punctuation">,</span> <span class="token symbol">with</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"config/environment.rb"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"lib/templates"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"log"</span></span><span class="token punctuation">,</span> <span class="token symbol">with</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">"log/</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">Rails<span class="token punctuation">.</span>env</span><span class="token delimiter punctuation">}</span></span><span class="token string">.log"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"public"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"public/javascripts"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"public/stylesheets"</span></span>
paths<span class="token punctuation">.</span>add <span class="token string-literal"><span class="token string">"tmp"</span></span>
paths
<span class="token keyword">end</span></code></pre>
<h2 id="" tabindex="-1">根目录 <a class="header-anchor" href="https://thekaiway.com/2013/07/12/rails-paths/">#</a></h2>
<p>目录结构配置是在Rails Engine定义的,这里最终得到的paths是每个Engine的根目录,而<code>Rails.root</code>是来自最顶层的Rails Application的根目录。这里Rails对根目录的判断,在Engine和Application的不一样,Application是通过检查存在<code>config.ru</code>文件的目录,而Engine只是查找存在<code>lib</code>目录的目录。</p>
<h2 id="-1" tabindex="-1">路径集合 <a class="header-anchor" href="https://thekaiway.com/2013/07/12/rails-paths/">#</a></h2>
<p>在上面的配置代码里的<code>paths.add</code>会做两件事情,一是将传进来的字符串定义为<strong>一组路径</strong>,二是将对应的字符串作为这组路径的默认目录。这个Paths里的每一项。比如,在配置完成之后<code>paths["app/models"]</code>可以将这组路径里的所有目录都取回来。</p>
<p>也就是说每一组路径都是一个集合,而有些特殊的路径里只有一个文件,比如<code>paths["config/database"]</code>。在Rails内部在查找对应目录或文件的时候,都是通过这个<code>paths</code>去查找,而不是硬编码相对目录位置。</p>
<p>另外可以看到<code>paths.add</code>方法除了目录之外,还会接受一些选项</p>
<ul>
<li>eager_load: 是否使用预加载</li>
<li>glob: 目录内的文件查找通配符</li>
<li>with: 指定为唯一的文件</li>
<li>load_path: 作为<code>require</code>或<code>load</code>时候可以查找到的路径</li>
</ul>
<p>在了解完目录与加载的事实之后,你会知道Rails其实并不能控制你把Model放到<code>app/controllers</code>或其他地方下,它处理的只是把某些目录设置为查找代码的加载路径。</p>
Code loading of Rails
2013-07-04T00:00:00Z
https://thekaiway.com/2013/07/04/code-loading-of-rails/
<p>Ruby on Rails中实现了一套复杂的代码加载机制,比如怎样自动加载对应的模型,在开发模式如何重新加载整个项目的代码,以及开发模式下的代码预加载。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分,<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部的实现与设计的一本书,欢迎阅读</p>
</blockquote>
<h2 id="activesupport-dependencies" tabindex="-1">ActiveSupport::Dependencies <a class="header-anchor" href="https://thekaiway.com/2013/07/04/code-loading-of-rails/">#</a></h2>
<p>本篇中讲到的Ruby on Rails的代码加载机制大部分实现代码都在<a href="https://github.com/rails/rails/blob/4-0-stable/activesupport/lib/active_support/dependencies.rb"><code>ActiveSupport::Dependencies</code></a>这个类中,这其中的实现逻辑算是比较复杂,我不想在这里贴满代码,在本篇中只是讲到实现机制以及对应的方法,请读者自行去看对应的代码。</p>
<p><code>ActiveSupport::Dependencies</code>这个类所在的文件被require时,就会自动进行初始化,以下是这个文件的最后一行代码。</p>
<p>ActiveSupport::Dependencies.hook!</p>
<p>我们可以先看看这个对应的<code>hook!</code>方法</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># ActiveSupport::Dependencies</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">hook</span></span><span class="token operator">!</span>
<span class="token builtin">Object</span><span class="token punctuation">.</span>class_eval <span class="token punctuation">{</span> <span class="token keyword">include</span> Loadable <span class="token punctuation">}</span>
<span class="token builtin">Module</span><span class="token punctuation">.</span>class_eval <span class="token punctuation">{</span> <span class="token keyword">include</span> ModuleConstMissing <span class="token punctuation">}</span>
<span class="token builtin">Exception</span><span class="token punctuation">.</span>class_eval <span class="token punctuation">{</span> <span class="token keyword">include</span> Blamable <span class="token punctuation">}</span>
<span class="token keyword">end</span></code></pre>
<p>它就是将所需的各种Meta Programming挂到Object和Module下,以下就会一步步讲到对应的秘密。</p>
<h2 id="" tabindex="-1">自动加载 <a class="header-anchor" href="https://thekaiway.com/2013/07/04/code-loading-of-rails/">#</a></h2>
<p>Ruby on Rails开发者一般不需关心这样一个问题,从来没有手动加载某个模型类或者控制器类,但为什么这个类可以直接使用呢?其中的秘密就是Rails使用了Ruby的其中一个Meta Programming功能,<code>const_missing</code>。所有的类和模组在Ruby里都是常量,当Ruby解析器在遇到没有见过的常量时,就会去调用对应上下文的<code>const_missing</code>方法。开启<code>const_missing</code>的地方就在前面看到加载到Module里的<code>ModuleConstMissing</code>模组中。</p>
<p>当Rails项目代码里遇到一个从来没有加载过的类或模组时,会调用
<code>Dependencies.load_missing_constant</code>方法去尝试利用之前章节提到的文件结构惯例加载对应代码。这个<code>load_missing_constant</code>的基本思路是,调用<code>Dependencies.search_for_file</code>方法去找到对应的文件,找到后通过<code>Dependencies.require_or_load</code>去加载。这过程其中需要将已经加载的所有内容都记录下来,以便对这个加载状态进行管理。</p>
<h2 id="-1" tabindex="-1">开发模式的代码重新加载 <a class="header-anchor" href="https://thekaiway.com/2013/07/04/code-loading-of-rails/">#</a></h2>
<p>Rails的一个著名的功能就是在开发时,当你修改了某个文件后,Rails会帮你自动去重新加载对应的代码。</p>
<p>ActiveSupport里实现了一个名为FileUpdateChecker的类,可以监视文件变化,当文件被更
改的时候调用相应的逻辑。Rails通过这种方式去监视所有标记为<code>autoload</code>的目录下的文件,
当下一次请求过来时,在文件被修改的条件下会自动去进行重新加载。</p>
<p>而重新加载的机制,同样是利用Ruby语言的Meta Programming,通过<a href="http://www.ruby-doc.org/core-2.0/Module.html#method-i-remove_const">remove_const</a>去
把已经加载的类和模组都从内存中清空,这就让加载状态又回到了原点。虽然这个实现的思路很简单,但是由于Ruby里对于命名空间的处理是以嵌套的形式存在的,故需要循环遍历所有已加载的类和模组,并对其下的类和模组做深度遍历,最后将它们通通都清理。</p>
<p>这部分对应的代码入口如下</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># ActiveSupport::Dependencies</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">clear</span></span>
log_call
loaded<span class="token punctuation">.</span>clear
remove_unloadable_constants<span class="token operator">!</span>
<span class="token keyword">end</span></code></pre>
<p>另外,由于不是所有的代码都是通过这种自动加载的方法,有些是开发者通过<code>require</code>和<code>load</code>手动显式加载的。因此必须替换系统的<code>require</code>和<code>load</code>,记录下哪些代码已经被加载进来,并以前面提到的相同的方法加载代码。这个就是最开始提到的<code>Dependenciese.hook!</code>里include到Object类中的<code>Loadable</code>模组做的事情。</p>
<h2 id="-2" tabindex="-1">生产模式的代码预加载 <a class="header-anchor" href="https://thekaiway.com/2013/07/04/code-loading-of-rails/">#</a></h2>
<p>Rails在生产模式下,为了提高运行时的速度,去掉在处理请求时加载对应代码的延迟,所以会在
启动后把所有的业务代码都预先加载进来。它是通过如下的initializer来实现的,这个功能
通过<code>eager_load</code>的选项来控制。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># Rails::Application::Finisher</span>
initializer <span class="token symbol">:eager_load!</span> <span class="token keyword">do</span>
<span class="token keyword">if</span> config<span class="token punctuation">.</span>eager_load
ActiveSupport<span class="token punctuation">.</span>run_load_hooks<span class="token punctuation">(</span><span class="token symbol">:before_eager_load</span><span class="token punctuation">,</span> <span class="token keyword">self</span><span class="token punctuation">)</span>
config<span class="token punctuation">.</span>eager_load_namespaces<span class="token punctuation">.</span><span class="token keyword">each</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token symbol">:eager_load!</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>如果需要某个环境启用预加载的话,可以对应环境将这个<code>eager_load</code>选项打开。</p>
<h2 id="-3" tabindex="-1">加载日志 <a class="header-anchor" href="https://thekaiway.com/2013/07/04/code-loading-of-rails/">#</a></h2>
<p>ActiveSupport::Dependencies内部的所有操作都是可以输出到日志,但默认情况Rails关闭了这部分日志,希望读者在读完这部分内容后去打开日志选项,去实际看看在你的项目中代码是怎样被加载的。打开的方法也很简单,在你的<code>config/application.rb</code>的Application类定义里加上这几行代码:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">config<span class="token punctuation">.</span>after_initialize <span class="token keyword">do</span>
ActiveSupport<span class="token double-colon punctuation">::</span>Dependencies<span class="token punctuation">.</span>logger <span class="token operator">=</span> Rails<span class="token punctuation">.</span>logger
ActiveSupport<span class="token double-colon punctuation">::</span>Dependencies<span class="token punctuation">.</span>log_activity <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token keyword">end</span></code></pre>
<p>如果你在项目里引用了一些Rails Engine,由于前面章节所提到的Rails Engine与Rails Application的关系,Engine的MVC组件的加载也是通过同种方式进行,因此也能看到相应的Rails Engine里的日志。</p>
你可能不知道的ActiveRecord Migration小技巧
2013-06-19T00:00:00Z
https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/
<p>ActiveRecord的Migration是ActiveRecord用来维护RDBMS Schema的工具,
使开发者的机器和服务器上的Schema保持同步。其原理在于将每次对数据库的改动都保存为一个脚本,
并以改动内容以及时间戳命名防止重复。</p>
<p>以下我分享一些关于Migration的小技巧。</p>
<h2 id="say-say-with-time" tabindex="-1">say/say_with_time <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>我们有时会在Migration里执行数据的改动或更新,而此时最好能在输出里打印一些对应的信息,或者记录下对应的代码的执行时间。</p>
<p><a href="http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-i-say">say</a>和
<a href="http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-i-say_with_time">say_with_time</a>就是为了上述需求而诞生的。对比使用<code>puts</code>之类的方法的优点是,这类输出会带有缩进或对应的与
Migration各种API更一致的输出。</p>
<p>下次需要在Migration里输出点什么的话,请用<code>say</code>以及<code>say_with_time</code>吧。</p>
<h2 id="references-belongs-to" tabindex="-1">references/belongs_to <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>很多时候我们会创建互相关联的表,这就需要在表里加入一些引用到其它表的外键字段,这时我们一般会以添加一个
integer类型的字段,并赋以对应的名字(一般为对应模型的单数形式再加上<code>_id</code>)。ActiveRecord提供了<a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-references">references</a>API帮助我们更快捷地处理这种情况。</p>
<p>这里列出文档中的一个非常好的例子,这个例子非常明显地体现了使用这个API的好处。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">create_table <span class="token symbol">:taggings</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>references <span class="token symbol">:tag</span><span class="token punctuation">,</span> <span class="token symbol">index</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">name</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'index_taggings_on_tag_id'</span></span> <span class="token punctuation">}</span>
t<span class="token punctuation">.</span>references <span class="token symbol">:tagger</span><span class="token punctuation">,</span> <span class="token symbol">polymorphic</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token symbol">index</span><span class="token operator">:</span> <span class="token boolean">true</span>
t<span class="token punctuation">.</span>references <span class="token symbol">:taggable</span><span class="token punctuation">,</span> <span class="token symbol">polymorphic</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token symbol">default</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'Photo'</span></span> <span class="token punctuation">}</span>
<span class="token keyword">end</span></code></pre>
<p>以上的代码等价于下面较长的代码:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">create_table <span class="token symbol">:taggings</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>integer <span class="token symbol">:tag_id</span><span class="token punctuation">,</span> <span class="token symbol">:tagger_id</span><span class="token punctuation">,</span> <span class="token symbol">:taggable_id</span>
t<span class="token punctuation">.</span>string <span class="token symbol">:tagger_type</span>
t<span class="token punctuation">.</span>string <span class="token symbol">:taggable_type</span><span class="token punctuation">,</span> <span class="token symbol">default</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'Photo'</span></span>
<span class="token keyword">end</span>
add_index <span class="token symbol">:taggings</span><span class="token punctuation">,</span> <span class="token symbol">:tag_id</span><span class="token punctuation">,</span> <span class="token symbol">name</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'index_taggings_on_tag_id'</span></span>
add_index <span class="token symbol">:taggings</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token symbol">:tagger_id</span><span class="token punctuation">,</span> <span class="token symbol">:tagger_type</span><span class="token punctuation">]</span></code></pre>
<p>此外,<code>references</code>这个API也被alias为更容易记住的<code>belongs_to</code>。</p>
<h2 id="change-table" tabindex="-1">change_table <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>在Migration里提供了Schema操作的API都操作了两种形式,比如<code>add_column</code>和<code>column</code>。在<code>create_table</code>里
可以使用如<code>column</code>比较简短形式的API,这与Form Helper在Form Buildler里可以使用不带<code>_tag</code>后缀的API一致。</p>
<p>当我们需要去对同一个表做多次操作的时候,可以通过<code>change_table</code>来化简代码,在<code>change_table</code>的代码块中,
可以使用简短形式的API</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">change_table<span class="token punctuation">(</span><span class="token symbol">:suppliers</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>column <span class="token symbol">:name</span><span class="token punctuation">,</span> <span class="token symbol">:string</span><span class="token punctuation">,</span> <span class="token symbol">limit</span><span class="token operator">:</span> <span class="token number">60</span>
t<span class="token punctuation">.</span>remove <span class="token symbol">:company_id</span>
<span class="token keyword">end</span></code></pre>
<h2 id="create-join-table-drop-join-table" tabindex="-1">create_join_table/drop_join_table <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>当我们使用多对多(has_and_belongs_to_many)关联时需要创建关联表,而关联Schema很简单,只是
需要把关联的两张表的ID字段分别记录下来,而其中涉及了ActiveRecord的命名规范。这时使用
<a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-create_join_table">create_join_table</a>这个API就能很方便地帮我们去处理命名的事情,
只需要将对应两个表的表名作为参数传进去。</p>
<p>对应的也有一个<a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-drop_join_table">drop_join_table</a>API去帮我们删除这种关联表。</p>
<h2 id="change-column-default-change-column-null" tabindex="-1">change_column_default/change_column_null <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>业务总是在不断变化的,有时数据库里一些字段可能会由非空改为允许为空,修改默认值。当你把这些规则放到数据库时就
需要修改对应的字段和数据。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">change_column_null<span class="token punctuation">(</span><span class="token symbol">:users</span><span class="token punctuation">,</span> <span class="token symbol">:nickname</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span>
change_column_default<span class="token punctuation">(</span><span class="token symbol">:accounts</span><span class="token punctuation">,</span> <span class="token symbol">:authorized</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span></code></pre>
<p><code>change_column_default</code>会做两个事情,首先是把对应的字段填上指定的默认值,之后再修改Schema。</p>
<h2 id="reversible" tabindex="-1">reversible <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>我们知道Migration提供了Up/Down两个方向,相当于do和undo。随着<code>change</code>API的流行,很多时候我们不会去写
up和down两个方法,但有时就是需要写两个方向的代码。比如下面这个例子,在添加了first_name和last_name两个字段
后,在up这个方法上需要从full_name字段提取出first_name和last_name,而down的方法又需要合并出full_name的数据,这就是<a href="http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-i-reversible">reversible</a>的使用场景。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">SplitNameMigration</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Migration
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">change</span></span>
add_column <span class="token symbol">:users</span><span class="token punctuation">,</span> <span class="token symbol">:first_name</span><span class="token punctuation">,</span> <span class="token symbol">:string</span>
add_column <span class="token symbol">:users</span><span class="token punctuation">,</span> <span class="token symbol">:last_name</span><span class="token punctuation">,</span> <span class="token symbol">:string</span>
reversible <span class="token keyword">do</span> <span class="token operator">|</span>dir<span class="token operator">|</span>
User<span class="token punctuation">.</span>reset_column_information
User<span class="token punctuation">.</span>all<span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span>u<span class="token operator">|</span>
dir<span class="token punctuation">.</span>up <span class="token punctuation">{</span> u<span class="token punctuation">.</span>first_name<span class="token punctuation">,</span> u<span class="token punctuation">.</span>last_name <span class="token operator">=</span> u<span class="token punctuation">.</span>full_name<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">' '</span></span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
dir<span class="token punctuation">.</span>down <span class="token punctuation">{</span> u<span class="token punctuation">.</span>full_name <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">u<span class="token punctuation">.</span>first_name</span><span class="token delimiter punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">u<span class="token punctuation">.</span>last_name</span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span> <span class="token punctuation">}</span>
u<span class="token punctuation">.</span>save
<span class="token keyword">end</span>
<span class="token keyword">end</span>
revert <span class="token punctuation">{</span> add_column <span class="token symbol">:users</span><span class="token punctuation">,</span> <span class="token symbol">:full_name</span><span class="token punctuation">,</span> <span class="token symbol">:string</span> <span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<h2 id="revert" tabindex="-1">revert <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>某些时候写反向的逻辑会比正向的逻辑好写一点,比如有时我们会用<code>unless</code>而不是<code>if</code>。Migration里的
<a href="http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-i-revert">revert</a>方法就能提供这样的形式去编写数据库改动。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">FixTLMigration</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Migration
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">change</span></span>
revert <span class="token keyword">do</span>
create_table<span class="token punctuation">(</span><span class="token symbol">:horses</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>text <span class="token symbol">:content</span>
t<span class="token punctuation">.</span>datetime <span class="token symbol">:remind_at</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
create_table<span class="token punctuation">(</span><span class="token symbol">:apples</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>string <span class="token symbol">:variety</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>同时<code>revert</code>这个方法也支持传入一个Migration的名字,其作用是执行该Migration的down方法,当某个Migration已经同步上代码库后,希望撤销这个Migration时特别有用。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">require_relative <span class="token string-literal"><span class="token string">'2012121212_tenderlove_migration'</span></span>
<span class="token keyword">class</span> <span class="token class-name">FixupTLMigration</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Migration
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">change</span></span>
revert TenderloveMigration
create_table<span class="token punctuation">(</span><span class="token symbol">:apples</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>string <span class="token symbol">:variety</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<h2 id="at-the-end" tabindex="-1">At the end <a class="header-anchor" href="https://thekaiway.com/2013/06/19/activerecord-migration-tricks-and-tips/">#</a></h2>
<p>最后,提示一下,以上的API有些在Rails 3.x中没有加入,在Rails 4.0上以上的API可以找到。</p>
Rails Internal Hierarchy
2013-06-14T00:00:00Z
https://thekaiway.com/2013/06/14/rails-internal-hierarchy/
<p>Rails 内部有清晰的层级结构,以实现Rails应用程序和Rails插件的配置以及初始化。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分,<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部的实现与设计的一本书,欢迎阅读</p>
</blockquote>
<pre class="language-graphviz" tabindex="0"><code class="language-graphviz">digraph rails_hierarchy {
label = "Rails Hierarchy";
edge[dir=back, arrowtail=empty]
node [
style = box
style = filled
fontname = "Ubuntu Mono"
fontcolor = "#F2F2F2"
arrowhead = "empty"
];
railtie [label = "Railtie", fillcolor = "#8FBCDB"];
rengine [label = "Rails Engine", fillcolor = "#447294"];
rapp [label = "Rails Application", fillcolor = "#294052"];
railtie -> rengine;
rengine -> rapp;
}</code></pre>
<p>如上图所示,所有Rails Application继承自Rails Engine,而Rails Engine继承自Railtie,这套继承体系的实现全部都封装在railties这个Rubygem里。值得一提的是,Railtie和Rails Engine的子类都是<a href="http://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a>,Rails Application本身就是<a href="http://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a>,所以在一个程序里Rails Application只有一个实例。</p>
<h2 id="railtie" tabindex="-1">Railtie <a class="header-anchor" href="https://thekaiway.com/2013/06/14/rails-internal-hierarchy/">#</a></h2>
<p>我们先从Railtie说起,如果你翻查过一些Rails插件的源码,会发现它都继承了Railtie。Railtie位于层级里最低最底层的部分,它实现了配置和初始化这两大功能,其中的逻辑都组织在以下两个Modules中</p>
<ul>
<li>Initializable, 实现位于<code>rails/lib/rails/initializable</code></li>
<li>Configuration, 实现位于<code>rails/lib/rails/railtie/configuration</code></li>
</ul>
<p>Initializable 模块顾名思义就是负责初始化,常用的方法只有一个叫<code>initializer</code>的方法,它的方法签名如下,接受一个名字,一个可选的参数,一个代码块</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">initializer<span class="token punctuation">(</span>name<span class="token punctuation">,</span> opts <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token operator">&</span>blk<span class="token punctuation">)</span></code></pre>
<p>定义好的 Initializer 代码块会在Rails应用程序启动时执行,并且可以在参数里指定<code>before</code>或者<code>after</code>选项,
让其在某个已定义的Initializer执行之前或之后执行,这个功能是通过<a href="http://www.ruby-doc.org/stdlib-2.0/libdoc/tsort/rdoc/TSort.html">Ruby内置的TSort</a>实现的。以下是ActiveRecord设置Logger的Initializer</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">initializer <span class="token string-literal"><span class="token string">"active_record.logger"</span></span> <span class="token keyword">do</span>
ActiveSupport<span class="token punctuation">.</span>on_load<span class="token punctuation">(</span><span class="token symbol">:active_record</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>logger <span class="token operator">||=</span> <span class="token double-colon punctuation">::</span>Rails<span class="token punctuation">.</span>logger <span class="token punctuation">}</span>
<span class="token keyword">end</span></code></pre>
<p>Configuration 是实现常见的<code>config.xxx = yyy</code>这一常见写法的源头,它使用了Ruby的method_missing实现了配置参数的属性访问和设置,全部的配置都放在一个名为<code>@@options</code>的类变量里。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">method_missing</span></span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> <span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">&</span>blk<span class="token punctuation">)</span>
<span class="token keyword">if</span> name<span class="token punctuation">.</span>to_s <span class="token operator">=~</span> <span class="token regex-literal"><span class="token regex">/=$/</span></span>
<span class="token variable">@@options</span><span class="token punctuation">[</span>$`<span class="token punctuation">.</span>to_sym<span class="token punctuation">]</span> <span class="token operator">=</span> args<span class="token punctuation">.</span>first
<span class="token keyword">elsif</span> <span class="token variable">@@options</span><span class="token punctuation">.</span>key<span class="token operator">?</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span>
<span class="token variable">@@options</span><span class="token punctuation">[</span>name<span class="token punctuation">]</span>
<span class="token keyword">else</span>
<span class="token keyword">super</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>Action Mailer, Action Controller, Action View和Active Record 它们集成到Rails框架的都是通过Railtie实现。</p>
<p>如果你去看现实中的各种插件写的Railtie中,基本上就是调用initializer方法配置初始化逻辑和通过config变量在添加自身相关的各种配置选项。而且Rails也使用这两个模块去设置各种框架本身的初始化和参数配置。</p>
<p>Railtie除此之外,它还负责rake tasks和generator等部分与Rails应用程序的集成,暂不讲。</p>
<h2 id="rails-engine" tabindex="-1">Rails Engine <a class="header-anchor" href="https://thekaiway.com/2013/06/14/rails-internal-hierarchy/">#</a></h2>
<p>Rails Engine主要的设想就是把一些通用的Rails应用程序抽象出来并得到重用,也就是说每个Rails Engine几乎就是一个Rails应用程序,它拥有MVC结构,具有自己的路由,独立的Middleware Stack。社区里最广为人知的一个Rails Engine应该是<a href="https://github.com/plataformatec/devise">devise</a>。</p>
<p>Rails Engine中实现Rails广为人知的"<a href="http://en.wikipedia.org/wiki/Convention_over_configuration">Convention over Configuration</a>"特性,整套目录结构的加载就是在这里定义的。</p>
<p>Rails Engine是一个Rack Middleware,它实现了<code>call</code>方法,所以能Mount到其他Rails Engine或者Rails Application的路由上。</p>
<p>关于Rails的代码加载方式会在后续的章节详细讲解。</p>
<h2 id="rails-application" tabindex="-1">Rails Application <a class="header-anchor" href="https://thekaiway.com/2013/06/14/rails-internal-hierarchy/">#</a></h2>
<p>组织起Rails应用程序的启动流程,是Rails Application这个类最主要的事情。而Rails Application区别于Rails Engine在于需要管理很多外部的资源,比如以下的内容</p>
<ul>
<li><code>Rails.logger</code></li>
<li><code>Rails.cache</code></li>
<li>Session 的存储机制</li>
<li>维护完整Middleware Stack</li>
<li>代码重新加载</li>
<li>与Bundler的集成</li>
</ul>
<p>关于Rails的启动流程和Middleware Stack等话题会在后续的章节中展开并详细讲解。</p>
<p>本节暂时到此。</p>
Dependencies of Rails
2013-06-12T00:00:00Z
https://thekaiway.com/2013/06/12/dependencies-of-rails/
<p>我们平时安装Rails时,执行的是<code>gem install rails</code>,安装的Rubygem名称就叫rails,而这个Rubygem其实只是个没有代码的Meta Gem,它的作用就是定义rails依赖的组件。</p>
<blockquote>
<p>本文是<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>的一部分,<a href="https://thekaiway.com/inspect-rails">Inspect Rails</a>是由我正在编写的讲解Rails内部的实现与设计的一本书,欢迎阅读</p>
</blockquote>
<p>从 rails 的 gemspec 看到</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># rails.gemspec</span>
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'activesupport'</span></span><span class="token punctuation">,</span> version
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'actionpack'</span></span><span class="token punctuation">,</span> version
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'activerecord'</span></span><span class="token punctuation">,</span> version
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'actionmailer'</span></span><span class="token punctuation">,</span> version
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'railties'</span></span><span class="token punctuation">,</span> version
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'bundler'</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">'>= 1.3.0'</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">'< 2.0'</span></span>
s<span class="token punctuation">.</span>add_dependency <span class="token string-literal"><span class="token string">'sprockets-rails'</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">'~> 2.0.0.rc4'</span></span></code></pre>
<p>以上的依赖声明说明了Rails依赖于哪些组件,首先有几个active或action打头的Rubygem</p>
<ul>
<li>activesupport, 对Ruby语言的一些扩展,Rails的所有核心组件都是依赖于它</li>
<li>actionpack, 包含了处理Web请求逻辑,包含了MVC中的Controller和View</li>
<li>activerecord, 以Active Record模式为基础的ORM</li>
<li>actionmailer, 包含邮件发送和接收逻辑</li>
<li>railties, 把以上的组件组合起来</li>
<li><a href="https://github.com/rails/sprockets-rails">sprockets-rails</a>, <a href="https://github.com/sstephenson/sprockets">Sprockets</a>的Rails
集成代码,Sprockets为Rails带来了著名的Assets Pipeline,Rails 3.1引入</li>
<li><a href="https://github.com/carlhuda/bundler">bundler</a>, 管理依赖Rubygem的版本</li>
</ul>
<p>除了Bundler和sprockets-rails外的几个Act***框架都是放在
<a href="https://github.com/rails/rails">Rails的Repo</a>里,还有以下介绍的大部分***-rails
的Rails与其它库的集成都是放在<a href="https://github.com/rails">Rails的Github账号</a>下的,
如sprockets-rails。</p>
<p>当然,各个组件还引用了其它的依赖</p>
<ul>
<li><a href="https://github.com/jimweirich/builder">builder</a>, 创建XML数据的DSL</li>
<li><a href="https://github.com/rack/rack">rack</a>, Ruby的Web Server接口,我们知道Rails是
一个基于Rack的Web框架</li>
<li><a href="https://github.com/brynary/rack-test">rack-test</a>, rack的测试框架</li>
<li><a href="https://github.com/kwatch/erubis">erubis</a>, 最快的ERB渲染引擎</li>
<li><a href="https://github.com/rails/arel">arel</a>, 基于关系代数的SQL生成框架</li>
<li><a href="https://github.com/jimweirich/rake">rake</a>, 不解释</li>
<li><a href="https://github.com/wycats/thor">thor</a>, rake的替代品,在Rails中只用到了Thor的
文件操作功能去构建Generator</li>
</ul>
<p><a name="req-deps" href="https://thekaiway.com/2013/06/12/dependencies-of-rails/"></a></p>
<h2 id="" tabindex="-1">必要组件 <a class="header-anchor" href="https://thekaiway.com/2013/06/12/dependencies-of-rails/">#</a></h2>
<p>Rails在gemspec里声明是核心组件,但并非是必要的组件,比如Assets Pipeline,
ActiveRecord和ActionMailer不是一定需要包含在你的Rails Application里。</p>
<p>Rails 应用程序首先必须是个Rails Application,所以需要railites去维护整个程序的
加载和目录结构等。除此以外,Rails是个Web Framework,所以actionpack也是其必要的
组件之一。剩下的一个必要组件是,ActiveSupport,所有组件的必要依赖。</p>
<p><a name="opt-deps" href="https://thekaiway.com/2013/06/12/dependencies-of-rails/"></a></p>
<h2 id="-1" tabindex="-1">可选组件 <a class="header-anchor" href="https://thekaiway.com/2013/06/12/dependencies-of-rails/">#</a></h2>
<p>AcitveRecord,在Rails 3之后属于可替换的组件。由于在Actionpack里如Routing和Form
Helper严重依赖于ActiveRecord,所以Rails Core Team就抽象出了ActiveModel去解开
这个依赖,将Routing和Form Helper等需要调用到的部分,以Module的形式定义好接口,
只要包含或者实现了ActiveModel接口就能完美地与ActionPack协作。</p>
<p>ActionMailer,不是所有的Rails应用都有发邮件的需求,显然这不是必要的组件。</p>
<p>Sprockets,为Rails提供Assets Pipeline功能,但并不是所有人都喜欢它。在Rails应用
生成器里也提供了这个选项,去掉Assets Pipeline功能。</p>
<p>Test::Unit,Rails默认的测试框架,但由于Test::Unit是Ruby语言自带的,当开发者不想
直接使用它的时候,Rails只是关闭相关的代码生成器。另外,其他任何的测试框架都只是
Test::Unit的包装,添加了Syntax Sugar而已。</p>
Hello Octopress
2013-06-08T00:00:00Z
https://thekaiway.com/2013/06/08/hello-octopress/
<p>开始使用<a href="http://octopress.org/">Octopress</a> Blog Engine,加上<a href="https://github.com/lucaslew/whitespace">whitespace</a>
这个Theme。</p>
Got a HHKB Pro2
2012-08-01T00:00:00Z
https://thekaiway.com/2012/08/01/got-a-hhkb-pro2/
<p>本文写在<a href="http://en.wikipedia.org/wiki/Happy_Hacking_Keyboard">HHKB</a>入手3个月后,觉着要使用一段时间之后才能写出比较客观的感受。这个键盘是在<a href="http://weibo.com/2051369563/ye9QmzIwW">4月11日</a>老婆偷偷买了送给我的。</p>
<p>这是刚买来时的样子</p>
<p><img src="http://ww3.sinaimg.cn/bmiddle/7a456a5bjw1drw179o1apj.jpg" alt="My Keyboard"></p>
<h2 id="hhkb" tabindex="-1">HHKB <a class="header-anchor" href="https://thekaiway.com/2012/08/01/got-a-hhkb-pro2/">#</a></h2>
<p>HHKB是Happy Hacking Keyboard的缩写,PFU出品,HHKB系列只有三种型号,HHKB
Pro2 Type-S,HHKB Pro2和HHKB
Lite2。其中Type-S的价格要比普通的Pro2再贵个1K左右。</p>
<p>值得一提的是HHKB Pro2是静电电容键盘,而HHKB
Lite2是薄膜键盘,都并非是机械键盘。</p>
<h2 id="" tabindex="-1">外形 <a class="header-anchor" href="https://thekaiway.com/2012/08/01/got-a-hhkb-pro2/">#</a></h2>
<p>这个是个白色无刻的键盘,我理想中的HHKB只有白色无刻和黑色同刻这两款,就觉得这两款感觉上非常Cool,一让人看到就有种装X的感觉。在连接方面,键盘带了一根可以拆卸的USB连接线。</p>
<h2 id="-1" tabindex="-1">键位布局 <a class="header-anchor" href="https://thekaiway.com/2012/08/01/got-a-hhkb-pro2/">#</a></h2>
<p>由于HHKB的键位布局和一般的键盘有比较大的差异,再加上无刻,刚使用那段时间有点不适应。</p>
<p>一开始最不适应的有两个地方,数字键上的符号,和<code>~</code>移到键盘最右上角。<code>~</code>和<code>|</code>很快能习惯。以前一直是看着符号来按的,用了这个无刻的键盘经常会按错。但经过一段时间的使用,手指的肌肉已经记住了每个数字键和符号的位置。</p>
<p>而很早之前我就是把Cap lock设置为Control,HHKB上原生的这种Unix的键位设置对我来说更是如鱼得水。Control加上Esc的位置让我这个<a href="https://bitbucket.org/kaichen/vimrc">Vim的重度用户</a>用起来非常的舒服。</p>
<p>这么几个月使用下来,由于完全不用低头看键盘(无刻看了也没用),所以输入效率着实提高了不少。</p>
<h2 id="-2" tabindex="-1">手感 <a class="header-anchor" href="https://thekaiway.com/2012/08/01/got-a-hhkb-pro2/">#</a></h2>
<p>手感很软,估计相当于红轴的机械键盘(试过<a href="https://plus.google.com/100461408096595815099">同事</a>的红轴键盘),所以手指不需要怎么发力。另外是键程比较长,按键的回弹力量刚刚好,所以打字的段落感和节奏感非常好。长时间使用下来,对比以前的打字经验,在速度快的时候敲击的错误率降低。这里的错误是输入的键在一般键盘上顺序就串了,比如rails很容易输错为rials,但HHKB上没有这样的情况,这让我感觉很神奇,也许这就是这个键盘的价值所在吧。</p>
<h2 id="-3" tabindex="-1">其他 <a class="header-anchor" href="https://thekaiway.com/2012/08/01/got-a-hhkb-pro2/">#</a></h2>
<p>HHKB对Mac的支持很好,支持跳线设置为Mac模式,以支持几个常用的功能键,Volume Up/Down和Mute。</p>
<p>这两天还败了个HHKB的专用包,理由是每天都背着这个键盘上下班,有时听到键盘在包里撞来撞去的声音有些心寒。这个包没现货需要订货,估计下星期能到手。</p>
<p>推荐大家在有经济能力的情况下可以败一个,毕竟真的能提高一些效率。当然这也就是个普通的键盘,对你基本的能力并没有提升,提高的只是输入的体验,让你把精力都focus在hacking上,达到这个键盘所称的境界,Happy Hacking。再次感谢我老婆,我自己是狠不下心买的。</p>
<p>最后附上网上的一篇比较好的HHKB的评测</p>
<ul>
<li><a href="http://cyher.net/peripherals/the-art-of-hhkb-pro-2">Happy Hacking keyboard pro 2的艺术</a></li>
</ul>
Instance Property of CoffeeScript
2012-05-02T00:00:00Z
https://thekaiway.com/2012/05/02/instance-property-of-coffeescript/
<p>用Class语法定义的Instance Property是直接append到prototype上,当
你把一个property定义为某个对象(非立即值)时,那所有的
Instance都会指向同一个内存地址上。</p>
<pre class="language-coffeescript" tabindex="0"><code class="language-coffeescript"><span class="token keyword">class</span> <span class="token class-name">Foo</span>
<span class="token property">favSites</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Google"</span><span class="token punctuation">]</span></code></pre>
<p>会编译得到:</p>
<pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">var</span> Foo<span class="token punctuation">;</span>
Foo <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">function</span> <span class="token function">Foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token class-name">Foo</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>favSites <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Google"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> Foo<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>这里容易犯错的地方就是当有实例去修改上面提到的共享
内存地址的内容,这样就会得到一个奇怪的结果。</p>
<pre class="language-coffeescript" tabindex="0"><code class="language-coffeescript">foo1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span>
foo2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span>
foo1<span class="token punctuation">.</span>favSites<span class="token punctuation">.</span>push <span class="token string">"Github"</span>
alert foo2<span class="token punctuation">.</span>favSites <span class="token comment"># => ["Google", "Github"]</span></code></pre>
<p>当不想出现这种情况时最好避免直接把Instance Property定义在
Class Contructor的prototype上。</p>
<pre class="language-coffeescript" tabindex="0"><code class="language-coffeescript"><span class="token keyword">class</span> <span class="token class-name">Foo</span>
<span class="token property">constructor</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token class-member variable">@options</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span>
<span class="token class-member variable">@favSites</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Google"</span><span class="token punctuation">]</span>
foo1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span>
foo2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span>
foo1<span class="token punctuation">.</span>favSites<span class="token punctuation">.</span>push <span class="token string">"Github"</span>
alert foo2<span class="token punctuation">.</span>favSites <span class="token comment"># => ["Google"]</span></code></pre>
<p>在Backbonejs里也是这么处理的,比如在Model中,每个实例的所
有属性值(<code>attributes</code>):</p>
<pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">var</span> Model <span class="token operator">=</span> Backbone<span class="token punctuation">.</span><span class="token function-variable function">Model</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">attributes<span class="token punctuation">,</span> options</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">var</span> defaults<span class="token punctuation">;</span>
attributes <span class="token operator">||</span> <span class="token punctuation">(</span>attributes <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
#<span class="token operator">...</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>attributes <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>_escapedAttributes <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>cid <span class="token operator">=</span> _<span class="token punctuation">.</span><span class="token function">uniqueId</span><span class="token punctuation">(</span><span class="token string">'c'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>changed <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>_silent <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>_pending <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
#<span class="token operator">...</span>
<span class="token punctuation">}</span></code></pre>
Web Resources Help You Fight with Older IEs
2012-04-06T00:00:00Z
https://thekaiway.com/2012/04/06/web-resources-help-you-fight-with-older-IE/
<p>Recently I have to fight with older version IE, that's really a
nightmare.</p>
<p>And then search some resources to fight with it.</p>
<ul>
<li>http://www.sitepoint.com/10-fixes-for-ie6-problems/</li>
<li>http://stylisticweb.com/design-tutorials/15-ie6-bugs-and-simple-solutions</li>
<li>http://css-tricks.com/ie-css-bugs-thatll-get-you-every-time/</li>
<li>http://www.virtuosimedia.com/dev/css/ultimate-ie6-cheatsheet-how-to-fix-25-internet-explorer-6-bugs#understanding-hasLayout</li>
</ul>
carrierwave-upyun配置多个不同buckets
2012-04-02T00:00:00Z
https://thekaiway.com/2012/04/02/use-multi-buckets-in-carrierwave-upyun/
<h1 id="" tabindex="-1">背景 <a class="header-anchor" href="https://thekaiway.com/2012/04/02/use-multi-buckets-in-carrierwave-upyun/">#</a></h1>
<p><a href="https://github.com/jnicklas/carrierwave/">carrierwave</a>是RubyOnRails社区中比较
流行的文件上传插件,<a href="https://github.com/nowa/carrierwave-upyun">carrierwave-upyun</a>
是集成<a href="https://github.com/nowa/carrierwave-upyun">upyun</a>服务的插件。</p>
<h1 id="upyun-bucket" tabindex="-1">把不同类型的文件存放到upyun不同的bucket上 <a class="header-anchor" href="https://thekaiway.com/2012/04/02/use-multi-buckets-in-carrierwave-upyun/">#</a></h1>
<p>使用upyun时有个常见的需求就是把不同类型的图片分开放到不同的bucket当中,但在
carreirwave-upyun的文档当中并没有提到这点,只是给出了怎么配置全局参数的例子:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">CarrierWave<span class="token punctuation">.</span>configure <span class="token keyword">do</span> <span class="token operator">|</span>config<span class="token operator">|</span>
config<span class="token punctuation">.</span>storage <span class="token operator">=</span> <span class="token symbol">:upyun</span>
config<span class="token punctuation">.</span>upyun_username <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"xxxxxx"</span></span>
config<span class="token punctuation">.</span>upyun_password <span class="token operator">=</span> <span class="token string-literal"><span class="token string">'xxxxxx'</span></span>
config<span class="token punctuation">.</span>upyun_bucket <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"my_bucket"</span></span>
config<span class="token punctuation">.</span>upyun_bucket_domain <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"my_bucket.files.example.com"</span></span>
<span class="token keyword">end</span></code></pre>
<h1 id="-1" tabindex="-1">解决 <a class="header-anchor" href="https://thekaiway.com/2012/04/02/use-multi-buckets-in-carrierwave-upyun/">#</a></h1>
<p>而实际上,可以这么去做:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">CoverUploader</span> <span class="token operator"><</span> CarrierWave<span class="token double-colon punctuation">::</span>Uploader<span class="token double-colon punctuation">::</span>Base
storage <span class="token symbol">:upyun</span>
<span class="token keyword">self</span><span class="token punctuation">.</span>upyun_bucket <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"my-covers"</span></span>
<span class="token keyword">end</span>
<span class="token keyword">class</span> <span class="token class-name">AttachementUploader</span> <span class="token operator"><</span> CarrierWave<span class="token double-colon punctuation">::</span>Uploader<span class="token double-colon punctuation">::</span>Base
storage <span class="token symbol">:upyun</span>
<span class="token keyword">self</span><span class="token punctuation">.</span>upyun_bucket <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"my-attachements"</span></span>
<span class="token keyword">end</span></code></pre>
<p>这样简单地在Uploader里assign一下就可以解决问题。</p>
<h1 id="-2" tabindex="-1">为什么以上的解决方法行得通? <a class="header-anchor" href="https://thekaiway.com/2012/04/02/use-multi-buckets-in-carrierwave-upyun/">#</a></h1>
<p>Carreirwave的各种Configuration都是通过这里的<code>add_config</code>方法加入的。代码可以看以下的链接</p>
<p><a href="https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/uploader/configuration.rb#L75-L94">https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/uploader/configuration.rb#L75-L94</a></p>
<p><code>add_config</code>为每个Uploader实例添加了直接访问Class variable的方法,Uploader中
的各种Configuration 项(比如这里的<code>upyun_bucket</code>)都是存储在Uploader的Class
中。</p>
<p>而所有默认的Configuration项都是存储在<code>CarrierWave::Uploader::Base</code>,所以在我们
自定义的Uploader可以通过<code>add_config</code>为我们加入的<a href="https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/uploader/configuration.rb#L98-L92"><code>self.#{config_item}=</code></a>
去修改Configuration项。</p>
<p>也就是说Carrierwave一早就实现了这样的机制让不同的Uploader天生可以具有配置能力。</p>
Use Weekly to fetch tech updates
2012-03-11T00:00:00Z
https://thekaiway.com/2012/03/11/use-weekly-to-fetch-tech-updates/
<p>我现在获取技术更新的来源,从订阅各种RSS改为订阅各类Weekly Mail。</p>
<p>比起RSS,Weekly的优点不少:</p>
<ul>
<li>更少的噪音,订阅某个Blog或者News site的RSS往往附带很多噪音</li>
<li>更少的信息量,Weekly中已经是人工筛选过,更重要的信息</li>
<li>减少阅读时间,每周读几封Weekly比时不时就去看RSS花更少的时间</li>
<li>清理RSS,大大减少RSS的未阅读数量</li>
</ul>
<p>现在RSS里剩下的内容,只有名人和大牛的博客(如韩寒,云风等),或者没有相关Weekly的内容。</p>
<p>以下是我订阅的几个Weekly</p>
<ul>
<li>Ruby Weekly <a href="http://rubyweekly.com/">http://rubyweekly.com/</a></li>
<li>HTML5 Weekly <a href="http://html5weekly.com/">http://html5weekly.com/</a></li>
<li>JavaScript Weekly <a href="http://javascriptweekly.com/">http://javascriptweekly.com/</a></li>
<li>Coder Weekly <a href="http://coderweekly.com/">http://coderweekly.com/</a></li>
</ul>
<p>以上全部Weekly Mail都是免费的,<strong>值得一提的是前3份都是大牛 <a href="http://peterc.org/">Peter Cooper</a> 搞的</strong>。</p>
<p>另外我还订一个Hacker News的monthly,是将HN上热门的内容制作成精美的ebook,有PDF,iPad版的PDF,epub和mobi。订阅地址是,<a href="http://hackermonthly.com/">http://hackermonthly.com/</a>。这个是付费,一年$29。</p>
Play a HTTP toy server with EventMachine
2012-01-08T00:00:00Z
https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/
<p><img src="http://www.faconneurs.enligne-fr.com/__/logos_clients/event_machine.JPG" alt="EventMachine"></p>
<p><a href="https://github.com/eventmachine/eventmachine/wiki">EventMachine</a>是Ruby社区的<a href="http://en.wikipedia.org/wiki/Reactor_pattern">Reactor模式</a>的实现。</p>
<p>所谓<code>Reactor</code>模式,通过运行一个事件循环,将输入分发给对应的处理器,处理过程全权交给处理器,从而实现同时处理多个输入,是实现高并发的利器。几乎每个语言都有对应的实现,比如Pythong的<a href="http://twistedmatrix.com/trac/">Twisted</a>,最近很火的<a href="https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/nodejs.org/">Node.js</a>。</p>
<p>这次我们通过实现一个简单的HTTP File server来探索EventMachine。</p>
<p>通过Rubygems可以安装它:</p>
<pre class="language-shell" tabindex="0"><code class="language-shell">$ gem <span class="token function">install</span> eventmachine</code></pre>
<h3 id="beginning-sample" tabindex="-1">Beginning Sample <a class="header-anchor" href="https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/">#</a></h3>
<p>我们先从一个简单的例子入手,以下代码实现了这样的一个服务器,打印发过来数据,并返回<code>Yike</code>。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">require</span> <span class="token string-literal"><span class="token string">'eventmachine'</span></span>
<span class="token keyword">module</span> <span class="token class-name">TcpSample</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">receive_data</span></span><span class="token punctuation">(</span>data<span class="token punctuation">)</span>
puts <span class="token string-literal"><span class="token string">"[</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content"><span class="token builtin">Time</span><span class="token punctuation">.</span>now</span><span class="token delimiter punctuation">}</span></span><span class="token string">] receive </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">data</span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span>
send_data <span class="token string-literal"><span class="token string">"Yike\n"</span></span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token constant">EM</span><span class="token punctuation">.</span>run <span class="token keyword">do</span>
<span class="token string-literal"><span class="token string">%w{INT TERM}</span></span><span class="token punctuation">.</span><span class="token keyword">each</span><span class="token punctuation">{</span> <span class="token operator">|</span>s<span class="token operator">|</span> Signal<span class="token punctuation">.</span>trap s<span class="token punctuation">,</span> <span class="token operator">&</span>proc<span class="token punctuation">{</span><span class="token constant">EM</span><span class="token punctuation">.</span>stop_event_loop<span class="token punctuation">}</span> <span class="token punctuation">}</span>
port <span class="token operator">=</span> <span class="token constant">ENV</span><span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"PORT"</span></span><span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token number">8001</span>
host <span class="token operator">=</span> <span class="token constant">ENV</span><span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"HOST"</span></span><span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token string-literal"><span class="token string">"127.0.0.1"</span></span>
<span class="token constant">EM</span><span class="token punctuation">.</span>start_server host<span class="token punctuation">,</span> port<span class="token punctuation">,</span> TcpSample
puts <span class="token string-literal"><span class="token string">"Server start on </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">host</span><span class="token delimiter punctuation">}</span></span><span class="token string">:</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">port</span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span>
<span class="token keyword">end</span></code></pre>
<p>来解释几个EventMachine的API:</p>
<ul>
<li><a href="http://eventmachine.rubyforge.org/EventMachine.html#M000461">EM.run</a> 这个方法初始化并启动一个事件循环。</li>
<li><a href="http://eventmachine.rubyforge.org/EventMachine.html#M000469">EM.stop_event_loop</a> 这个方法顾名思义就是停止事件循环。在这段代码中我们注册了两个Signal,INT和TERM,用来在命令行用Ctrl-C停止程序。</li>
<li><a href="http://eventmachine.rubyforge.org/EventMachine.html#M000470">EM.start_server</a> 启动一个TCP服务器并监听传入参数的host和port,最后一个传入的参数是具体的行为逻辑实现,可以是Module或者是Class。</li>
</ul>
<p>代码中TcpSample module就是具体的Connection逻辑实现,只要实现几个由EventMachine Connection约定的方法,比如收发数据的<code>receive_data</code>和<code>send_data</code>。EventMachine会在运行过程事件被触发时回调Connection里的方法。具体关于EventMachine::Connection的文档请点击<a href="http://eventmachine.rubyforge.org/EventMachine/Connection.html">这里</a>。</p>
<p>我们可以用telnet来测试它:</p>
<pre class="language-shell" tabindex="0"><code class="language-shell">$ telnet <span class="token number">127.0</span>.0.1 <span class="token number">8001</span>
Trying <span class="token number">127.0</span>.0.1<span class="token punctuation">..</span>.
Connected to localhost.
Escape character is <span class="token string">'^]'</span><span class="token builtin class-name">.</span>
hey
Yike</code></pre>
<p>第一个简单的例子就这样演示完成了,继续下一步。</p>
<h3 id="toy-file-server" tabindex="-1">Toy File Server <a class="header-anchor" href="https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/">#</a></h3>
<p>接着步入正题,实现HTTP File Server。一句话来解释HTTP服务器做的事情,就是解析来自客户的Request,然后依照请求生成Response。这里的演示代码如题目所示,只是个Toy,按照请求返回静态文件。</p>
<p>首先需要接受并解析Request。EventMachine已经附带了<a href="http://eventmachine.rubyforge.org/EventMachine/Protocols.html">好几种Protocol的解析</a>,其中包括实现了HTTP的<a href="http://eventmachine.rubyforge.org/EventMachine/Protocols/HeaderAndContentProtocol.html">HeaderAndContentProtocol</a>。注意这里的各种Protocol实现都是继承第一个例子中讲到EventMachine::Connection,并为各自的协议包装了一个<code>receive_xxx</code>的回调方法,HeaderAndContentProtocol的回调方法名为<code>receive_request</code>。我们的HTTP Toy要做的就是继承<code>HeaderAndContentProtocol</code>,在<code>receive_request</code>方法中实现File Server的逻辑。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">HTTPToy</span> <span class="token operator"><</span> <span class="token constant">EM</span><span class="token double-colon punctuation">::</span><span class="token constant">P</span><span class="token double-colon punctuation">::</span>HeaderAndContentProtocol
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">receive_request</span></span> headers<span class="token punctuation">,</span> content
<span class="token comment">#TODO ...</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>先来完成HTTP Headers的解析:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token constant">REGEX</span> <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/\A(?<request_method>\w+) (?<full_path>\S+) HTTP\/(?<version>[\d.]+)\Z/</span></span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">parse_request</span></span><span class="token punctuation">(</span>data<span class="token punctuation">)</span>
<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">.</span>tap <span class="token keyword">do</span> <span class="token operator">|</span>req<span class="token operator">|</span>
matched <span class="token operator">=</span> <span class="token constant">REGEX</span><span class="token punctuation">.</span>match<span class="token punctuation">(</span>headers<span class="token punctuation">.</span>shift<span class="token punctuation">)</span>
req<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"request_method"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> matched<span class="token punctuation">[</span><span class="token symbol">:request_method</span><span class="token punctuation">]</span>
req<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"full_path"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> matched<span class="token punctuation">[</span><span class="token symbol">:full_path</span><span class="token punctuation">]</span>
req<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"http_version"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> matched<span class="token punctuation">[</span><span class="token symbol">:version</span><span class="token punctuation">]</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>接着实现Callback方法<code>receive_request</code>,主要的逻辑是查找文件和拼装Response并返回:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">receive_request</span></span> headers<span class="token punctuation">,</span> content
<span class="token variable">@request</span> <span class="token operator">=</span> parse_headers<span class="token punctuation">(</span>headers<span class="token punctuation">)</span>
filename <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"."</span></span> <span class="token operator">+</span> <span class="token variable">@request</span><span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"full_path"</span></span><span class="token punctuation">]</span>
<span class="token keyword">if</span> <span class="token variable">@request</span><span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"full_path"</span></span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string-literal"><span class="token string">"/"</span></span>
filename <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"./index.html"</span></span>
<span class="token keyword">end</span>
<span class="token keyword">if</span> <span class="token builtin">File</span><span class="token punctuation">.</span>exists<span class="token operator">?</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token builtin">File</span><span class="token punctuation">.</span>file<span class="token operator">?</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span>
serve_file filename
<span class="token keyword">else</span>
respond_not_found
<span class="token keyword">end</span>
<span class="token keyword">ensure</span>
close_connection_after_writing
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">serve_file</span></span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span>
extname <span class="token operator">=</span> <span class="token builtin">File</span><span class="token punctuation">.</span>extname<span class="token punctuation">(</span>filename<span class="token punctuation">)</span>
send_headers <span class="token string-literal"><span class="token string">"Content-Type"</span></span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token string-literal"><span class="token string">"html"</span></span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"text/html"</span></span><span class="token punctuation">,</span>
<span class="token string-literal"><span class="token string">"js"</span></span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"application/x-javascript"</span></span><span class="token punctuation">,</span>
<span class="token string-literal"><span class="token string">"css"</span></span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"text/css"</span></span>
<span class="token punctuation">}</span><span class="token punctuation">(</span>extname<span class="token punctuation">)</span>
send_data <span class="token builtin">File</span><span class="token punctuation">.</span>read<span class="token punctuation">(</span>filename<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">send_headers</span></span><span class="token punctuation">(</span>more <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
request<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"status"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> more<span class="token punctuation">.</span>delete<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">'status'</span></span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string-literal"><span class="token string">"200 OK"</span></span>
headers <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"HTTP/1.1 </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">request<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'status'</span></span><span class="token punctuation">]</span></span><span class="token delimiter punctuation">}</span></span><span class="token string">\r\n"</span></span>
more <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment"># the magic string "+8000" means my life is at HARD MODE</span>
<span class="token string-literal"><span class="token string">"Date"</span></span> <span class="token operator">=></span> <span class="token builtin">Time</span><span class="token punctuation">.</span>now<span class="token punctuation">.</span>strftime<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"%a, %d %b %Y %H:%m:%S +8000"</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string-literal"><span class="token string">"Server"</span></span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"my-http-toy"</span></span>
<span class="token punctuation">}</span><span class="token punctuation">.</span>merge<span class="token punctuation">(</span>more<span class="token punctuation">)</span>
<span class="token keyword">if</span> more<span class="token punctuation">.</span>any<span class="token operator">?</span>
more<span class="token punctuation">.</span><span class="token keyword">each</span><span class="token punctuation">{</span> <span class="token operator">|</span>k<span class="token punctuation">,</span> v<span class="token operator">|</span> headers <span class="token operator"><<</span> <span class="token string-literal"><span class="token string">"</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">k</span><span class="token delimiter punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">v</span><span class="token delimiter punctuation">}</span></span><span class="token string">\r\n"</span></span> <span class="token punctuation">}</span>
<span class="token keyword">end</span>
send_data headers <span class="token operator">+</span> <span class="token string-literal"><span class="token string">"\r\n"</span></span>
<span class="token keyword">end</span></code></pre>
<p>以上代码的<code>close_connection_after_writing</code>方法也是EventMachine的API之一,文档在<a href="http://eventmachine.rubyforge.org/EventMachine/Connection.html#M000286">这里</a>。这个方法会等待<code>send_data</code>的完成再把与客户端之间的连接关闭。上述大段代码的作用就是读文件并用<code>send_data</code>返回。</p>
<p>把它跑起来并通过浏览器可以测试一下它:</p>
<p><img src="http://dl.dropbox.com/u/1080383/screenshot-my-http-toy.png" alt="screenshot"></p>
<p>查看完整的实现代码请点击<a href="http://gist.github.com/1580890">这里</a>。</p>
<h3 id="benchmark" tabindex="-1">Benchmark <a class="header-anchor" href="https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/">#</a></h3>
<p>最后来对比异步IO和同步的IO效率相差有多大,和本文实现的简单http file server对比的是Rack。用Rack来做对比是因为它几乎是最小最快的HTTP File server实现。代码如下:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># file: config.ru</span>
run Rack<span class="token double-colon punctuation">::</span><span class="token class-name">File</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token builtin">Dir</span><span class="token punctuation">.</span>pwd<span class="token punctuation">)</span></code></pre>
<p>压力测试用的是<a href="http://www.hpl.hp.com/research/linux/httperf/">HTTPerf</a>,作为测试Fixture的是一个名为index.html的小文件(47B)。</p>
<p>Rack的File middleware在并发超过350的情况就歇菜了:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">$ rackup <span class="token punctuation">.</span><span class="token operator">/</span>config<span class="token punctuation">.</span>ru <span class="token operator">-</span>p <span class="token number">8002</span>
$ httperf <span class="token operator">-</span><span class="token operator">-</span>rate<span class="token operator">=</span><span class="token number">350</span> <span class="token operator">-</span><span class="token operator">-</span>timeout<span class="token operator">=</span><span class="token number">5</span> <span class="token operator">-</span><span class="token operator">-</span>num<span class="token operator">-</span>conns<span class="token operator">=</span><span class="token number">350</span> <span class="token operator">-</span><span class="token operator">-</span>port<span class="token operator">=</span><span class="token number">8002</span> <span class="token operator">-</span><span class="token operator">-</span>uri<span class="token operator">=</span><span class="token operator">/</span>index<span class="token punctuation">.</span>html
<span class="token symbol">Total</span><span class="token operator">:</span> connections <span class="token number">350</span> requests <span class="token number">350</span> replies <span class="token number">347</span> test<span class="token operator">-</span>duration <span class="token number">1.978</span> s
Connection rate<span class="token operator">:</span> <span class="token number">177.0</span> conn<span class="token operator">/</span>s <span class="token punctuation">(</span><span class="token number">5.7</span> ms<span class="token operator">/</span>conn<span class="token punctuation">,</span> <span class="token operator"><=</span><span class="token number">26</span> concurrent connections<span class="token punctuation">)</span>
Connection time <span class="token punctuation">[</span>ms<span class="token punctuation">]</span><span class="token operator">:</span> min <span class="token number">2.1</span> avg <span class="token number">68.5</span> max <span class="token number">1272.6</span> median <span class="token number">13.5</span> stddev <span class="token number">245.5</span>
Connection time <span class="token punctuation">[</span>ms<span class="token punctuation">]</span><span class="token operator">:</span> connect <span class="token number">63.6</span>
Connection length <span class="token punctuation">[</span>replies<span class="token operator">/</span>conn<span class="token punctuation">]</span><span class="token operator">:</span> <span class="token number">1.000</span>
Request rate<span class="token operator">:</span> <span class="token number">177.0</span> req<span class="token operator">/</span>s <span class="token punctuation">(</span><span class="token number">5.7</span> ms<span class="token operator">/</span>req<span class="token punctuation">)</span>
Request size <span class="token punctuation">[</span><span class="token constant">B</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token number">72.0</span></code></pre>
<p>350个并发的请求用了接近2秒的时间,速度是177个连接每秒。接着再来测试我们的HTTPToy:</p>
<pre class="language-shell" tabindex="0"><code class="language-shell">$ ruby ./httptoy.rb <span class="token number">8001</span>
$ httperf <span class="token parameter variable">--rate</span><span class="token operator">=</span><span class="token number">600</span> <span class="token parameter variable">--timeout</span><span class="token operator">=</span><span class="token number">5</span> --num-conns<span class="token operator">=</span><span class="token number">600</span> <span class="token parameter variable">--port</span><span class="token operator">=</span><span class="token number">8001</span> <span class="token parameter variable">--uri</span><span class="token operator">=</span>/index.html
Total: connections <span class="token number">600</span> requests <span class="token number">600</span> replies <span class="token number">600</span> test-duration <span class="token number">1.000</span> s
Connection rate: <span class="token number">600.1</span> conn/s <span class="token punctuation">(</span><span class="token number">1.7</span> ms/conn, <span class="token operator"><=</span><span class="token number">17</span> concurrent connections<span class="token punctuation">)</span>
Connection <span class="token function">time</span> <span class="token punctuation">[</span>ms<span class="token punctuation">]</span>: min <span class="token number">0.4</span> avg <span class="token number">2.8</span> max <span class="token number">25.9</span> median <span class="token number">0.5</span> stddev <span class="token number">3.9</span>
Connection <span class="token function">time</span> <span class="token punctuation">[</span>ms<span class="token punctuation">]</span>: connect <span class="token number">0.2</span>
Connection length <span class="token punctuation">[</span>replies/conn<span class="token punctuation">]</span>: <span class="token number">1.000</span>
Request rate: <span class="token number">600.1</span> req/s <span class="token punctuation">(</span><span class="token number">1.7</span> ms/req<span class="token punctuation">)</span>
Request size <span class="token punctuation">[</span>B<span class="token punctuation">]</span>: <span class="token number">72.0</span></code></pre>
<p><strong>完胜</strong>,和前面的差距不是一星半点,一秒内响应600个连接。如果继续提高并发数,到了700以上我们的HTTPToy也会出现不稳定的情况(崩溃或连接失败)。</p>
<h3 id="conclusion" tabindex="-1">Conclusion <a class="header-anchor" href="https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/">#</a></h3>
<p>EventMachine应用的场景和Node.js基本一样,IO密集的高并发场景,比如</p>
<ul>
<li>Web Socket服务端,https://github.com/igrigorik/em-websocket</li>
<li>并发的HTTP Client,https://github.com/igrigorik/em-http-request</li>
<li>Proxy,https://github.com/igrigorik/em-proxy</li>
</ul>
<p>在生产环境中大量使用EventMachine公司就是<a href="https://thekaiway.com/2012/01/08/play-a-http-toy-server-with-eventmachine/www.postrank.com/">PostRank</a>,这个公司基于EventMachine开发了大量的框架和库,有兴趣可以点击<a href="https://github.com/igrigorik">igrigorik</a>和<a href="https://github.com/postrank-labs">postrank-labs</a>的Github帐号。</p>
<p>最后谈下EventMachine缺点,和其它的Reactor模式实现一样,对付CPU密集的应用不行,而且使用的库全部都必须是异步,不然会把Main Event Loop阻塞(其后果是处理速度大大降低)。而像Node.js程序里出现了大量Callback的情况,在EventMachine上会好一点。</p>
<p>本文中的运行环境是Mac OSX Lion,Ruby 1.9.3-p0。</p>
如何阅读
2011-11-20T00:00:00Z
https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/
<h1 id="" tabindex="-1">阅读 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h1>
<p>结构化的知识大都是通过阅读书籍得到的,所以阅读是学习的重要途径之一。</p>
<h2 id="-1" tabindex="-1">阅读的结果 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<p>获得以下四个问题的答案:</p>
<ul>
<li><em>这本书讲了什么?</em></li>
<li><em>作者详细讲述了什么?怎么讲?</em></li>
<li><em>这本书讲得有道理吗?是全部都有道理还是只有部分?</em></li>
<li><em>为什么要读这本书?获得了什么信息,得到了什么启发?</em></li>
</ul>
<h2 id="-2" tabindex="-1">做笔记 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<ul>
<li><em>划线</em></li>
<li><em>作记号</em></li>
<li><em>记下当时的问题</em></li>
<li><em>记下感想</em></li>
</ul>
<h2 id="-3" tabindex="-1">理解的技巧 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<ul>
<li><em>通过书名对书本做分类</em></li>
<li><em>把握内容的结构</em></li>
<li><em>找到重点的字词句</em></li>
<li><em>判断作者的主旨</em></li>
<li><em>评判该书</em></li>
<li><em>赞同或反对作者</em></li>
</ul>
<h2 id="-4" tabindex="-1">快速阅读的技巧 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<p>过慢的阅读速度并不一定能提高理解能力,但一定会影响精神状态。</p>
<p>更少的定焦能提高阅读速度,比如阅读一行文字仅用定位三次,可以通过指示物加快定焦(包括换行)</p>
<p>通过高速阅读练习(限制的眼睛定焦次数和时间{3,2,1分钟内}),实现高速效应,提高平时正常阅读速度。</p>
<h2 id="-5" tabindex="-1">记忆 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<p>有时需要在阅读后记忆一些事情,这需要理解人是怎么遗忘信息的。快速的多遍阅读记忆效果比一遍阅读强很多。</p>
<p>通过在<a href="http://en.wikipedia.org/wiki/Forgetting_curve" title="Forgetting Curve">遗忘曲线</a>上的几个拐点复习来巩固记忆的效果,这几个拐点分别是5分钟,30分钟,12小时,1天,2天和5天。</p>
<h2 id="-6" tabindex="-1">后记 <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<p>本篇文章是我在读完文末列出的几本书之后写下的笔记。这几本是很有趣的书,让你在阅读中学习如何阅读的书。</p>
<h2 id="refs" tabindex="-1">Refs <a class="header-anchor" href="https://thekaiway.com/2011/11/20/%E5%A6%82%E4%BD%95%E9%98%85%E8%AF%BB/">#</a></h2>
<ul>
<li><a href="http://book.douban.com/subject/1013208/">如何阅读一本书</a></li>
<li><a href="http://book.douban.com/subject/6064502/">超级快速阅读</a></li>
<li><a href="http://book.douban.com/subject/1803504/">17天搞定GRE单词</a></li>
<li><a href="http://en.wikipedia.org/wiki/Forgetting_curve" title="Forgetting Curve">http://en.wikipedia.org/wiki/Forgetting_curve</a></li>
</ul>
新起点
2011-03-31T00:00:00Z
https://thekaiway.com/2011/03/31/new-start/
<p>这个月开始我从<a href="http://theplant.jp">ThePlant</a>离职,加入<a href="http://intridea.com">Intridea</a>。</p>
<p>ThePlant是我毕业后第一个公司。算上在毕业前半年的时间,我已经为<a href="http://theplant.jp">ThePlant</a>工作了两年。在这两年里我学到了很多,和同事们一起也很开心,学到很多宝贵的经验,在此感谢<a href="http://theplant.jp">ThePlant</a>里的所有人。</p>
<p>经过这两年工作,我有了更多的思考,思考自己能做什么,需要去做什么,所以便有了换工作的理由。就是去和更多不同的优秀的开发者一起工作,理解更多解决问题的思路,验证在之前得到想法和经验是否正确,参与更多不同类型项目的开发,获得更多经验。</p>
<p>理由很简单是吗?真的就这么简单,我想进步想有更多提升。</p>
<p>新的工作是Remote work的形式,远程的沟通协作和更多自我管理。</p>
<p>新的发型 <a href="http://instagr.am/p/Bxu71">http://instagr.am/p/Bxu71</a> 和新的起点 <a href="http://intridea.com/about/people/kai">http://intridea.com/about/people/kai</a>。</p>
Learning tinyrb 1
2009-04-29T00:00:00Z
https://thekaiway.com/2009/04/29/learning-tinyrb-1/
<p><a href="http://code.macournoyer.com/tinyrb/">Tinyrb</a>,一个Ruby的实现,
<a href="http://macournoyer.com">作者Marc-André Cournoyer</a>计划实现参照Lua和Potion
实现来做出一个轻量级快速的Ruby实现,<a href="http://on-ruby.blogspot.com/2009/02/tinyrb-interview.html">
这是一个对作者关于Tinyrb想法的采访</a>。目前还不能直接运行mspec,或者是我不知道怎么运行mspec。</p>
<p>其中用了一些外挂:</p>
<ul>
<li>GC, Boehm-Demers-Weiser conservative garbage collector</li>
<li>语法解析(Lexical Parse), peg/leg</li>
<li>命令行选项解析(Command-Line Option Parse), Free Getopt</li>
<li>正则表达式解析(Regular Parse), PCRE (Perl-compatible regular expression library)</li>
</ul>
<p>Tinyrb到目前为止是个非常清晰简洁的实现,整个实现,VM+Ruby Library就200K多一点。编译运行非常简单,在Github上clone下来后make就行了。跑下Fib测试:</p>
<p>~/ws/tinyrb > time ruby bench/bm_app_fib.rb
real 0m10.866s
user 0m9.393s
sys 0m1.170s
~/ws/tinyrb > time ruby-1.9 bench/bm_app_fib.rb
real 0m1.827s
user 0m1.423s
sys 0m0.013s
~/ws/tinyrb > time jruby bench/bm_app_fib.rb
real 0m2.718s
user 0m2.447s
sys 0m0.097s
~/ws/tinyrb > time tinyrb bench/bm_app_fib.rb
real 0m1.884s
user 0m1.680s
sys 0m0.007s</p>
<p>运行环境:Archlinux,kernal26-2.26.29,Core T8100,2G Mem。或许Tinyrb目前实现还不完全,启动速度比其它的Ruby实现就快了不少。</p>
<p>现在来剖析一下源码,我们从Tinyrb解析器启动入手,整个解析器的启动是在vm/tr.c文件中定义的:</p>
<p>/* file: vm/tr.c */
int main (int argc, char *argv[]) {
int opt;
TrVM <em>vm = TrVM_new();
while((opt = getopt(argc, argv, "e:vdh")) != -1) {
switch(opt) {
/</em> 参数解析 <em>/
...
}
}
/</em> 参数处理 */
if (argc > 0) {
TR_FAILSAFE(TrVM_load(vm, argv[argc-1]));
return 0;
}
TrVM_destroy(vm);
return usage();
}</p>
<p>可以看到整个VM的启动由TrVM_new()开始</p>
<p>/* file: vm/vm.c <em>/
/</em> GC初始化 <em>/
GC_INIT();
/</em> [A]VM分配空间并初始化 */
TrVM <em>vm = TR_ALLOC(TrVM);
vm->symbols = kh_init(str);
vm->globals = kh_init(OBJ);
vm->consts = kh_init(OBJ);
vm->debug = 0;
/</em> [B]初始化几个核心类Method,Symbol,Class,Object,Module <em>/
TrMethod_init(vm);
TrSymbol_init(vm);
TrModule_init(vm);
TrClass_init(vm);
TrObject_preinit(vm);
TrClass <em>symbolc = (TrClass</em>)TR_CORE_CLASS(Symbol);
TrClass <em>modulec = (TrClass</em>)TR_CORE_CLASS(Module);
TrClass <em>classc = (TrClass</em>)TR_CORE_CLASS(Class);
TrClass <em>methodc = (TrClass</em>)TR_CORE_CLASS(Method);
TrClass <em>objectc = (TrClass</em>)TR_CORE_CLASS(Object);
/</em> [C]设置核心类的继承体系 <em>/
symbolc->super = modulec->super = methodc->super = (OBJ)objectc;
classc->super = (OBJ)modulec;
/</em> [D]设置核心类的MetaClass <em>/
symbolc->class = TrMetaClass_new(vm, objectc->class);
modulec->class = TrMetaClass_new(vm, objectc->class);
classc->class = TrMetaClass_new(vm, objectc->class);
methodc->class = TrMetaClass_new(vm, objectc->class);
objectc->class = TrMetaClass_new(vm, objectc->class);
/</em> [E]确保所有在Object之前创建的的Symbol的类得到初始化 <em>/
TR_KH_EACH(vm->symbols, i, sym, {
TR_COBJECT(sym)->class = (OBJ)symbolc;
});
/</em> [F]装入各种核心类 <em>/
...
/</em> [G] <em>/
vm->self = TrObject_alloc(vm, 0);
vm->cf = -1;
/</em> [H]缓存几个常用的值 <em>/
vm->sADD = tr_intern("+");
vm->sSUB = tr_intern("-");
vm->sLT = tr_intern("<");
vm->sNEG = tr_intern("@-");
vm->sNOT = tr_intern("!");
/</em> 装载Ruby Library(在lib/目录下的文件) */
TR_FAILSAFE(TrVM_load(vm, "lib/boot.rb"));</p>
<p>在GC完成初始化之后,代码中[A]部分完成了维护整个Tinyrb运行环境的虚拟机对象的空间分配和初始化,VM是个宏:</p>
<p>#define VM TrVM *vm</p>
<p>TrVM这个VM的结构包括了什么:</p>
<p>/* file: vm/tr.h */
typedef struct TrVM {
khash_t(str) <em>symbols; /</em> 全局的符号表 */
khash_t(OBJ) <em>globals; /</em> 全局对象 */
khash_t(OBJ) <em>consts; /</em> 全局常量 <em>/
OBJ classes[TR_T_MAX]; /</em> 虚拟机维护的核心类 */
TrFrame <em>top_frame; /</em> 最顶层的栈幀(运行时栈的栈顶) */
TrFrame <em>frame; /</em> 当前的栈幀 <em>/
int cf; /</em> 栈幀的数量 count of frames <em>/
OBJ self; /</em> 根对象 <em>/
/</em> 调试和错误符号,还有一堆异常 <em>/
...
/</em> 几个缓存的对象 */
OBJ sADD;
OBJ sSUB;
OBJ sLT;
OBJ sNEG;
OBJ sNOT;
};</p>
<p>在前一块代码中设置的symbols,globals,consts就是保存运行时(Runtime)环境的各种信息,这几个变量都是Hash。接着下面的是Tinyrb的几个核心类列表,这是一个枚举值。然后是运行环境必不可少的栈帧,作用域调用就是靠这个维护的。栈幀由对象栈幀,栈顶,栈幀数这几个变量维护。还有一个虚拟机环境的根对象,这个对象就是在整个运行环境最外层作用域的对象,Ruby能做到不像Java那样写个什么都要包覆在一个对象中就是靠这个对象实现的,这个对象混入了Kernel模块,后面会看到每个栈帧(TrFrame)中都会有一个这样对象存在。最后是调试标记和异常信息,暂时略过。最后的几个常用的对象,可以看到在TrVM_new()中的[H]处进行初始化。</p>
<p>在VM的初始化中,中间的大段代码就是复杂完成Tinyrb的对象体系的构建,由Method开始初始化:</p>
<p>/* file:vm/class.c */
void TrMethod_init(VM) {
OBJ c = TR_INIT_CORE_CLASS(Method, Object);
tr_def(c, "name", TrMethod_name, 0);
tr_def(c, "arity", TrMethod_arity, 0);
tr_def(c, "dump", TrMethod_dump, 0);
}</p>
<p>TR_INIT_CORE_CLASS这个宏会引发一连串复杂的调用:</p>
<p>/* file:vm/tr.h */
#define TR_INIT_CORE_OBJECT(T) ({ <br>
Tr##T <em>o = TR_ALLOC(Tr##T); <br>
o->type = TR_T_##T; <br>
o->class = vm->classes[TR_T_##T]; <br>
o->ivars = kh_init(OBJ); <br>
o; <br>
})
#define TR_CORE_CLASS(T) vm->classes[TR_T_##T]
#define TR_INIT_CORE_CLASS(T,S) <br>
TR_CORE_CLASS(T) = TrObject_const_set(vm, vm->self, tr_intern(#T), <br>
TrClass_new(vm, tr_intern(#T), TR_CORE_CLASS(S)))
/</em> vm/class.c */
OBJ TrClass_new(VM, OBJ name, OBJ super) {
TrClass <em>c = TR_INIT_CORE_OBJECT(Class);
TR_INIT_MODULE(c);
/</em> if VM is booting, those might not be set */
if (super && TR_CCLASS(super)->class) c->class = TrMetaClass_new(vm, TR_CCLASS(super)->class);
c->super = super;
return (OBJ)c;
}
#define TR_INIT_MODULE(M) <br>
(M)->name = name; <br>
(M)->methods = kh_init(OBJ); <br>
(M)->meta = 0</p>
<p>TR_INIT_CORE_CLASS(Method, Object);展开如下</p>
<p>TrClass <em>obj = TR_ALLOC(TrClass);
obj->type = TR_T_Class;
obj->class = vm->classes[TR_T_Class];/</em> 实际上这句将其赋值为0,因为VM内部还没有创建Class类型 <em>/
obj->ivars = kh_init(OBJ);
obj->name = tr_intern(Method);
obj->methods = kh_init(OBJ);
obj->meta = 0;
obj->super = vm->classes[TR_T_Object]; /</em> 实际上这句将其赋值为0,因为VM内部还没有创建Object类型 */
OBJ const_class = TrObject_const_set(vm, vm->self, tr_intern(Method), (OBJ)obj);
vm->classes[TR_T_Method]=const_class;</p>
<p>这样很清楚看到Method这个类初始化的过程,首先分配一个类(TrClass)的内存空间,接着设置所有的属性,之后把这个Method类设置到VM的常量列表,最后将这个Method类的对象地址保存到虚拟机的类型列表(Classes List)。注意这里其实Method类的class和super都是为0的。</p>
<p>顺便提一下,Tinyrb内部的对象类型(Object type)就是这个枚举值。</p>
<p>/* file: vm/tr.h <em>/
typedef enum {
/</em> 0 <em>/ TR_T_Object, TR_T_Module, TR_T_Class, TR_T_Method, TR_T_Binding,
/</em> 5 <em>/ TR_T_Symbol, TR_T_String, TR_T_Fixnum, TR_T_Range, TR_T_Regexp,
/</em> 10 <em>/ TR_T_NilClass, TR_T_TrueClass, TR_T_FalseClass,
/</em> 12 <em>/ TR_T_Array, TR_T_Hash,
/</em> 14 <em>/ TR_T_Node,
TR_T_MAX /</em> keep last */
} TR_T;</p>
<p>当Method的属性设置完成之后接着就开始为其添加方法name(),arity(), dump()。添加方法是通过之前已经设置好的Method类,实例化三个Method Object,并把这三个对象添加到Method类的方法列表中,下面是tr_def(c, "name", TrMethod_name, 0) 的展开:</p>
<p>TrMethod <em>method = TR_INIT_CORE_OBJECT(Method); /</em> 初始化一个Method对象并返回 */
method->func = TrMethod_name;
method->data = TR_NIL;
method->arity = 0;
TrClass <em>m = (TrClass</em>)E(vm->classes[TR_T_Method])
TR_KH_SET(m->methods, name, method);
method->name = name;</p>
<p>接着其它几个类型也类似的过程进行初始化:Symbol,Module,Class和Object。</p>
<p>到此为止VM的Const List已经有了这几个类Method,Symbol,Module,Class,Object,并且它们的第一个实例也已经保存到Classes List中。接着这些类的继承体系和剩下的核心类怎样初始化呢?请听下回分解。</p>
runtime error words file not found of DataMapper
2009-01-30T00:00:00Z
https://thekaiway.com/2009/01/30/runtime-error-words-file-not-found/
<p>最近运行测试时在跑到用dm-sweatshop创建fixtures时总是报RuntimeError words file not found,而出错的行包含/\w+/.gen,然后就开始折腾…</p>
<p>最初看到/\w+/.gen这种语法时觉得十分的新奇,还以为是dm-sweatshop的创意,原来是dm-sweatshop依赖的randexp库的魔法,而这背后原来是从系统的words文件中随机挑选出一个单词来实现的。<a href="http://en.wikipedia.org/wiki/Words_(Unix)" target="_blank">words file in Wikipedia</a></p>
<p>如果系统中没有words文件那在使用刀sweatshop的/\w+/.gen时就一直会报RuntimeError words file not found。而就是这个没有明确说明的依赖,让我折腾了好多天,因为系统中不一定一开始就有这个words文件,像在我用的ArchLinux上是由 words包提供的(貌似Ubuntu中是wbritish包)。</p>
<p>一开始我以为是DM又抽风了,就删了dm,然后又把整个rubygems删了。然后开始用grep和find搜索各种源码,先是在dm-more中找不到,接着去找ParseTree(sweatshop依赖它来完成unique {/\w+/.gen})语法。最后读了一下sweatshop依赖中看到randexp,接着在randexp中搜索到了”words file not found”这个RuntimeError。</p>
<p>以上的做法比较S13,如果在使用sweatshop之前<a href="http://en.wikipedia.org/wiki/RTFM" target="_blank">RTFM</a>,就能看到它依赖于Randexp,然后如果再去看看源码,就不用折腾那么久XD</p>
<p>不过仔细读了一下sweatshop的源码,知道其中的猫腻,sweatshop为DataMapper::Model模块加入了几个方法:</p>
<p>fixtures:定义fixtures,和FactoryGirl的define差不多,可以用个Symbol指定名字,否则就为:defualt。方法缩写fix。
generate:使用之前fixtures方法中定义的属性值创建模型实例,调用的是模型的create方法。缩写gen。
generate!:同上,不过故名思义创建实例调用的方法是create!。缩写gen!。
generate_attributes:返回在fixtures方法中定义的属性Hash。类似FactoryGirl的attribute_for。
make:调用new创建实例,即不保存。
pick:返回一个由上述创建实例的方法创建的实例。一般用于关联,要将已创建的模型实例作为关联对象时使用。
sweatshop中维护了两个Hash,维护模型定义的model_map和维护实例(包括保存与未保存)的record_map。上述方法中 fixtures将模型定义加入model_map,gen/gen!/make方法则在创建实例后将其加入record_map中,pick方法就在 record_map中找出现存实例。</p>
<p>如果想要在FactoryGirl中使用/\w+/.gen就require ‘rendexp’这句就行了。</p>
<p>最后有个疑问,Rendexp这个包在win平台能用吗?</p>
Learning jQuery Notes
2009-01-14T00:00:00Z
https://thekaiway.com/2009/01/14/learning-jquery-notes/
<p>以下内容是Learning JQuery一书的前六章笔记摘要,基本的JQuery操作就在这里了。很多地方只是提一下,具体要用时还是去查<a href="http://docs.jquery.com">官方文档</a>。</p>
<h2 id="selecter" tabindex="-1">Selecter <a class="header-anchor" href="https://thekaiway.com/2009/01/14/learning-jquery-notes/">#</a></h2>
<p>基本通过工厂方法<code>$()</code>进行选择,可以选择标签名(Tag),ID(id),类(Class)。</p>
<p>通过子元素组合符(>)可以选择到后代元素</p>
<p>当需要通过属性进行选择时可以使用XPath语法:<code>tag[@attribute]</code>。更好的选择过滤符$=,^=,*=。</p>
<p>JQuery特有的自定义选择符,以冒号开头(:)</p>
<ul>
<li><code>:not</code> 反向选择</li>
<li><code>:eq(index)</code> 通过下标对数组进行选择</li>
<li><code>:odd</code> 索引为奇数的元素</li>
<li><code>:even</code> 索引为偶数的元素</li>
<li><code>:nth-child(index)</code> 通过索引访问子元素</li>
<li><code>:contains(string)</code> 包含指定字符串的元素</li>
</ul>
<p>选择到元素后可通过JQuery的特有遍历方式进行遍历</p>
<p><code>filter()</code> 可以调用自定义选择符进行过滤
<code>parent()</code> 取得父元素
<code>next()</code> 取得下一个同辈元素
<code>siblings()</code> 取得所有同辈元素
<code>find()</code> 通过标签加选择符进行过滤</p>
<p>通过<code>get(0)</code>或者<code>$(el)[0]</code>可以直接返回DOM元素</p>
<h2 id="event" tabindex="-1">Event <a class="header-anchor" href="https://thekaiway.com/2009/01/14/learning-jquery-notes/">#</a></h2>
<p><code>$(document).ready()</code>是注册事件处理器的入口,它会在HTML下载完毕并解析为DOM树时自动执行,并且是按顺序执行注册的处理器。</p>
<p>一般的形式:</p>
<p>$(document).ready(function(){
# codes....
});</p>
<p>事件绑定是使用bind()函数进行的,常见形式:</p>
<p><code>$(el).bind(event, function-define);</code></p>
<p>bind到元素上的事件不会覆盖,即可为元素同个事件定义多个事件处理器并让其按顺序执行。</p>
<p><code>bind()</code>的简写形式是直接调用与事件同名函数,如<code>click(functions-define)</code>。</p>
<p><code>toggle()</code>方法接受两个函数参数,在第一次单击时会调用第一个函数,第二次则调用第二个函数并循环这个过程。</p>
<p><code>toggleClass(classname)</code>方法会为元素切换指定的类。</p>
<p><code>hover()</code>方法与<code>toggle()</code>方法类似,接受两个函数的参数,分别在鼠标指针进入和离开该元素范围时执行。</p>
<p><code>unbind()</code>方法可以解除元素的事件处理器绑定,提供两个参数,一个事件名,和函数名。</p>
<p><code>one()</code>方法可以绑定一次性事件,即绑定完成后解除。</p>
<p><code>trigger()</code>方法可以模拟事件的发生。参数为事件名。其简写形式为不带任何参数的事件名同名函数。</p>
<h2 id="effect" tabindex="-1">Effect <a class="header-anchor" href="https://thekaiway.com/2009/01/14/learning-jquery-notes/">#</a></h2>
<p><code>css()</code>方法可以接受属性名和值来对元素的style进行设置。两种调用形式<code>css(property, value)</code>,<code>css(property1 : value1, property2 : value2...)</code>。</p>
<p><code>css()</code>方法也可以取得对应属性名的值。</p>
<p>BTW,<code>css()</code>方法类似reader和writer。</p>
<p><code>hide()</code>和<code>show()</code>方法可以对元素进行隐藏和显示。</p>
<p>所有的效果都可以加上速度参数:slow normal fast。</p>
<p><code>fadeIn()</code>,<code>fadeOut()</code>和<code>fadeTo()</code>修改元素的不透明度。</p>
<p><code>slideDown()</code>和<code>slideUp()</code>修改元素的高度。</p>
<p><code>animate()</code>方法可以同时修改元素的多个属性。</p>
<p>效果函数可以带上第二个参数作为回调函数,在效果完成后调用。</p>
<p>效果的顺序原则:</p>
<ol>
<li>一组元素上的效果</li>
</ol>
<ul>
<li>当在一个.antimate()方法中以多个属性的方式应用时,是同时发生的。</li>
<li>当以方法连缀的形式应用时,是按顺序发生的(排队效果)。</li>
</ul>
<ol start="2">
<li>多组元素上的效果</li>
</ol>
<ul>
<li>默认情况下是同时发生的。</li>
<li>当在事件处理程序的回调函数中应用时,是按顺序发生的(排队效果)。</li>
</ul>
<h2 id="dom" tabindex="-1">DOM <a class="header-anchor" href="https://thekaiway.com/2009/01/14/learning-jquery-notes/">#</a></h2>
<p><code>addClass()</code>和<code>removeClass()</code>,<code>togleClass()</code>可以对元素的类进行增删。</p>
<p><code>attr()</code>和<code>removeAttr()</code>可以对元素属性进行操作。</p>
<p><code>each(function(index){})</code>可以对元素组进行迭代。</p>
<p><code>clone()</code>方法可以进行深度复制</p>
<p><code>insertBefore()</code>,<code>before()</code>,<code>insertAfter()</code>,<code>after()</code>方法用于在相邻位置插入新元素。insertXxx和Xxx存在着被动与主动的关系。</p>
<p><code>append()</code>,<code>appendTo()</code>,<code>prepend()</code>,<code>prependTo()</code>用于元素中插入新元素。</p>
<p><code>wrap()</code>用于在元素外插入新元素,即用新元素包裹自身。</p>
<p><code>html()</code>,<code>text()</code>使用新元素或文本进行替换</p>
<p><code>empty()</code>移除元素</p>
<p><code>remove()</code>移除元素及其后代元素</p>
<h2 id="ajax" tabindex="-1">Ajax <a class="header-anchor" href="https://thekaiway.com/2009/01/14/learning-jquery-notes/">#</a></h2>
<p><code>load()</code>方法可以直接请求html片段并直接插入元素中。</p>
<p><code>$.getJSON()</code>方法可以请求json文档,第二个参数可以带上回调函数。</p>
<p><code>$.get()</code>和<code>$.post()</code>两个方法可以模拟GET和POST请求,并支持回调函数。第二个参数可以加上一个参数map。</p>
<p><code>$('#form').find('input').serialize()</code>可以序列化表单数据。</p>
<p><code>ajaxStart()</code>和<code>ajaxStop()</code>是两个Ajax请求的监听器,在请求开始时会调用注册在前一个函数的回调处理器,在请求结束时会调用后一个函数的回调处理器。</p>
国庆节两天假期
2008-10-04T00:00:00Z
https://thekaiway.com/2008/10/04/have-two-days-holiday/
<p>趁着国庆给自己放假两天。</p>
<p>那天踢了一下球之后那天晚上基本无法工作,太累,就简单地作一些翻译的文档的整理。第二天(亦就是昨天2号)也是很累,全身酸痛,从肩膀到腰到腿,好像才两个月没有踢球就这样了,看来身体越来越不行了。然后懵懵地就到了晚上,基本不知道自己在作什么,晚上有点精神,配置了一下实验室的服务器,把redmine从lighttpd迁移到apache2+passenger2的环境,并把svn库整理了一下。服务器上的所有软件包作更新,删除掉无用的包,再换个sever的内核(Ubuntu系统,原来用generic内核),还有更新rubygems。顺便把服务器的配置写份文档,并写了几个配置脚本,以后交给师弟来弄了。由于把服务器配置为a2+p2所以试了一下部署,结果发现capistrano一些问题,下篇post再谈。</p>
<p>今天早上起来又很困,结果就顺便把自己的电脑的环境清理一下。上个星期因为自己编译了gnome2.24结果把整个gnome搞坏了,所以最近一直用着KDEmod,其实很不爽。这次就趁火打劫地把gnome和kdemod整个完整删掉,换个轻量级的环境,一开始是openbox+rox+fbpanel+feh,配置挺烦一下,弄到最后换成openbox+rox+lxpanel。中间折腾了一下panel,又折腾了一下session,还要分配好rox和openbox的分工(有多种分工方法,最后选了rox负责桌面背景但不接管桌面,openbox接管桌面菜单)。因为有段时间看过openbox和rox的文档,所以用起来觉得还挺习惯,快捷键的操作很爽。下午再把firefox清理了一下,直接删了配置目录,自己再去下插件,网速有些问题,弄了一个钟。晚上学习如何查英文paper,大概摸出个门道。</p>
<p>在配置服务器的时候发现Ubuntu和其它发行版的目录结构差好多,比如apache的配置方法和在arch和贱兔的差好多,模块和站点的设置竟然是要在XX-available目录的文件link到XX-enable目录中,不是直接写个文件丢进去。本来还想配个git仓库的,后来觉得算了。在清理系统时,发现旧的rubygems竟然占了1G多,原来很多过时的gems还呆在里面。遂运行gem cleanup,过程很漫长,觉得那个依赖计算也是有问题,而且有内存泄漏问题,竟然有段时间把我的内存吃完了(2G),不知道是程序的问题还是GC的问题。</p>
<p>什么叫放假,不干活就是放假。</p>
open source camp guangzhou小记
2008-09-23T00:00:00Z
https://thekaiway.com/2008/09/23/open-source-camp-guangzhou-%E5%B0%8F%E8%AE%B0/
<p>第一次参加开源活动,感觉不错,不过topic都是科普性质,有些失望。会议上其实cpug的大妈带了一帮人马来会课,还霸占了M1会议室+<em>+,而大部分topic都讲得比较科普。我去听的三个topic是老外讲agile,香港同胞讲香港活动,最后听了Pyer洗脑。petty chen和Pyer的前两个topic错过了。其中自己和老外有一些问答,感觉自己的英语至少有人能听得懂了,咔咔咔。和香港同胞有些讨论,会后想mail'他,可是发去的mail给弹回来了,他的msn,gtalk等都没有连上-</em>-</p>
<p>感觉这种活动是结交朋友为主的。看到很多传说中的大牛(其实很多不认识),如cpug的大妈(真人比相片年轻),petty chen,俞黎敏等等。和<a href="http://yulimin.javaeye.com/">俞黎敏</a>打了下招呼(他在<a href="http://yulimin.javaeye.com/blog/245167">blog post</a>中提了下我^_^)。petty chen很有主持天赋。Pyer很拽。</p>
<p>PS:抽奖系统有点龊,不是随机序,是字母序循环,然后还不会自动去除已中奖的人,导致有人连拿两次奖。</p>
<p>会议相片在:
http://www.haokanbu.com/story/113420/
http://picasaweb.google.com/epaulin/OscampGuangzhou2008</p>
last year in hzu
2008-09-18T00:00:00Z
https://thekaiway.com/2008/09/18/last-year-in-hzu/
<p>这是在惠州学院的最后一年写下的。</p>
<p>一早起来就看到了pluskid的这篇<a href="http://pluskid.lifegoo.com/?p=455#comment-1608">post</a>,看完后很想把自己的想法写下来。</p>
<p>从上学期末开始,我就一直泡在实验室,一边开发一边学习,和同学们也疏远了。到了大学的末期,和身边的人可以用分道扬镳这个词来形容,有的已经为了生活在外打拼,有的为了考研苦读,有的还是一直蹲在宿舍玩着游戏看着电影。每个人怀着不同的想法,正走在各人选择不同的路上。记得那时刚来到的时候,大家三五成群,一般都一个宿舍全体一起去吃饭,一起出去玩,或者是一个班的同学组织去什么活动。现在比较多时间是一个人骑着单车在去实验室的路上,或是搭着女友去上课。</p>
<p>现在每天都很忙,我是那种遇到了自己想做的事情就会接下来做的那样,不会考虑太多自己的时间精力等问题,所以现在身上事情挺多,同时要忙着3,4项事情,还要自己学习看书。但一旦答应了别人就要帮别人做完做好事情,不过以后我回把一段时间内要做的事情控制在2件左右,以我的精力并没用能力作那么多事情。</p>
<p>对于未来,经过一段时间的思考。大概已经有了答案。就是找份payment不错的remote work,这样就有很多时间学习。出国的梦想已经破灭,但是我还是想能有很多时间学习,但又不想考研不想在国内读研(另一方面国内考研难度也不低),所以基于这些考虑,这个选择是不错的,还可以留在惠州这里陪着女友。</p>
<p>最后一年,在搬出去外面住之前可能大部分时间都会在实验室里渡过。到了上学期末,托和索老师做项目的福,得到了这个实验室的资源。好像比较好的学校的话,都会有这种资源提供给学生的吧,而且是让学生自己以社团的形式参与并开发出一些东西,比如pluskid所在的浙大。希望这个实验室能够给更多的学生,带来好处,让他们能够在这里开发,学习,交流,这对于来到这间学校的学生来说,是莫大的好处。可是这仅仅是我个人的天真想法,这学期本来想吸纳一些07级的学生进来,可是经过考察和了解后,发现他们还是差远了,差远不是说技术人品等,而是觉悟想法,他们现在还没有那种老老实实呆在实验室学习的觉悟,也没有太多关于以后的想法。竟然还有一个因为想参与进来但不想带电脑来又不想没时间,一直在和我和老师做些原则上的无谓争论。最后只加进来了两个06级的师弟,结果还不错^_^真心地祝福后来的同学能够好好利用这个资源,提高自身的价值。</p>
<p>现在每天都很累,咖啡喝了不少,睡眠不足。不过前两天病了一下,在床上躺了两天,觉得很无聊,头又痛又晕基本不能工作。所以要好好保重好身体,要革命先得有革命的本钱。</p>
<p>要学习的东西还很多,人生才刚刚开始没多久,路还很长。</p>
rails i18n feature
2008-09-11T00:00:00Z
https://thekaiway.com/2008/09/11/rails-i18n-feature/
<p>最近在其中一个项目中需要对Rails app进行i18n,时不时会查查Rails源码,昨日在insoshi的mailist中看到了这个link:</p>
<p><a href="http://rails-i18n.org/wiki">http://rails-i18n.org/wiki</a></p>
<p>建议对此有兴趣的人去看看,基本上把i18n都讲得比较清楚。其中还有个demo:</p>
<p><a href="http://github.com/clemens/i18n_demo_app/tree/master">http://github.com/clemens/i18n_demo_app/tree/master</a></p>
<p>我clone下来看看后顺手就也加上了zh_CN的locale configure,我先fork了一个分支,花了一个钟,完工pull,不知什么时候才会被merge。大家如果想要了解Rails22的i18n可以看看我汉化过的Demo,enjoy:</p>
<p><a href="http://github.com/maninred/i18n_demo_app">http://github.com/maninred/i18n_demo_app</a></p>
<p>Rails越来越多功能,但也越来越臃肿了。还可以看到Ruby也有很多i18n资源。</p>
check spelling on rails
2008-09-11T00:00:00Z
https://thekaiway.com/2008/09/11/check-spelling-on-rails/
<p>A few days ago I need to do spell checking for a rails project, but can't find a ready-to-use plugin, so I implemented one.</p>
<p>I use the<a href="http://aspell.net/"> Aspell </a>Lib to do the checking work, and ajax for editing.</p>
<p>First you need to install the Aspell and at lease one dictionary.</p>
<p>Mac:
<strong> sudo port install aspell aspell-dict-en</strong></p>
<p>Ubuntu:
<strong> sudo apt-get install aspell libaspell-dev aspell-en</strong></p>
<p>Arch:
<strong> sudo pacman -S aspell aspell-en </strong></p>
<p>And install the Aspell Ruby binding: <a href="http://github.com/fauna/raspell/tree/master">raspell</a></p>
<p><strong> sudo gem install raspell</strong></p>
<p>Add a controller:</p>
<pre><code> # app/controllers/spelling_controller.rb
class SpellingController << ApplicationController
def check
render :update do |page|
page.replace_html 'check-result', SpellingChecker.check_spell(params[:text])
end
end
end
</code></pre>
<p>Add a SpellingChecker Class to Lib</p>
<pre><code> # lib/spelling_checker.rb
class SpellingChecker
def self.check_spell text
speller = Aspell.new("en_US")
speller.suggestion_mode = Aspell::NORMAL
wrongs = []
text.gsub(/[\w\']+/) do |word|
wrongs << word unless speller.check(word)
end
wrongs.uniq.each do |wrong|
text.sub!(wrong, "<span class='wrong'>#{wrong}</span>")
end
return text
end
end
</code></pre>
<p>in the view:</p>
<pre><code> <label for="description">Description: </label>
<div id="check-desc" style="display:none;">
<%=link_to_function 'Resume editing', "ttoggleChecking()" %>
<br />
<div id="check-result">Checking...</div>
</div>
<div id="edit-desc">
<%=link_to_function 'Check spelling', "new Ajax.Updater({ success:toggleChecking()},
'/spelling/check',
{method:'get', parameters:{text:$F('ticket_description')}});" %>
<br />
<%= f.text_area :description, :cols => 84, :rows => 10 %>
</div>
<script type='text/javascript'>
function toggleChecking() {
$('edit-desc', 'check-desc').invoke('toggle');
}
</script>
</code></pre>
<p>This is what it looks like:</p>
<p><a href="http://www.chenk85.com/wp-content/uploads/2008/09/feedbackmine.png"><img src="http://www.chenk85.com/wp-content/uploads/2008/09/feedbackmine.png" alt="" title="check_spelling1" width="500" height="167" class="aligncenter size-full wp-image-128"></a></p>
<p><a href="http://www.chenk85.com/wp-content/uploads/2008/09/feedbackmine2.png"><img src="http://www.chenk85.com/wp-content/uploads/2008/09/feedbackmine2.png" alt="" title="checkspelling2" width="500" height="168" class="aligncenter size-full wp-image-129"></a></p>
<p><a href="http://www.chenk85.com/wp-content/uploads/2008/09/feedbackmine3.png"><img src="http://www.chenk85.com/wp-content/uploads/2008/09/feedbackmine3.png" alt="" title="checkspelling3" width="500" height="172" class="aligncenter size-full wp-image-130"></a></p>
<p>That is it, quite sample, isn't it? I would like to make a plugin for it, but no much time recently.</p>
<p>Thank yawl for pointing out my grammar misstake.</p>
a rspec cheetsheet
2008-08-22T00:00:00Z
https://thekaiway.com/2008/08/22/a-rspec-cheetsheet/
<p>from:<a href="http://coldfire.org.ua/blog/?p=26">http://coldfire.org.ua/blog/?p=26</a></p>
<p>a rspec cheetsheet, cool!</p>
<img src="http://coldfire.org.ua/blog/wp-content/uploads/2007/04/rspec_cheetsheet.thumbnail.png" alt="rspec cheetsheet thumbail">
<p>download: <a href="http://coldfire.org.ua/blog/wp-content/uploads/2007/04/rspec_cheetsheet.png">http://coldfire.org.ua/blog/wp-content/uploads/2007/04/rspec_cheetsheet.png</a></p>
Get Start Java Network App Dev
2008-08-06T00:00:00Z
https://thekaiway.com/2008/08/06/get-start-java-network-app-dev/
<p>如果你要开发一个Java的network app,有很多不错的opensource project帮助你开始开发。</p>
<p>比较基础和底层的话可以试试HttpClient,<a href="http://hc.apache.org">hc.apache.org</a>,按照它官方的教程,六步走。这个框架提供了Http访问的能力,加上Java的multithread能力,虽然效率不及noblocking io那么高但胜在文档资料多。这个一个初学者的好起点。</p>
<p>高级点的就是mina,<a href="http://mina.apache.org">mina.apache.org</a>,一个高性能高扩展能力的network app框架。基于Java的nio,并发能力得以保证,并在比较高层次进行封装。不过这个东西文档不多是弱点。值得一提的是Logo,so Cool。</p>
<p>当你的network app需要大量的数据处理时,使用hadoop是个不错解决方案,<a href="http://hadoop.apache.org">hadoop.apache.org/</a>。这个MapReduce实现,非常著名,不用我废话了:P Hbase是Hadoop的一个子项目,是Bigtable的实现。Hbase有Ruby的客户端,Hbase-ruby。</p>
<p>另hypertable也是一个值得注意的Bigtable实现,<a href="http://www.hypertable.org/">hypertable.org</a>。</p>
<p>现在的Network app常常需要有搜索功能,这时就需要Lunece,Solr,Nutch啦。Lunece也是不用介绍的。Solr很多人也熟悉,一个企业级搜索服务器,是Lunece的的扩展,提供了web管理界面等。Netch,一个通用型的Web搜索引擎,其实就一大Crawler,他的存储基于hadoop,原dadoop是其子项目。</p>
<p>以上的项目好像基本上都是Apache的Project,现在的Apache项目都有Wiki了,以前貌似没有,进步了。</p>
<p>这就是我最近开发Crawler遇到的几个Java Framework。</p>
a few interesting rails plugin
2008-08-06T00:00:00Z
https://thekaiway.com/2008/08/06/a-few-interesting-rails-plugin/
<p>最近开发中遇到一些有趣的rails pulgin,好像国内连介绍都没有,所以打算写写。</p>
<p><strong>Rails Widgets</strong></p>
<p><a href="http://www.seesaw.it/en/toolbox/widgets/">http://www.seesaw.it/en/toolbox/widgets/</a></p>
<p>一个DSLful的Page widgets plugin,让你简单地在页面上添加各种widgets,如tab,nav等,这些似乎是现在页面上不可或缺的页面元素。这个框架旨在减少手工维护这些widgets的代价,手工维护的话,so buggy!</p>
<p>具体看<a href="http://www.slideshare.net/paolo.dona/rails-widgets-by-paolo-dona-at-railstoitaly">其演示文档</a>可以看到它是干嘛的。</p>
<hr>
<p><strong>Bj</strong></p>
<p>http://codeforpeople.rubyforge.org/svn/bj</p>
<p>一个让Rails参与管理Server上后台进程(background job)的plugin。这样有什么好处呢?如果你前台Rails app是使用了后台的某些服务的,那这个插件就非常有用了。</p>
<p>不过这个plugin非常少资料,只能从它的<a href="http://codeforpeople.rubyforge.org/svn/bj/trunk/README">README</a>中获得比较多的信息。</p>
<p>......</p>
大三结束
2008-07-10T00:00:00Z
https://thekaiway.com/2008/07/10/_%E7%AC%AC6%E4%B8%AA%E5%AD%A6%E6%9C%9F%E7%BB%93%E6%9D%9F/
<p>今天考完试,大学生涯就剩下一年了,开始发牢骚。</p>
<p>每次考试完了之后总会觉得考试很假,都是学期初看完了书和相关的东西,比较有兴趣的东西才会去深入研究。但身边大多数人不是这样,一般都是到了最后一周左右时间才开始背书,一般这类同学都会得到比较高的分数。这与老师的出题方向也有关,出题基本都是作业题,就算什么也不会,考试前去背背作业就能拿很高分。记得中央台还报道过学校这里考试前打印店就非常热闹,其实最后一两天打印都是字特别小的“资料”。</p>
<p>中午发现了这个东西挺搞笑:<a href="http://www.cnbeta.com/articles/59918.htm">http://www.cnbeta.com/articles/59918.htm</a></p>
<p>原来做游戏的是比魔鬼还魔鬼,应该去枪毙。其实我一直就认为现在社会上很多奇诡的问题和国人的经济水平,文化水平不高有关。想想小时候家里的电器还不多,就电冰箱电视机,想买本十万个为什么还要和爸妈讲几天。然后忽然到了小学到了中学,再到大学,一时间生活完全变了,物质水平面目全非,精神水平也不敢恭维。这种变化速度国人无法适应。他们说游戏是毒品,他们就是为了赚钱让你玩更长时间,他们还在高楼大厦里,这个逻辑如果放到其它行业其实也是成立的。</p>
<p>还想到一个事情,如果中国不存在盗版软件或者盗版没那么猖獗,计算机会不会不那么流行。在国外来说,现在正常人使用的计算机,应该是软件贵过硬件。</p>
<p>目前国人的所谓家长群体,认为上网就是玩游戏,用电脑就是玩游戏。特别是目前大学生这个群体,很多人买计算机就是买了一部多功能游戏机,目的主要是娱乐。这是个性价比超低的买卖,花五千块去买部计算机打游戏看A片还不如花一千买部psp爽。但大部分人还是买了,用的是家长的血汗钱,就算是家庭经济水平不高的同学也为了面子也会购入计算机,理由是查资料。然后大学宿舍楼就会出现这样的喊声:“XX,建图阿”,“XX,我在X场X桌”,学生会为了娱乐(不单游戏,还有其他),逃课,通宵等等。大学生玩游戏时一般也会有比较多噪音出来,比如我常听见的“你会不会的阿?”,“你们这群傻13/垃圾”,“那么厉害不去打第一名”,“我踢,我踢,看你们还敢打我”......祖国未来的花朵就是这么成长的。</p>
<p>最近sourceforge还给封了。</p>
<p>这个世界太假拉。</p>
<p>发完牢骚,该干嘛干嘛去,END</p>
FxRuby Part4
2008-07-02T00:00:00Z
https://thekaiway.com/2008/07/02/fxruby%E5%88%9D%E4%BD%93%E9%AA%8Cpart4/
<p>之前的:FxRuby初体验<a href="http://redworld.blog.ubuntu.org.cn/2008/07/01/fxruby%e5%88%9d%e4%bd%93%e9%aa%8cpart1/">Part1</a>,<a href="http://redworld.blog.ubuntu.org.cn/2008/07/01/fxruby%e5%88%9d%e4%bd%93%e9%aa%8cpart2/">Part2</a>,<a href="http://redworld.blog.ubuntu.org.cn/2008/07/02/fxruby%e5%88%9d%e4%bd%93%e9%aa%8cpart3/">Part3</a>。</p>
<p>到了PictureBook的V0.4了。这个版本算是基本的功能都做足了,包括了加上了相册选择,相册持久化等等。</p>
<p>#album_list_view.rb
require 'fox16'</p>
<p>include Fox</p>
<p>class AlbumListView < FXList
attr_reader :album_list
def initialize(p, opts)
super(p, :opts => opts)
end
def switcher=(sw)
@switcher = sw
end</p>
<p>def add_album(album)
appendItem(album.title)
AlbumView.new(@switcher, album)
end</p>
<p>def album_list=(albums)
@album_list = albums
@album_list.each_album do |album|
add_album(album)
end
end
end</p>
<p>#picture_book.rb
$KCODE = "U"
require 'fox16'
require 'yaml'</p>
<p>include Fox</p>
<p>require 'album'
require 'album_list'
require 'photo'
require 'photo_view'
require 'album_view'
require 'album_list_view'</p>
<p>class PictureBook < FXMainWindow
def initialize(app)
super(app, "Picture Book" , :width => 600, :height => 400)
add_menu_bar
begin
@album_list = YAML.load_file("picturebook.yml" )
rescue
@album_list = AlbumList.new
@album_list.add_album(Album.new("My Photos" ))
end
splitter = FXSplitter.new(self,
:opts => SPLITTER_HORIZONTAL|LAYOUT_FILL)
@album_list_view = AlbumListView.new(splitter, LAYOUT_FILL)
@switcher = FXSwitcher.new(splitter, :opts => LAYOUT_FILL)
@switcher.connect(SEL_UPDATE) do
@switcher.current = @album_list_view.currentItem
end
@album_list_view.switcher = @switcher
@album_list_view.album_list = @album_list
end</p>
<p>def create
super
show(PLACEMENT_SCREEN)
end</p>
<p>def add_menu_bar
# 创建一个菜单栏的实例
menu_bar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
# 创建一个菜单栏项
file_menu = FXMenuPane.new(self)
FXMenuTitle.new(menu_bar, "文件" , :popupMenu => file_menu)
# 下面是一个创建菜单栏项和所关联动作的绑定
import_cmd = FXMenuCommand.new(file_menu, "导入..." )
import_cmd.connect(SEL_COMMAND) do
dialog = FXFileDialog.new(self, "导入图片" )
dialog.selectMode = SELECTFILE_MULTIPLE
dialog.patternList = ["JPEG Images (*.jpg, *.jpeg)" ]
if dialog.execute != 0
import_photos(dialog.filenames)
end
end
new_album_command = FXMenuCommand.new(file_menu, "New Album..." )
new_album_command.connect(SEL_COMMAND) do
album_title = FXInputDialog.getString("My Album" , self, "New Album" , "Name:" )
if album_title
album = Album.new(album_title)
@album_list.add_album(album)
@album_list_view.add_album(album)
AlbumView.new(@switcher, album)
end
end
exit_cmd = FXMenuCommand.new(file_menu, "退出" )# 一个简单的退出项
exit_cmd.connect(SEL_COMMAND) do
store_album_list
exit
end
end</p>
<p>def import_photos(filenames)
filenames.each do |filename|
photo = Photo.new(filename)
current_album.add_photo(photo)
current_album_view.add_photo(photo)
end
current_album_view.create
end</p>
<p>def current_album_view
@switcher.childAtIndex(@switcher.current)
end</p>
<p>def current_album
current_album_view.album
end</p>
<p>def store_album_list
File.open("picturebook.yml" , "w" ) do |io|
io.write(YAML.dump(@album_list))
end
end</p>
<p>end</p>
<p>if <strong>FILE</strong> == $0
FXApp.new do |app|
PictureBook.new(app)
app.create
app.run
end
end</p>
<p>剩下的晚上再写。。。</p>
FxRuby Part3
2008-07-02T00:00:00Z
https://thekaiway.com/2008/07/02/fxruby%E5%88%9D%E4%BD%93%E9%AA%8Cpart3/
<p>之前的:FxRuby初体验<a href="http://redworld.blog.ubuntu.org.cn/2008/07/01/fxruby%e5%88%9d%e4%bd%93%e9%aa%8cpart1/">Part1</a>,<a href="http://redworld.blog.ubuntu.org.cn/2008/07/01/fxruby%e5%88%9d%e4%bd%93%e9%aa%8cpart2/">Part2</a>。</p>
<p>这个V0.3的PictureBook添加了相册视图,也就是把相片都放到其中。看看下面的代码吧,我把每次都贴上完整的代码,方便大家复制粘帖:-)</p>
<p>#photo.rb
class Photo
attr_reader :path
def initialize(path)
@path = path
end
end</p>
<p>#album.rb
class Album
attr_reader :title
def initialize(title)
@title = title
@photos = []
end
def add_photo(photo)
@photos << photo
end
def each_photo
@photos.each { |photo| yield photo }
end
end</p>
<p>#album_list.rb
class AlbumList
def initialize
@albums = []
end</p>
<p>def add_album(album)
@albums << album
end
def remove_album(album)
@albums.delete(album)
end
def each_album
@albums.each { |album| yield album }
end
end</p>
<p>#photo_view.rb
class PhotoView < FXImageFrame
MAX_WIDTH = 200
MAX_HEIGHT = 200
def initialize(p, photo)
super(p, nil)
load_image(photo.path)
end
def load_image(path)
File.open(path, "rb" ) do |io|
self.image = FXJPGImage.new(app, io.read)
scale_to_thumbnail
end
end
def scaled_width(width)
[width, MAX_WIDTH].min
end
def scaled_height(height)
[height, MAX_HEIGHT].min
end
def scale_to_thumbnail
aspect_ratio = image.width.to_f/image.height
if image.width > image.height
image.scale(
scaled_width(image.width),
scaled_width(image.width)/aspect_ratio,
1
)
else
image.scale(
aspect_ratio*scaled_height(image.height),
scaled_height(image.height),
1
)
end
end
end</p>
<p>#album_view.rb
require 'photo_view'
class AlbumView < FXMatrix
attr_reader :album
def initialize(p, album)
super(p, :opts => LAYOUT_FILL|MATRIX_BY_COLUMNS)
@album = album
@album.each_photo { |photo| add_photo(photo) }
end
def add_photo(photo)
PhotoView.new(self, photo)
end
def layout
self.numColumns = [width/PhotoView::MAX_WIDTH, 1].max
super
end
end</p>
<p>#picture_book.rb
require 'fox16'
include Fox
require 'album'
require 'album_view'
require 'photo'
class PictureBook < FXMainWindow
def initialize(app)
super(app, "Picture Book" , :width => 600, :height => 400)
add_menu_bar
@album = Album.new("My Photos" )
@album_view = AlbumView.new(self, @album)
end
def add_menu_bar
menu_bar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
file_menu = FXMenuPane.new(self)
FXMenuTitle.new(menu_bar, "File" , :popupMenu => file_menu)
import_cmd = FXMenuCommand.new(file_menu, "Import..." )
import_cmd.connect(SEL_COMMAND) do
dialog = FXFileDialog.new(self, "Import Photos" )
dialog.selectMode = SELECTFILE_MULTIPLE
dialog.patternList = ["JPEG Images (*.jpg, *.jpeg)" ]
if dialog.execute != 0
import_photos(dialog.filenames)
end
end
exit_cmd = FXMenuCommand.new(file_menu, "Exit" )
exit_cmd.connect(SEL_COMMAND) do
exit
end
end
def import_photos(filenames)
filenames.each do |filename|
photo = Photo.new(filename)
@album.add_photo(photo)
@album_view.add_photo(photo)
end
@album_view.create
end
def create
super
show(PLACEMENT_SCREEN)
end
end</p>
<p>if <strong>FILE</strong> == $0
FXApp.new do |app|
PictureBook.new(app)
app.create
app.run
end
end</p>
<p>这里相对于上个版本的改动的地方有,PictureBook增加了菜单栏,增加了一个视图AlbumView,PhotoView实例的创建放在了AlbumView中,PhotoView中增加了图片的缩略图操作。</p>
<p>菜单栏的创建也听挺麻烦的,先创建<a href="http://www.fxruby.org/doc/api/classes/Fox/FXMenuBar.html">FXMenuBar的</a>实例,然后是创建<a href="http://www.fxruby.org/doc/api/classes/Fox/FXMenuTitle.html">FXMenuTitle</a>和<a href="http://www.fxruby.org/doc/api/classes/Fox/FXMenuPane.html">FXMenuPane</a>的实例,FXMenuTitle是FXMenuBar的下级元素,就是菜单栏的每一段,FXMenuPane是FXMenuTitle的弹出菜单。然后要创建<a href="http://www.fxruby.org/doc/api/classes/Fox/FXCommand.html">FXMenuCommand</a>,它是FXMenuPane的下级元素,实例化后就要用connect方法给出的代码块将其绑定到一定的行为上。</p>
<p>File>Importd这个动作是打开一个文件对话框<a href="http://www.fxruby.org/doc/api/classes/Fox/FXDialogBox.html">FXFileDialog</a>让你选择文件,然后用filenames方法获得选择得到的文件的数组。退出的菜单项很简单,就是一个exit方法。</p>
<p>在PhotoView中的图片操作功能是从FXImageFrame继承得到的,这个能力和接口很像RMagic。 AlbumView 是<a href="http://www.fxruby.org/doc/api/classes/Fox/FXMatrix.html">FXMatrix</a>的实例,FXMatrix是一种布局管理器,它以行列的方式来对组件进行布局。好像布局管理器的基类就是<a href="http://www.fxruby.org/doc/api/classes/Fox/FXPacker.html">FXPacker </a>,可以通过重写它的Layout方法来改变布局流。像这里就改变了FXMatrix的行数。</p>
<p>接着书中作者又改了一下,因为FXMatrix不能够在图片太多的情况下全部显示,会产生因为图片过多导致产生很多行,然后让超出主窗体的图片行无法看见,所以作者又对AlbumView换了一个布局管理器。</p>
<p>#album_view.rb
require 'photo_view'
class AlbumView < FXScrollWindow
attr_reader :album
def initialize(p, album)
super(p, :opts => LAYOUT_FILL)
@album = album
FXMatrix.new(self, :opts => LAYOUT_FILL|MATRIX_BY_COLUMNS)
@album.each_photo { |photo| add_photo(photo) }
end
def layout
contentWindow.numColumns = [width/PhotoView::MAX_WIDTH, 1].max
super
end
def add_photo(photo)
PhotoView.new(contentWindow, photo)
end
end</p>
<p><a href="http://www.fxruby.org/doc/api/classes/Fox/FXScrollWindow.html">FXScrollWindow</a>是另一种布局管理器,它只是多了滚动条。其中还是包含了一个FXMatrix。</p>
FxRuby part2
2008-07-01T00:00:00Z
https://thekaiway.com/2008/07/01/fxruby%E5%88%9D%E4%BD%93%E9%AA%8Cpart2/
<p>接上一篇:<a href="http://redworld.blog.ubuntu.org.cn/2008/07/01/fxruby%e5%88%9d%e4%bd%93%e9%aa%8cpart1/">FxRuby初体验Part1</a></p>
<p>本书的入门例子是个相册管理的桌面应用,名曰Picture Book。这个例子书中用了5 Chapter(2-6)来讲,基本讲到了常用的Gui组件。</p>
<p>不废话现在来看看代码,下面是第一个版本的Picture Book App:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token comment">#photo.rb</span>
<span class="token keyword">class</span> <span class="token class-name">Photo</span>
attr_reader <span class="token symbol">:path</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span>path<span class="token punctuation">)</span>
<span class="token variable">@path</span> <span class="token operator">=</span> path
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token comment">#album.rb</span>
<span class="token keyword">class</span> <span class="token class-name">Album</span>
attr_reader <span class="token symbol">:title</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span>title<span class="token punctuation">)</span>
<span class="token variable">@title</span> <span class="token operator">=</span> title
<span class="token variable">@photos</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">add_photo</span></span><span class="token punctuation">(</span>photo<span class="token punctuation">)</span>
<span class="token variable">@photos</span> <span class="token operator">&</span>lt<span class="token punctuation">;</span><span class="token operator">&</span>lt<span class="token punctuation">;</span> photo
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">each_photo</span></span>
<span class="token variable">@photos</span><span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token punctuation">{</span> <span class="token operator">|</span>photo<span class="token operator">|</span> <span class="token keyword">yield</span> photo <span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token comment">#album_list.rb</span>
<span class="token keyword">class</span> <span class="token class-name">AlbumList</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span>
<span class="token variable">@albums</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">add_album</span></span><span class="token punctuation">(</span>album<span class="token punctuation">)</span>
<span class="token variable">@albums</span> <span class="token operator">&</span>lt<span class="token punctuation">;</span><span class="token operator">&</span>lt<span class="token punctuation">;</span> album
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">remove_album</span></span><span class="token punctuation">(</span>album<span class="token punctuation">)</span>
<span class="token variable">@albums</span><span class="token punctuation">.</span>delete<span class="token punctuation">(</span>album<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">each_album</span></span>
<span class="token variable">@albums</span><span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token punctuation">{</span> <span class="token operator">|</span>album<span class="token operator">|</span> <span class="token keyword">yield</span> album <span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token comment">#photo_view.rb</span>
<span class="token keyword">class</span> <span class="token class-name">PhotoView</span> <span class="token operator">&</span>lt<span class="token punctuation">;</span> FXImageFrame
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span>p<span class="token punctuation">,</span> photo<span class="token punctuation">)</span>
<span class="token keyword">super</span><span class="token punctuation">(</span>p<span class="token punctuation">,</span> <span class="token keyword">nil</span><span class="token punctuation">)</span>
load_image<span class="token punctuation">(</span>photo<span class="token punctuation">.</span>path<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">load_image</span></span><span class="token punctuation">(</span>path<span class="token punctuation">)</span>
<span class="token builtin">File</span><span class="token punctuation">.</span>open<span class="token punctuation">(</span>path<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"rb"</span></span> <span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>io<span class="token operator">|</span>
<span class="token keyword">self</span><span class="token punctuation">.</span>image <span class="token operator">=</span> <span class="token class-name">FXJPGImage</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span>app<span class="token punctuation">,</span> io<span class="token punctuation">.</span>read<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token comment">#picturebook.rb</span>
<span class="token keyword">require</span> <span class="token string-literal"><span class="token string">'fox16'</span></span>
<span class="token keyword">include</span> Fox
<span class="token keyword">require</span> <span class="token string-literal"><span class="token string">'photo'</span></span>
<span class="token keyword">require</span> <span class="token string-literal"><span class="token string">'photo_view'</span></span>
<span class="token keyword">class</span> <span class="token class-name">PictureBook</span> <span class="token operator">&</span>lt<span class="token punctuation">;</span> FXMainWindow
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span>app<span class="token punctuation">)</span>
<span class="token keyword">super</span><span class="token punctuation">(</span>app<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Picture Book"</span></span> <span class="token punctuation">,</span> <span class="token symbol">:width</span> <span class="token operator">=</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> <span class="token number">600</span><span class="token punctuation">,</span> <span class="token symbol">:height</span> <span class="token operator">=</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> <span class="token number">400</span><span class="token punctuation">)</span>
photo <span class="token operator">=</span> <span class="token class-name">Photo</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"shoe.jpg"</span></span> <span class="token punctuation">)</span>
photo_view <span class="token operator">=</span> <span class="token class-name">PhotoView</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">,</span> photo<span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">create</span></span>
<span class="token keyword">super</span>
show<span class="token punctuation">(</span><span class="token constant">PLACEMENT_SCREEN</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">if</span> __FILE__ <span class="token operator">==</span> $<span class="token number">0</span>
<span class="token class-name">FXApp</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token keyword">do</span> <span class="token operator">|</span>app<span class="token operator">|</span>
<span class="token class-name">PictureBook</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span>
app<span class="token punctuation">.</span>create
app<span class="token punctuation">.</span>run
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>这是个V0.1的版本。不过你从中看出这个应用写得很MVC。</p>
<p>Model部分就是Photo,Album,AlbumList三个,Controller部分(当然它还没有细化)就是PictureBook,View部分就是PhotoView。就是如此清晰的结构,我想如果是正在学着什么MFC的可怜人儿们看到了之后会把那本什么深入浅出MFC扔了吧。</p>
<p>对比那个HelloWorld,这堆代码除了多了几个普通的Ruby Class(扮演Model的那几个)外,就是多了一个继承自<a href="http://www.fxruby.org/doc/api/classes/Fox/FXImageFrame.html">FXImageFrame</a>的PhotoView,还有在FXMainWindow的构造方法中多了对PhotoView的实例化,实例化时将其自身挂钩到PictureBook(构造方法的第一个参数)。之后在App实例化后会实例化MainWindow,接着实例化MainWindow包含的组件,比如PhotoView这个FxImageFrame。PhotoView中对图片的包装是这样的,它先读入Photo的路径,然后用<a href="http://www.fxruby.org/doc/api/classes/Fox/FXImage.html">FxImage</a>组件对图片进行包装,然后保存为自己的一个实例变量image中。</p>
<p>PictureBook的第一个版本就是这样。</p>
<p>接着下一篇讲下个版本和下下篇讲下下个版本,然后就介绍完了。</p>
FxRuby part1
2008-07-01T00:00:00Z
https://thekaiway.com/2008/07/01/fxruby%E5%88%9D%E4%BD%93%E9%AA%8Cpart1/
<p>FxRuby,一个更新较为频繁的Ruby GUI 开发库。今年还出了本书,今晚刚好有空,吃了饭后,6:30开始到现在9:30,看完了它的入门例子。其实是因为今晚不用开工,而想起了一个想做很久的事情,就是开发一个编辑器,一个能够用Ruby作为配置和开发语言的编辑器,就像Emacs能用Lisp在其上开发一样,而且能支持Rails,Rspec,Rake语法和开发辅助(如MVC跳转)等等,这是个妄想:-)</p>
<p><a href="http://pragprog.com/titles/fxruby/fxruby"><img src="http://www.fxruby.org/images/fxruby-book.jpg" alt="FxRuby book"></a>
<a href="http://pragprog.com/titles/fxruby/fxruby">http://pragprog.com/titles/fxruby/fxruby
</a></p>
<p>先来看看它的HelloWorld的代码:
require 'fox16'
include Fox
class HelloWindow < FXMainWindow
def initialize(app)
super(app, "Hello, World!" , :width => 200, :height => 100)
end
def create
super
show(PLACEMENT_SCREEN)
end
end
if <strong>FILE</strong> == $0
FXApp.new do |app|
HelloWindow.new(app)
app.create
app.run
end
end</p>
<p>HW没什么太多好讲的,有些东西要提一提就Ok了。GUI应用,肯定有个明显的入口点,就是<a href="http://www.fxruby.org/doc/api/classes/Fox/FXApp.html">FxApp</a>的实例,因为是Desktop app,所以一般都有个主窗体,在FxRuby中是<a href="http://www.fxruby.org/doc/api/classes/Fox/FXMainWindow.html">FXMainWindow</a>的实例。FxRuby中,App实例创建之后还要调用create和run两个方法,主窗体的构造函数要做的事情是设置好主窗体的属性,App的create方法会调用会调用每个下级控件的create方法,在create中要加入的就是显示的方式(好像窗体才需要)等等。从上面的HW就可以看到这些。
<a href="http://www.fxruby.org/doc/ch03s05.html">
FxRuby官方手册</a>中还有另一个复杂一点点的HelloWorld。</p>
<p>入门例子是个相册管理的桌面应用,名曰Picture Book。这个例子书中用了5 Chapter(2-6)来讲,基本讲到了常用的Gui组件。</p>
<p>好像太长了,拆开放到另一个日志上吧。</p>
<p>参考Link:</p>
<p>FxRuby官站:<a href="http://www.fxruby.org/">http://www.fxruby.org/</a></p>
<p>FxRuby的Rdoc:<a href="http://www.fxruby.org/doc/api/">http://www.fxruby.org/doc/api/</a></p>
<p>FxRuby在线手册:<a href="http://www.fxruby.org/doc/book.html">http://www.fxruby.org/doc/book.html</a></p>
有眼不识Ruby on Rails
2008-06-27T00:00:00Z
https://thekaiway.com/2008/06/27/%E6%9C%89%E7%9C%BC%E4%B8%8D%E8%AF%86ror/
<p>前天,学校的网页设计大赛,展示并且评审。我用的是Ruby on Rails开发一个Web日程管理的应用。结果评委老师们一直认为RoR是一个网站,和他们讲了许久,他们还是认为我和另一个也是用RoR开发的同学是抄袭所谓开源项目的。并且他们对开源项目这种东西,觉得十分的小儿科,觉得很新奇,很Toyful。当我在展示应用中的很多有趣的Ajax效果之后台下同学们还有有些兴趣,但是我看到老师们全部是低下头不听我讲的。虽然最后我获得了一个所谓的最佳代码奖,但是我和同学都不爽,不爽的是自己的作品被人认为是抄袭。更令我失望的是,虽然很多同学看到演示觉得很好玩,但是只有一个学生向我要了联系方式,而且也没有联系我。</p>
<p>今晚看了最新一期的程序员,这一期的主题讲的是开源,很多地方都提到了一点,就是开源要从大学教育开始,让学生在学习的过程中去阅读这些开源的项目,并且去参与其中,甚至去做出贡献。我觉得这个离国内的现实还是好远,国内的大学绝大多数的学生和老师都对开源没什么认识,他们只认得Ms,认得Windows,他们认为VC就是C++,SQL就是SQL Server,认为电脑就是Windows,课件就叫PPT。</p>
<p>我自从发现了Linux下校园网的上网解决方案之后,就再也没有怎么用过windows,并一年前就把windows删除了。其实我也向很多人推广了Linux,但是很多人都是一两天新鲜过后就删除了或者根本没有用过。现在我怎么用IDE,怎么不爽,总觉得写完一个东西之后一定要按下Esc,:w。</p>
<p>在中国大陆的校园内接触开源软件接触的人还是太少。</p>
Skinny Spec的小技巧
2008-06-26T00:00:00Z
https://thekaiway.com/2008/06/26/%E4%BD%BF%E7%94%A8skinny-spec%E7%9A%84%E4%B8%80%E4%B8%AA%E5%B0%8F%E6%8A%80%E5%B7%A7/
<p>使用Skinny Spec对Controller进行测试时需要定义shared_request来进行请求,
但是这个方法的命名非常的令我费解,一般都是使用do_get,
make_request,do_request等等,那要使用Skinny_Spec对原来的Spec进行测试时比较挺麻烦。</p>
<p>因为我是个懒人,简单Hack一下就不用定义多个shared_request方法。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token comment">#spec/spec_helper.rb</span>
<span class="token keyword">module</span> <span class="token class-name">ControllerMacros</span>
<span class="token keyword">module</span> <span class="token class-name">ExampleMethods</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">shared_request</span></span>
verb <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token symbol">:get</span><span class="token punctuation">,</span> <span class="token symbol">:post</span><span class="token punctuation">,</span> <span class="token symbol">:put</span><span class="token punctuation">,</span> <span class="token symbol">:delete</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find<span class="token punctuation">{</span><span class="token operator">|</span>verb<span class="token operator">|</span> respond_to<span class="token operator">?</span> <span class="token symbol">:"do_#{verb}"</span><span class="token punctuation">}</span>
<span class="token keyword">raise</span> <span class="token string-literal"><span class="token string">"未定义do_get, do_post, do_put, 或do_delete方法 / No do_get, do_post_ do_put, or do_delete has been defined"</span></span> <span class="token keyword">unless</span> verb
send<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"do_</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">verb</span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
Spec<span class="token double-colon punctuation">::</span>Runner<span class="token punctuation">.</span>configure <span class="token keyword">do</span> <span class="token operator">|</span>config<span class="token operator">|</span>
<span class="token comment">#...</span>
config<span class="token punctuation">.</span><span class="token keyword">include</span><span class="token punctuation">(</span>ControllerMacros<span class="token punctuation">,</span> <span class="token symbol">:type</span> <span class="token operator">=</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> <span class="token symbol">:controllers</span><span class="token punctuation">)</span>
<span class="token keyword">end</span></code></pre>
重构Rspec测试代码
2008-06-23T00:00:00Z
https://thekaiway.com/2008/06/23/refactoring_rspec_code/
<p>消除Spec中的冗余,减少浪费。</p>
<p>看到ben的Blog写了一篇关于Rspec的测试宏的文章:
http://www.benmabey.com/2008/06/08/writing-macros-in-rspec/</p>
<p>其实很多人都是看到Tammer Saleh在MountainWest_Ruby_Conference2008上的Shoulda演示后,和我有一样的感想,就是如果如此DSL化,如此DRY的测试宏能用在Rspec上那就好了。那时我还把Shoulda的官方文档翻译了一下=_=,还有人和我讨论为什么要用Shoulda,还说他就是喜欢Rspec,其实我一次也没有用过Shoulda,但就是觉得这个DSL写的很好。不过Shoulda的缺点也很明显,AR的测试宏依赖于fixture,在业界不提倡使用fixture的情况下还有对测试数据的控制粒度的角度来说,这个做法是不受欢迎的。Rspec中提倡的是全部数据都是在测试时手动Mock出来,所以在1.1.4中才有了stub_mock!这个方法来减轻Mock对象的负担。</p>
<p>Rspec测试的粒度是比较细的,测试的覆盖面在Stroy框架出来之后也能和之前Rails提供的Unittest一样。但是Rspec的测试代码,看上去很冗余,一开始就有这种感觉,一直到看到了Shoulda才发现原来它冗余的地方是什么,这存在于很多地方,比如Controller中每次都是mock一个请求方法,然后就对各种会触发的行为和保存的状态进行断言。这些每次千篇一律的东西,我在想如果在每个context下定义一个请求方法(do_xxx之类的),然后在测试时会在测试方法内部的断言前或断言后自动调用它,这样就能减少很多冗余了。当然我在看完了Shoulda的文档后也尝试写出Rspec的测试宏,不过由于不知道怎么连接到Rspec中就放弃了,话说回来,Rspec中每个测试中上下文关系是很复杂的。</p>
<p>不说废话了,看看鬼佬们怎么减少Rspec中的冗余吧。</p>
<p>下面是一个常见的以Product为领域模型的Controller的测试,用Rspec-Rails插件生成的Scaffold的测试就类似下面这样:</p>
<p>describe ProductsController do
describe "handling GET /products" do</p>
<p>before(:each) do
@product = mock_model(Products)
Product.stub!(:find).and_return([@product])
end</p>
<p>def do_get
get :index
end</p>
<p>it "should be successful" do
do_get
response.should be_success
end</p>
<p>it "should render index template" do
do_get
response.should render_template('index')
end</p>
<p>it "should find all products" do
Product.should_receive(:find).with(:all).and_return([@product])
do_get
end</p>
<p>it "should assign the found products for the view" do
do_get
assigns[:products].should == [@product]
end
end
end</p>
<p>上面的代码一看过去就觉得很多重复吧。如果我想让它变成下面这些DSL化的测试代码要怎么办呢?</p>
<p>describe ProductsController do
describe "处理Get /products" do
before(:each) do
@products = mock_model(Product)
Products.stub!(:find).and_return([@product])
end
def do_get
get :index
end
it_should_response_success
it_should_render :template, "index"
it_should_receive Product, :find, :all, [@product]
it_should_assign :products, [@product]
end
end</p>
<p>那么首先为这些宏定义一个Module吧:</p>
<p>module ControllersMacro
module ExampleMethods
def do_action
verb = [:get, :post, :put, :delete].find{|verb| respond_to? :"do_#{verb}"}
raise "No do_get, do_post_ do_put, or do_delete has been defined!" unless verb
send("do_#{verb}")
end
end</p>
<p>module ExampleGroupMethods
def it_should_assign(variable_name, value=nil)
it "should assign #{variable_name} to the view" do
value ||= instance_variable_get("@#{variable_name}")
if value.kind_of?(String) && value.starts_with?("@")
value = instance_variable_get(value)
end
do_action
assigns[variable_name].should == value
end
end
def it_should_response_success
it "should be successful" do
do_action
response.should be_success
end
end
def it_should_receive model, action, with_value = anything, return_value = anything
it "should make #{model.to_s} #{action.to_s}" do
model.should_receive(action).with(with_value).and_return(return_value)
do_action
end
end
end</p>
<p>def self.included(receiver)
receiver.extend ExampleGroupMethods
receiver.send :include, ExampleMethods
end
end</p>
<p>这些就是Spec中的DSL,一般称为Rspec Macros,Rspec宏。那么那么把这些宏与Controller们挂钩呢?每次测试挂一次?当然不是,在ben那篇Blog的留言里,Rspec的核心开发成员David Chelimsky给出了答案,原来Rspec中本来就有接口开放了:</p>
<p>Spec::Runner.configure do |config|
#...
config.include(ControllerMacros, :type => :controllers)
end</p>
<p>这样就把测试宏挂到了Controller的Spec那里,那还等什么就直接在Spec用这些DSL来写出清爽的Spec吧,享受BDD。在这之后当然,我们不会止步于只在Controller中消除冗余,我立马就想到了Shoulda中几个Api,像下面那样,马上就能想到一些ActiveRecord的测试宏。</p>
<p>module ModelsMacro
module ExampleMethods
#......
end</p>
<p>module ExampleGroupMethods
def it_should_require_attributes(variable_name)
it "should require #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_require_unique_attributes variable_name
it "should require unique attributes #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_not_allow_values_for variable_name, not_allow = []
it "should require #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_allow_values_for variable_name, allow_values = []
it "should allow values for #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_protect_attributes variable_name
it "should protect attributes #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_have_one variable_name
it "should have one #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_have_many variable_name, option => {}
it "should have many #{variable_name}" do
#TODO
end
end</p>
<p>def it_should_belong_to variable_name
it "should belong to #{variable_name}" do
#TODO
end
end
end</p>
<p>def self.included(receiver)
receiver.extend ExampleGroupMethods
receiver.send :include, ExampleMethods
end
end</p>
<p>如果觉得自己写很麻烦,那就用别人做好的现成的东西吧:
一个现成的Rspec宏项目,Skinny Spec。它已经完成了Controller和AR的测试宏,页面的测试宏,还有一个生成器:
<a href="http://github.com/rsl/skinny_spec/tree">http://github.com/rsl/skinny_spec/tree</a>
使用很简单,只要用script/plugin安装就好了。不过这个还有些不足,比如还没有shoulda中那个should_be_restful这个最魔幻的方法,在控制器中做出请求动作的那个方法的定义是实现一个名为shared_request方法,在其中加入请求的方法和参数。</p>
<p>其实我对Rspec还有很多想法,比如更加DSL化的测试,对Mock测试数据时更加强大的控制,测试中描述信息的国际化,动态生成测试方法等等。</p>
<p>清爽的Rspec代码,相信每个人都想把它放进自己的项目中。。。</p>
收回来
2008-06-20T00:00:00Z
https://thekaiway.com/2008/06/20/%E6%94%B6%E5%9B%9E%E6%9D%A5/
<p>大学三年里好像不知不觉就学到了很多东西,感觉自己学的很泛很散。我这个人好奇心很强,所以东学一下,西学一下,很多东西都是学到了皮毛。</p>
<p>现在要把自己的知识面收回来,收到一个比较小的知识面上,然后好好把这部分知识学习得深入些。还有就是不要研究过多的过程的东西,比如敏捷,只要了解一下就好了。</p>
<p>编程语言:Ruby,Cpp,C,Erlang,就这四门就够了。整个计算机世界的底层就是C构建出来的,如果要再上一层就是Cpp,要面向对象要DSL就是继承Smalltalk之魂的Ruby,并发的FP语言Erlang很有前途的。D语言的话,以后有机会要用到再说,而Java要用的时候查查书就可以。说什么语言都是一样的人,我觉得是放屁。</p>
<p>开发技术也有几个方向:Web开发,编译器解析器等语言底层构建技术,自动化测试和TDD/BDD。应该就这些了,对于敏捷还是看看就算了,到了实际软件开发的问题开始的时候再考虑这些方法,其实敏捷的经典书籍我也基本看了。</p>
<p>多看看源码。其实现在我看源码的感觉是这样,大概看得懂,可是不懂得怎么改。唉呀呀,还需要修炼。</p>
<p>实践也还是很少。自己有时会写写一些算法的实现,有时会写些小demo。其实我的想法蛮多的,想作的东西也是很多的,但是并不是十分有动力去把这些想法做出来。这次有机会参与到老师的科研项目就要努力去完成它,还有要下定决心完成一个编译器也是一个不错的开始。上次比赛作的那个日程管理平台也是一个不错的东西,其实可以把以前想做的,那个依照《高效人士七个习惯》里提出的日程管理的方法,以Web化的方式实现在这个平台上,要继续开发下去。</p>
<p>自己的不足,对于很多技术的简单应用其实还是游刃有余的状态,但是代码/系统开始复杂的时候就开始手足无措了;看书也很努力很认真,可是思考和总结还是很少,作笔记太少;平时开发时遇到问题时没有作记录下解决方法,之后遇到相同的问题又去查找了一次,太低效率的做法了;有买书强迫症,看到好书就想买,不会太管自己身上还有多少钱,买来又看不完,买了的一些书到现在还只是看完了前言,又浪费钱又阻住地方;英语现在还是太烂,能翻译文档又怎样,能用简单的词语和鬼佬交流又怎样,很多文章还是要一边查词一边看,口语又烂,英文技术写作能力又不行,英语真的是一个相当相当重要的一个工具;数学不行,越来越发现数学的重要,计算机世界本来就是构建在大量的数学定理和分析方法之上的。其实领导能力也是很重要的,自己一个人能做出一个东西,再怎样也是有限的,只有能领导一批人完成一个东西才是真的厉害。</p>
<p>看到了不足就要改进。还有有一个不错的工具,panda和toplang上的同学也经常提到的,那就是Google Notes,好好利用这个工具。</p>
<p>我相信,未来只能由自己创造,自己就是自己的救世主。现在还是和我的理想差的远了,还要努力才行,我要变得很厉害很厉害。</p>
差距
2008-06-20T00:00:00Z
https://thekaiway.com/2008/06/20/%E5%B7%AE%E8%B7%9D/
<p>我和一些同样在技术之路上的同龄人具有不小的差距,如<a href="http://pluskid.lifegoo.com">Male</a>,还有以前金中去了MS的那个人等。</p>
<p>其实这些差距很多人都说是来自小时候的教育,那些人家庭教育背景都是不错的,父母都是大学毕业,或者知识水平比较高。他们都很早就接触了计算机(是接触了计算机而不是接触游戏),小时候就学会了英语。比如云风,初中就开始学习编程,比如toplang里的一些人说到的,高中初中就读了编译原理这一类的计算机经典理论书籍。</p>
<p>不过大部分人应该都是到了大学才真正的开始接触,学习计算机知识。其实如果来到大学之前,对计算机有所了解但不是掌握很多的话,其实四年(其实还不够四年就是大一到大四前段)也就是全部的学习时间。大一还是在懵懵懂懂,在摸索自己的技术之路,到了大二开始知道怎么学习基础,到了大三,开始要想去作些东西,到了大四,没时间了。</p>
<p>其实四年很短,这就拉开了和一开始就接受了很好教育的人的差距。到了工作的时候,如果自己不挤榨时间来学习的话就没有学习的机会了,最近在忙的时候这种感觉特别的重。</p>
Guile
2008-06-19T00:00:00Z
https://thekaiway.com/2008/06/19/guile/
<p>Guile,一个GNU的Scheme的解析器。</p>
<p>官站:<a href="http://www.gnu.org/software/guile">http://www.gnu.org/software/guile</a></p>
<p>很好玩的一个解析器,在学习《SICP》的好工具,而且带有如何实现的文档,翻了一下它的文档,好像与MRI Ruby差不多,支持C 扩展。这是个不错的解析器的参考。</p>
<p>最近比较忙,学习时间只有中午和睡觉前这段时间,可以我还是要努力,我要成为能够被人记住的一个开发者。</p>
一段日历生成的代码
2008-06-17T00:00:00Z
https://thekaiway.com/2008/06/17/%E4%B8%80%E6%AE%B5%E7%94%9F%E6%88%90%E6%97%A5%E5%8E%86%E7%9A%84%E4%BB%A3%E7%A0%81/
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">month_day</span></span><span class="token punctuation">(</span>month<span class="token punctuation">,</span> year<span class="token operator">=</span>Date<span class="token punctuation">.</span>today<span class="token punctuation">.</span>year<span class="token punctuation">)</span>
mdays <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token keyword">nil</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">28</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token number">31</span><span class="token punctuation">]</span>
mdays<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">29</span> <span class="token keyword">if</span> Date<span class="token punctuation">.</span>leap<span class="token operator">?</span><span class="token punctuation">(</span>year<span class="token punctuation">)</span>
mdays<span class="token punctuation">[</span>month<span class="token punctuation">]</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">calendar</span></span><span class="token punctuation">(</span>month<span class="token punctuation">,</span> year<span class="token punctuation">)</span>
days <span class="token operator">=</span> month_day<span class="token punctuation">(</span>month<span class="token punctuation">,</span> year<span class="token punctuation">)</span>
t <span class="token operator">=</span> <span class="token builtin">Time</span><span class="token punctuation">.</span>mktime<span class="token punctuation">(</span>year<span class="token punctuation">,</span> month<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
first <span class="token operator">=</span> t<span class="token punctuation">.</span>wday
list <span class="token operator">=</span> <span class="token operator">*</span><span class="token number">1.</span><span class="token punctuation">.</span>days
weeks <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
week1 <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> first
week1<span class="token punctuation">.</span>times <span class="token punctuation">{</span> weeks<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">&</span>amp<span class="token punctuation">;</span>lt<span class="token punctuation">;</span><span class="token operator">&</span>amp<span class="token punctuation">;</span>lt<span class="token punctuation">;</span> list<span class="token punctuation">.</span>shift <span class="token punctuation">}</span>
nweeks <span class="token operator">=</span> list<span class="token punctuation">.</span>size<span class="token operator">/</span><span class="token number">7</span> <span class="token operator">+</span> <span class="token number">1</span>
nweeks<span class="token punctuation">.</span>times <span class="token keyword">do</span> <span class="token operator">|</span>i<span class="token operator">|</span>
weeks<span class="token punctuation">[</span>i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">||=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token number">7.</span>times <span class="token keyword">do</span>
<span class="token keyword">break</span> <span class="token keyword">if</span> list<span class="token punctuation">.</span>empty<span class="token operator">?</span>
weeks<span class="token punctuation">[</span>i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">&</span>amp<span class="token punctuation">;</span>lt<span class="token punctuation">;</span><span class="token operator">&</span>amp<span class="token punctuation">;</span>lt<span class="token punctuation">;</span> list<span class="token punctuation">.</span>shift
<span class="token keyword">end</span>
<span class="token keyword">end</span>
pad_first <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> weeks<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>size
pad_first<span class="token punctuation">.</span>times <span class="token punctuation">{</span> weeks<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>unshift<span class="token punctuation">(</span><span class="token keyword">nil</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
pad_last <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> weeks<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>size
pad_last<span class="token punctuation">.</span>times <span class="token punctuation">{</span> weeks<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>unshift<span class="token punctuation">(</span><span class="token keyword">nil</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
weeks
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">calendar_table</span></span><span class="token punctuation">(</span>month<span class="token punctuation">,</span> year<span class="token punctuation">)</span>
today <span class="token operator">=</span> <span class="token builtin">Time</span><span class="token punctuation">.</span>now<span class="token punctuation">.</span>day
table_body <span class="token operator">=</span> <span class="token string-literal"><span class="token string">""</span></span>
calendar<span class="token punctuation">(</span>month<span class="token punctuation">,</span> year<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span>week<span class="token operator">|</span>
table_body <span class="token operator">+=</span> <span class="token string-literal"><span class="token string">"&amp;lt;tr&amp;gt;"</span></span>
week<span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span>day<span class="token operator">|</span>
<span class="token keyword">if</span> day<span class="token punctuation">.</span><span class="token keyword">nil</span><span class="token operator">?</span>
table_body <span class="token operator">+=</span> <span class="token string-literal"><span class="token string">"&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;"</span></span>
<span class="token keyword">else</span>
table_body <span class="token operator">+=</span> <span class="token punctuation">(</span>day <span class="token operator">==</span> today<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string-literal"><span class="token string">"&amp;lt;td class='today'&amp;gt;"</span></span> <span class="token operator">:</span> <span class="token string-literal"><span class="token string">"&amp;lt;td&amp;gt;"</span></span>
table_body <span class="token operator">+=</span> <span class="token string-literal"><span class="token string">"</span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">day</span><span class="token delimiter punctuation">}</span></span><span class="token string">&amp;lt;/td&amp;gt;"</span></span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
table_body <span class="token operator">+=</span> <span class="token string-literal"><span class="token string">"&amp;lt;/tr&amp;gt;"</span></span>
<span class="token keyword">end</span>
table_body
<span class="token keyword">end</span></code></pre>
<p>calendar(month, year)方法其实是《The Ruby Way》里面的。</p>
毕业设计题目确定
2008-06-06T00:00:00Z
https://thekaiway.com/2008/06/06/%E5%87%86%E5%A4%87%E5%86%99%E4%B8%AA%E7%BC%96%E8%AF%91%E5%99%A8/
<p>现在就要准备毕业设计了,打算暑假留在学校这里。编译器不是个简单的东西,就算做出来了,如主任说的“千里马需要伯乐,如果你做了这个出来,老师们没有兴趣看这个,那也没用。”</p>
<p>其实现在是想作编译器,但是最后可能会做出一个C语言的解析器(让C动起来)的东西,很难说。最近在关注一个Ruby VM,Rubinius,想让Ruby实现Ruby VM的项目。这个东西满有参考价值的,而且可以的话我也想参与到其中。而Rubinius的开发方式是挺特别的,大家都可以提交mspec(用BDD的形式描述VM的运行),然后项目开发者们就实现相关的代码,过几天我把我从Rubinius那里了解到的东西写一下。或许还会翻译一些Rubinius的文档。</p>
<p>要作编译器或者VM,那需要编译原理等知识,下面列举一下所需知识和资料:</p>
<p>程序设计语言原理,其中包括语法,语义的解析等等。《SICP》,《程序设计语言实践之路》。
编译原理,其中包括代码解析,运行时环境构建等等。《编译原理2e》,《现代编译原理-C语言描述》(龙虎两书)</p>
<p>我其实是想在这个过程中学习到更多东西,如果真的可以写出来的话,自己的思想应该就不会像现在那么幼稚了。做个编译器/解析器的第一步应该就是能做出一个Scheme的解析器。</p>
<hr>
<h3 id="update-at-2012" tabindex="-1">Update at 2012 <a class="header-anchor" href="https://thekaiway.com/2008/06/06/%E5%87%86%E5%A4%87%E5%86%99%E4%B8%AA%E7%BC%96%E8%AF%91%E5%99%A8/">#</a></h3>
<p>当时是这么想的,但是最后只是做了REE的GC分析。</p>
GAE开放
2008-05-29T00:00:00Z
https://thekaiway.com/2008/05/29/%E8%B0%B7%E6%AD%8C%E5%BC%80%E6%94%BEgoogle-app-engine/
<p>http://code.google.com/appengine/</p>
<p>谷歌开放了免费的App Engine,500M的储存空间&5百万的每月PV。看了一下之后是基于Py的,最简单的HelloWorld只要写个输出的Py脚本再用个yaml指定请求的路由处理。如果是需要更加复杂的处理就是要使用谷歌提供的Web框架,其中包装了Django等框架。看来是个不错的机会学习Py和Py的WebApp开发。感觉好像是谷歌开始要推广Py。</p>
<p>在YouTube的视频介绍:
http://www.youtube.com/watch?v=3Ztr-HhWX1c
http://www.youtube.com/watch?v=tcbpTQXNwac
http://www.youtube.com/watch?v=oG6Ac7d-Nx8
http://www.youtube.com/watch?v=oTFL7FPLnXY
http://www.youtube.com/watch?v=JcM2Ejk1tis
http://www.youtube.com/watch?v=K7usoKm5zwE</p>
<p>不知道是不是RP问题,创建时一直提示域名不可用......或许毕业后会去作个Pyer或者Cpper,而不是Rubyist。</p>
Rspec 1.1.4更新
2008-05-29T00:00:00Z
https://thekaiway.com/2008/05/29/rspec-114%E7%89%88%E7%9A%84%E6%96%B0%E7%89%B9%E6%80%A7/
<p>Rspec新版本发布了。来看看它又带来了什么。</p>
<p>hash_including方法,它能让开发者在Mock接受参数时不用指定全部参数,只需要指定应包含某个键值对。</p>
<p>示例代码如下:</p>
<p># before
account.should_receive(:deposit).with({:amount => 37.42, :date => anything()})
# in 1.1.4
account.should_receive(:deposit).with(hash_including(:amount => 37.42))</p>
<p>修正了heckle支持的bug。stub_model方法出现了,再也不用在View Spec里用mock_model之后,一个一个属性写上去了。</p>
<p>参考:<br><a href="http://blog.davidchelimsky.net/articles/2008/05/27/rspec-1-1-4">http://blog.davidchelimsky.net/articles/2008/05/27/rspec-1-1-4</a></p>
c.vim简介
2008-05-28T00:00:00Z
https://thekaiway.com/2008/05/28/%E8%AF%95%E7%94%A8cvim/
<p>c.vim(C-support)是一个vim插件,C/Cpp开发的助推器,这个系列还有一个lua和Bash的版本。</p>
<p>不废话了,先看看它的主要功能先。</p>
<p>首先是代码模板:</p>
<pre><code> -- Statements
\sd do { } while (n,v,i)
\sf for (n,i)
\sfo for { } (n,v,i)
\si if (n,i)
\sif if { } (n,v,i)
\sie if else (n,v,i)
\sife if { } else { } (n,v,i)
\sw while (n,i)
\swh while { } (n,v,i)
\ss switch (n,v,i)
\sc case (n,i)
\s{ { } (n,v,i)
-- Preprocessor
\p&lt; #include &lt;&amp;gt; (n,i)
\p" #include "" (n,i)
\pd #define (n,i)
\pu #undef (n,i)
\pie #if #else #endif (n,v,i)
\pid #ifdef #else #endif (n,v,i)
\pin #ifndef #else #endif (n,v,i)
\pind #ifndef #def #endif (n,v,i)
\pi0 #if 0 #endif (n,v,i)
\pr0 remove #if 0 #endif (n)
-- Idioms -------------------------------------------------------------
\if function (n,v,i)
\isf static function (n,v,i)
\im main() (n,v,i)
\i0 for( x=0; x&lt;n; x+=1 ) (n,v,i)
\in for( x=n-1; x&gt;=0; x-=1 ) (n,v,i)
\ie enum + typedef (n,i)
\is struct + typedef (n,i)
\iu union + typedef (n,i)
\ip printf() (n,i)
\isc scanf() (n,i)
\ica p=calloc() (n,i)
\ima p=malloc() (n,i)
\isi sizeof() (n,v,i)
\ias assert() (n,v)
\ii open input file (n,i)
\io open output file (n,i)
-- Snippets
\nr read code snippet (n)
\nw write code snippet (n,v)
\ne edit code snippet (n)
\np pick up prototype (n,v)
\ni insert prototype(s) (n)
\nc clear prototype(s) (n)
\ns show prototype(s) (n)
-- C++
\+c class (n,i)
\+cn class (using new) (n,i)
\+ci class implementation (n,i)
\+cni class (using new) implementation (n,i)
\+mi method implementation (n,i)
\+ai accessor implementation (n,i)
\+tc template class (n,i)
\+tcn template class (using new) (n,i)
\+tci template class implementation (n,i)
\+tcni template class (using new) impl. (n,i)
\+tmi template method implementation (n,i)
\+tai template accessor implementation (n,i)
\+tf template function (n,i)
\+ec error class (n,i)
\+tr try ... catch (n,v,i)
\+ca catch (n,v,i)
\+c. catch(...) (n,v,i)
</code></pre>
<p>这个表最左边的就是按键,中间就是对应的模板,最后面是对应的模式。n是normal,i就是插入模式,v是虚拟模式。展开的模板放在 ~/.vim/c-support/templates 这里,自己可以hack,比如第一件事就是把Templates中的个人资料改一下。我还把函数声明改了一下,默认是把返回值类型放到上一行,看起来不爽。</p>
<p>然后就是编译链接和运行代码啦,我只用快捷键。</p>
<pre><code> F9 compile and link
Alt-F9 write buffer and compile
Ctrl-F9 run executable
Shift-F9 set command line arguments
Shift-F2 switch between source files and header files
有了这些快捷键之后加上Vim7本身就有的一点点补全功能,其实这个工具也有些IDEful了。
最后看看一些实用功能,比如代码和注释的转化,还有行尾插入注释,插入注释行等等。
-- Comments -----------------------------------------------------------
\cl end-of-line comment (n,v,i)
\cj adjust end-of-line comment(s) (n,v,i)
\cs set end-of-line comment column (n)
\c* code -&gt; comment /* */ (n,v)
\c/ code -&gt; comment // (n,v)
\cc code -&gt; comment // (n,v)
\co comment -&gt; code (n,v)
\cfr frame comment (n,i)
\cfu function comment (n,i)
\cme method description (n,i)
\ccl class description (n,i)
\cd date (n,i)
\ct date \&amp; time (n,i)
</code></pre>
<p>有空试用一下lua-support和bash-support。其实一直想写篇rails.vim的配置和使用和一点点Src分析。</p>
TCPPL的一段话
2008-05-28T00:00:00Z
https://thekaiway.com/2008/05/28/tcpppl%E4%B8%AD%E7%9A%84%E4%B8%80%E6%AE%B5%E8%AF%9D/
<p>这是在B.Stroustrup的TCppPL中Chapter2最后结尾的一段话:</p>
<blockquote>
<p>没有一种程序设计语言是完美无缺的。幸运的是,一种程序设计语言不必是完美无缺的,也可以成为构造伟大系统的良好工具。事实上,一种通用的程序设计语言根本不可能对它被用于的所有工作都是最完美的,对一项工作来说最完美的东西对于另外的工作就常常会表现出严重的缺陷,因为在一个领域中的最完美事物实际上也就是意味着专门化。Cpp的设计是想成为构造范围广泛多样的系统的良好工具,而且能够直接表达范围广泛多样的思想。<br><br>并不是所有的东西都能直接通过语言的某些内部特征表述。事实上,这也不应该成为一种理想。语言特征的存在是为了支持各种各样的程序设计风格和技术,因此,学习一种语言的工作就应该集中于去把握对该语言而言固有的和自然的风格--而不是去理解该语言所有语言特征的细枝末节。<br><br>在实践性的程序设计中,理解语言中最晦涩难懂的语言特征,或者使用最大量的不同特征并不能获得什么利益。把一种特征孤立起来看并没有什么意思,只是在由技术和其他特征所形成的环境里,这一特征才获得了意义和趣味。因此,在阅读后面的章节时请牢记,考察Cpp的各种细节的真实目的在于能够应用它们,在有效的环境里,去支持良好的程序设计风格。</p>
</blockquote>
<p>Bjarne真的是一个超人级的人物,很早就看清了程序设计语言的真谛。这段话的意思人人都懂吧。(还有赞叹一下裘掌门的翻译功力)</p>
<p>上次Topcoder的比赛很失败,让我觉着自己。这让我又回过头来再看Cpp的书,看Cpp圣经--TCppPL,看侯Sir的STL剖析。看着Cpp的代码有种亲切的感觉,书中Bjarne写出来的Sample,很优美干练,可读性也非常高。</p>
<p>其实程序设计语言规格到底还是给人来描述现实问题的,描述后让计算机来处理问题,所以能更好地解决问题的语言就是一门不错的语言。RoR最能体现专门化解决问题的思想,Ruby的DSL能力不是盖的。而创造Ruby的这些DSL或者框架的超人们,他们都是掌握了良好程序设计风格的家伙。(说到最后知不到自己在说什么)</p>
体验Rails 2.1.rc1
2008-05-18T00:00:00Z
https://thekaiway.com/2008/05/18/%E6%97%A0%E7%97%9B%E4%BD%93%E9%AA%8Crails21rc1/
<p>无须升级整个系统的Rails,只需要在项目中的Rakefile中加入下面的代码,然后运行rake rails:freeze:edge:</p>
<p>namespace :rails do
namespace :freeze do
desc 'Lock to latest Edge Rails, for a specific release use RELEASE=1.2.0'
task :edge do
require 'open-uri'
version = ENV["RELEASE"] || "edge"
target = "rails_#{version}.zip"
url = "http://dev.rubyonrails.org/archives/#{target}"</p>
<p>chdir 'vendor' do
puts "Downloading Rails from #{url}"
File.open('rails.zip', 'wb') do |dst|
open url do |src|
while chunk = src.read(4096)
dst << chunk
end
end
end</p>
<p>puts 'Unpacking Rails'
rm_rf 'rails'
<code>unzip rails.zip</code>
%w(rails.zip rails/Rakefile rails/cleanlogs.sh rails/pushgems.rb rails/release.rb).each do |goner|
rm_f goner
end
end</p>
<p>puts 'Updating current scripts, javascripts, and configuration settings'
Rake::Task['rails:update'].invoke
end
end
end</p>
<p>这段代码其实是Rails2.1里的rails:freeze:edeg的Rake任务代码,因为Rails的代码库已经迁移至github,好像svn不提供新的版本,现在获取Rails最新版本的方法一是到github那里clone下来,要么就到http://dev.rubyonrails.org/archives/下载一个打包好的zip文件。</p>
<p>其实最傻瓜的方法就是直接下载http://dev.rubyonrails.org/archives/rails_edge.zip然后解压到vendor/那里,其实上面贴的rake脚本也是做了这个事情:-)</p>
[译文] Shoulda教程4 - Test Controller
2008-05-18T00:00:00Z
https://thekaiway.com/2008/05/18/shoulda-tutor4/
<p>基本的控制助手方法</p>
<p>如ActiveRecord宏一样,Shoulda 提供了一套测试控制器的宏,以尽可能简洁的方法进行测试。所有的这些方法都在Shoulda的Rdoc中,但这里再送上一个快捷的例子:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UsersControllerTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
context <span class="token string-literal"><span class="token string">"on GET to :show"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> get <span class="token symbol">:show</span><span class="token punctuation">,</span> <span class="token symbol">:id</span> <span class="token operator">=></span> <span class="token number">1</span> <span class="token punctuation">}</span>
should_assign_to <span class="token symbol">:user</span>
should_respond_with <span class="token symbol">:success</span>
should_render_template <span class="token symbol">:show</span>
should_not_set_the_flash
should <span class="token string-literal"><span class="token string">"do something else really cool"</span></span> <span class="token keyword">do</span>
assert_equal <span class="token number">1</span><span class="token punctuation">,</span> assigns<span class="token punctuation">(</span><span class="token symbol">:user</span><span class="token punctuation">)</span><span class="token punctuation">.</span>id
<span class="token keyword">end</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"on POST to :create"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> post <span class="token symbol">:create</span><span class="token punctuation">,</span> <span class="token symbol">:user</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">'Ronald'</span></span><span class="token punctuation">,</span> <span class="token symbol">:party</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">'Repukeulan'</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
should_assign_to <span class="token symbol">:user</span>
should_redirect_to <span class="token string-literal"><span class="token string">"user_url(@user)"</span></span>
should_set_the_flash_to<span class="token punctuation">(</span><span class="token regex-literal"><span class="token regex">/created/i</span></span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<h2 id="restful" tabindex="-1">应该RESTful <a class="header-anchor" href="https://thekaiway.com/2008/05/18/shoulda-tutor4/">#</a></h2>
<p>这里每个 should_xxx 宏都会产生一个单独的测试方法,编写起来又DRY。而should_be_restful 宏可以产生遵循基本的RESTful设计模式的控制器。should_be_restful 宏就像一个超级测试生成器,每次调用是都会产生40到50个测试方法。这里有个超简单的例子:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UsersControllerTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token symbol">:first</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"Billy Bumbler"</span></span><span class="token punctuation">,</span> <span class="token symbol">:party</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">'Sure do!'</span></span><span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"Changed"</span></span> <span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>这里会生成一整套包含全部CRUD行为的测试,也包括 :new 和 :edit 。对这个宏还有很多定义的空间,能让它处理各种情况的控制器,不过默认设置已经非常智能了。在上面的例子中,通过测试类名就能查找出如何处理User模型,users_url方法,params[:user],@user 和@users实例变量等。</p>
<h3 id="should-be-restful" tabindex="-1">配置should_be_restful <a class="header-anchor" href="https://thekaiway.com/2008/05/18/shoulda-tutor4/">#</a></h3>
<p>来看看更改should-be_restful产生的测试的行为的方法。(这些也在Rdoc文档中有详细记录)</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UsersControllerTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token symbol">:first</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>klass <span class="token operator">=</span> User
resource<span class="token punctuation">.</span>object <span class="token operator">=</span> <span class="token symbol">:user</span>
resource<span class="token punctuation">.</span>parent <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
resource<span class="token punctuation">.</span>actions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token symbol">:index</span><span class="token punctuation">,</span> <span class="token symbol">:show</span><span class="token punctuation">,</span> <span class="token symbol">:new</span><span class="token punctuation">,</span> <span class="token symbol">:edit</span><span class="token punctuation">,</span> <span class="token symbol">:update</span><span class="token punctuation">,</span> <span class="token symbol">:create</span><span class="token punctuation">,</span> <span class="token symbol">:destroy</span><span class="token punctuation">]</span>
resource<span class="token punctuation">.</span>formats <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token symbol">:html</span><span class="token punctuation">,</span> <span class="token symbol">:xml</span><span class="token punctuation">]</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"bob"</span></span><span class="token punctuation">,</span> <span class="token symbol">:email</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">'bob@bob.com'</span></span><span class="token punctuation">,</span> <span class="token symbol">:age</span> <span class="token operator">=></span> <span class="token number">13</span><span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"sue"</span></span> <span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>redirect <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"user_url(@user)"</span></span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>redirect <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"user_url(@user)"</span></span>
resource<span class="token punctuation">.</span>destroy<span class="token punctuation">.</span>redirect <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"users_url"</span></span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>flash <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/created/i</span></span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>flash <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/updated/i</span></span>
resource<span class="token punctuation">.</span>destroy<span class="token punctuation">.</span>flash <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/removed/i</span></span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>下面是参数的具体解释:</p>
<ul>
<li>klass:要处理的模型类。</li>
<li>objuect:用于#edit,#update和#destroy测试的对象的名字。所以如果在setup中实例化@user,那么res.object 应该设置为 :user。</li>
<li>parent:嵌套资源中的上级对象名字。这是要传入到嵌套资源url助手方法的参数。如果不需要用到嵌套资源,可以忽略这个参数。如果使用嵌套资源,路由设置中:users置于:cities之下,那么user测速中res.parent设置应为:city。实际上,parent参数是User模型类的关联对象引用。所以如果当User belongs_to :location,class_name => "City",那么应该把这个参数设置为:city。</li>
<li>actions:要测试的行为列表。这个值应该是[:index, :show, :new, :edit, :create, :update, :destroy](默认)的子集或全集。如果控制器是read_only的,那么可能需要设置这个值为[:index, :show]。</li>
<li>formats:测试控制器支持的格式列表。默认值为:html和 :xml,以后会加入JSON,CSV和其他。添加上其他的新格式也是非常简单的,可以参考ThoughtBot::Shoulda::Controller::XML文件。</li>
</ul>
<p>在#create,#update和#destory测试中可以附上一些参数:</p>
<ul>
<li>params:要传给行为的参数。</li>
<li>flash:在行为完成之后在flash中预期得到的值。可以是字符串或者正则表达式。</li>
<li>redirect:在行为完成之后应该重定向到方向。这里有些巧妙...在测试运行时才会解释设置的字符串。这个延后解析的字符串将会遍历所有的命名路由和所有控制器创建的实例变量。</li>
</ul>
<h3 id="denied" tabindex="-1">Denied行为 <a class="header-anchor" href="https://thekaiway.com/2008/05/18/shoulda-tutor4/">#</a></h3>
<p>RESTful控制器的部分或全部的行为在大多数场景里是有访问限制的(比如需要用户认证的时候)。resource.denied选项让should_be_restful原生地支持行为的访问限制。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UsersControllerTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token symbol">:first</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"bob"</span></span><span class="token punctuation">,</span> <span class="token symbol">:email</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">'bob@bob.com'</span></span><span class="token punctuation">,</span> <span class="token symbol">:age</span> <span class="token operator">=></span> <span class="token number">13</span><span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"sue"</span></span> <span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>actions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token symbol">:new</span><span class="token punctuation">,</span> <span class="token symbol">:create</span><span class="token punctuation">,</span> <span class="token symbol">:edit</span><span class="token punctuation">,</span> <span class="token symbol">:update</span><span class="token punctuation">,</span> <span class="token symbol">:destroy</span><span class="token punctuation">]</span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>redirect <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"new_session_url"</span></span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>flash <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/administrator/i</span></span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>每个列在resource.denied.actions的行为(默认为空)将测试行为被限制访问,相关的记录不会被触及。 :redirect 和 :flash 参数和上面的 resource.create 和 resource.update 一样。</p>
<h3 id="should-be-restful-1" tabindex="-1">混合使用should_be_restful和上下文 <a class="header-anchor" href="https://thekaiway.com/2008/05/18/shoulda-tutor4/">#</a></h3>
<p>测试每个控制器和所有控制器行为时,混合should_be_restful和上下文代码块是一个非常强大的方法。下面的代码是在我们(作者)的应用的测试中很常见到。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">PostsControllerTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token variable">@user</span> <span class="token operator">=</span> users<span class="token punctuation">(</span><span class="token symbol">:bob</span><span class="token punctuation">)</span>
<span class="token variable">@post</span> <span class="token operator">=</span> <span class="token variable">@user</span><span class="token punctuation">.</span>posts<span class="token punctuation">.</span>first
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"An administrator"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> login_as users<span class="token punctuation">(</span><span class="token symbol">:admin</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:subject</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"test"</span></span><span class="token punctuation">,</span> <span class="token symbol">:body</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"message"</span></span> <span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:subject</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"changed"</span></span> <span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"A user"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> login_as <span class="token variable">@user</span> <span class="token punctuation">}</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:subject</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"test"</span></span><span class="token punctuation">,</span> <span class="token symbol">:body</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"message"</span></span> <span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:subject</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"changed"</span></span> <span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"A stranger"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> login_as users<span class="token punctuation">(</span><span class="token symbol">:sally</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:subject</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"test"</span></span><span class="token punctuation">,</span> <span class="token symbol">:body</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"message"</span></span> <span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>actions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token symbol">:edit</span><span class="token punctuation">,</span> <span class="token symbol">:update</span><span class="token punctuation">,</span> <span class="token symbol">:destroy</span><span class="token punctuation">]</span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>redirect <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"login_url"</span></span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>flash <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/only the owner can/i</span></span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"The public"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> logout <span class="token punctuation">}</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>actions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token symbol">:new</span><span class="token punctuation">,</span> <span class="token symbol">:create</span><span class="token punctuation">,</span> <span class="token symbol">:edit</span><span class="token punctuation">,</span> <span class="token symbol">:update</span><span class="token punctuation">,</span> <span class="token symbol">:destroy</span><span class="token punctuation">]</span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>redirect <span class="token operator">=</span> <span class="token string-literal"><span class="token string">"signup_url"</span></span>
resource<span class="token punctuation">.</span>denied<span class="token punctuation">.</span>flash <span class="token operator">=</span> <span class="token regex-literal"><span class="token regex">/must be a member/i</span></span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<h3 id="" tabindex="-1">混合与匹配 <a class="header-anchor" href="https://thekaiway.com/2008/05/18/shoulda-tutor4/">#</a></h3>
<p>最后,记住更好的测试在should_be_restful助手之外的额外行为,或者在Shoulda中加入一般的test_xxx方法。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UsersControllerTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>find
<span class="token keyword">end</span>
should_be_restful <span class="token keyword">do</span> <span class="token operator">|</span>resource<span class="token operator">|</span>
resource<span class="token punctuation">.</span>create<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"Billy Bumbler"</span></span><span class="token punctuation">,</span> <span class="token symbol">:party</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">'Sure do!'</span></span><span class="token punctuation">}</span>
resource<span class="token punctuation">.</span>update<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=></span> <span class="token string-literal"><span class="token string">"Changed"</span></span> <span class="token punctuation">}</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"on GET to /users/:id;activate"</span></span>
setup <span class="token punctuation">{</span> get <span class="token symbol">:activate</span><span class="token punctuation">,</span> <span class="token symbol">:id</span> <span class="token operator">=></span> <span class="token variable">@user</span><span class="token punctuation">.</span>id <span class="token punctuation">}</span>
should_render_template <span class="token symbol">:activate</span>
<span class="token comment"># etc.</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">test_normal_stuff</span></span>
assert <span class="token boolean">true</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
Shoulda教程索引
2008-05-18T00:00:00Z
https://thekaiway.com/2008/05/18/shoulda-index/
<p>终于把这个框架的文档翻译完了。最后一篇的缩进真是搞死我了,如果wp的编辑器更加强些就好了。</p>
<ul>
<li><a href="https://thekaiway.com/2008/05/10/shoulda-tutor1">Shoulda教程之一—基本的should语句</a></li>
<li><a href="https://thekaiway.com/2008/05/10/shoulda-tutor2">Shoulda教程之二—上下文</a></li>
<li><a href="https://thekaiway.com/2008/05/18/shoulda-index/2008/05/11/shoulda-tutor3">Shoulda教程之三—测试ActiveRecord模型</a></li>
<li><a href="https://thekaiway.com/2008/05/18/shoulda-tutor4">Shoulda教程之四—测试控制器</a></li>
</ul>
<p>其实看到Shoulda这个框架之后我会有很多思考,思考之前的BDD或TDD是不是太麻烦了,比如每次Fun Test都是要些mock Model,然后才能测试每次每次都是在重复,而每次页面的测试(Rspec里才有分离出来的测试)每次都要先构造出页面中要调用的对象,再assigns页面要调用到的元素之后再测试。整个过程非常繁琐,虽然这样可以很细粒度地测试到整个应用,但一点也不DRY(或许我的水平还不够)。</p>
<p>再抱怨一点,很多有名的Rails开源应用(如Beast,Redmind,Typo等等)很多都还未迁移到Rspec,虽然他们的项目目录中很多都有了spec这个目录,可是一般都是空的。想看到的Rspec的用于集成测试的Stroy框架的实例也没有,这个只能在教程中看到。Rspec的教程也太少,Rdoc中基本没有什么解释,代码里也是,如果对一些东西不知道怎么用就要去看代码,有些代码只是包装了Rails中的assist方法的还要去查Rails的方法代码(如果对Rails的一些assist不熟的话)。虽然这个过程可以看很多代码,学到更多,可是也太费时间了。</p>
<p>最后如果能把Shoulda的一些强大的宏移植到Rspec那就好了。当然,我会继续研究Shoulda源码,还要下一步要看看merb。</p>
<p>翻译完最后生成PDF在这里可以下载:http://download.csdn.net/source/458857</p>
Rails 2.1.rc1 Changelog
2008-05-18T00:00:00Z
https://thekaiway.com/2008/05/18/rails-2-1-changelog/
<p>Rails2.1RC1的Changelog也是满有趣的。</p>
<p>http://api.rubyonrails.org/files/vendor/rails/railties/CHANGELOG.html</p>
<p>可以看到最下面这里有写着兼容Ruby1.9,另一个比较重大的特性就是Timezone的加入,另外还有对于Rails应用程序的依赖包的管理。其它都是小改动或者修补Bug。</p>
参加BEA JAVA2SOA DEV2DEV Techdays
2008-05-14T00:00:00Z
https://thekaiway.com/2008/05/14/bea-java2soa-dev2dev-techdays/
<img src="http://lh3.ggpht.com/chenk85/SCq44eDvyBI/AAAAAAAAACg/NRZLptA0tRA/SNV81056.JPG?imgmax=720" alt="">
<img src="http://lh5.ggpht.com/chenk85/SCq44-DvyCI/AAAAAAAAACo/m0VLYd_4JcE/SNV81057.JPG?imgmax=720" alt="">
<img src="http://lh3.ggpht.com/chenk85/SCq45eDvyDI/AAAAAAAAACw/krxRCgW1gy0/SNV81061.JPG?imgmax=720" alt="">
持续集成笔记本
2008-05-13T00:00:00Z
https://thekaiway.com/2008/05/13/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90de%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/
<p>五一的两天空余时间,读完了<a href="http://www.amazon.com/Continuous-Integration-Improving-Addison-Wesley-Signature/dp/0321336380/ref=sr_11_1?ie=UTF8&qid=1200534624&sr=11-1">持续集成</a>这本书。</p>
<p>Continuous Integration,持续集成,简称CI。这是一项业界推荐的软件开发实践,是XP中提到的实践之一。CI有四个特征:</p>
<blockquote>
<p>与版本控制系统的连接
构建脚本
某种类型的反馈机制
集成源代码变更的过程</p>
</blockquote>
<p>CI的价值在于:</p>
<blockquote>
<p>减少风险
减少重复过程
在随时随地生成可部署的软件
对开发团队的软件产品建立起更大的产品信心</p>
</blockquote>
<p>CI的步骤:</p>
<blockquote>
<p>“I Build So Consistently”。四个步骤:“Identify”,“Build”,“Share”,“Continuous”。</p>
</blockquote>
<p>CI的实践:</p>
<p>经常提交代码</p>
<blockquote>
<p>不要提交无法构建的代码
立即修复无法集成的构建
编写自动化的开发者测试
必须通过所有测试和审查
执行私有构建
避免签出无法构建的代码</p>
</blockquote>
<p>CI可以缓解一些关键的风险:</p>
<blockquote>
<p>没有可部署的软件
很晚才发现缺陷
缺乏项目可见性
低品质的软件</p>
</blockquote>
<p>CI系统的创建:</p>
<blockquote>
<p>持续数据库集成
持续测试
持续审查
持续部署
持续反馈</p>
</blockquote>
<p>Rails 的CI系统有来自TW的<a href="http://cruisecontrolrb.thoughtworks.com/">cruisecontrolrb</a></p>
[译文] Rails 2.1 dbconsole
2008-05-12T00:00:00Z
https://thekaiway.com/2008/05/12/rails-2-1-script-dbconsole/
<p>原文连接:http://blog.codefront.net/2008/05/11/living-on-the-edge-of-rails-20-scriptdbconsole-and-flashnow-now-test-able/</p>
<p>script/dbconsole 脚本允许用户使用Rails的控制台客户端连接到数据库。</p>
<p>如果需要连接到MySQL的生产数据库作一些操作,直接运行 RAILS_ENV=production script/dbconsole 就能登录到数据库服务器上并使用MySQL的命令行客户端。当然,这个脚本也同样在 PostgreSQL 和 SQLite 数据库运行。</p>
<p>要在当前Rails应用程序中使用这个新脚本,就要先升级到edge Rails,再运行 rake rails:update:script 。好好享受这个脚本的便利吧。</p>
[译文] New Feature on Rails 2.1: change_table
2008-05-12T00:00:00Z
https://thekaiway.com/2008/05/12/rails-2-1-migrations-change_table/
<p>原文:http://blog.codefront.net/2008/05/04/living-on-the-edge-of-rails-19-change_table-for-migrations-and-more/</p>
<p>现在可以通过 change_table 代码块来完成对数据库表的修改。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">change_table <span class="token symbol">:videos</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
t<span class="token punctuation">.</span>add_timestamps
t<span class="token punctuation">.</span>add_belongs_to <span class="token symbol">:goat</span>
t<span class="token punctuation">.</span>add_string <span class="token symbol">:name</span><span class="token punctuation">,</span> <span class="token symbol">:email</span><span class="token punctuation">,</span> <span class="token symbol">:limit</span> <span class="token operator">=</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> <span class="token number">20</span>
t<span class="token punctuation">.</span>remove_column <span class="token symbol">:name</span><span class="token punctuation">,</span> <span class="token symbol">:email</span> <span class="token comment"># takes multiple arguments</span>
t<span class="token punctuation">.</span>rename <span class="token symbol">:new_name</span>
t<span class="token punctuation">.</span>string <span class="token symbol">:new_string_column</span> <span class="token comment"># executes against the renamed table name</span>
<span class="token keyword">end</span></code></pre>
<p>补充些要注意的事情:</p>
<ul>
<li>add_XXX 方法会添加一个新列,比如 add_string 会添加一个新的 string 类型的字段。</li>
<li>Of course, add_timestamps 会添加神奇的 created_at 和 updated_at 的 datetime 类型的字段。</li>
<li>remove_column 现在可以接受多个参数。</li>
<li>rename 方法会重命名数据库表。</li>
</ul>
[译文] Rails 2.1 ActiveRecord的Create方法支持Block
2008-05-12T00:00:00Z
https://thekaiway.com/2008/05/12/rails-2-1-activerecord-basecreate-changes/
<p>ActiveRecord::Base.create 现在可以像 ActiveRecord::Base.new 一样带上一个代码块参数了。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token variable">@person</span> <span class="token operator">=</span> Person<span class="token punctuation">.</span>create<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token symbol">:person</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>p<span class="token operator">|</span>
p<span class="token punctuation">.</span>name <span class="token operator">=</span> <span class="token string-literal"><span class="token string">'Konata Izumi'</span></span>
p<span class="token punctuation">.</span>age <span class="token operator">=</span> <span class="token number">17</span>
<span class="token keyword">end</span></code></pre>
[译文] Shoulda教程3 - Model Helpers
2008-05-11T00:00:00Z
https://thekaiway.com/2008/05/11/shoulda-tutor3/
<h1 id="activerecord" tabindex="-1">ActiveRecord助手方法 <a class="header-anchor" href="https://thekaiway.com/2008/05/11/shoulda-tutor3/">#</a></h1>
<p>Shoulda具有一套ActiveRecord测试宏,这让开发效率大大提高,TDD变得轻而易举。关于这个方面的所有的文档都在Shoulda的Rdoc中。下面就来个小小的例子:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UserTest</span>
should_has_many <span class="token symbol">:dogs</span>
should_belong_to <span class="token symbol">:lover</span>
<span class="token keyword">end</span></code></pre>
<p>上面的代码创建了如下的测试:</p>
<pre class="language-bash" tabindex="0"><code class="language-bash">test: Person should allow phone_number to be <span class="token builtin class-name">set</span> to <span class="token string">"(123) 456-7890"</span><span class="token builtin class-name">.</span>
test: Person should belong to lover.
test: Person should have many dogs.
test: Person should have many messes through dogs.
test: Person should have one profile.
test: Person should not allow admin to be changed by update.
test: Person should not allow phone_number to be <span class="token builtin class-name">set</span> to <span class="token string">"1234"</span><span class="token builtin class-name">.</span>
test: Person should not allow phone_number to be <span class="token builtin class-name">set</span> to <span class="token string">"abcd"</span><span class="token builtin class-name">.</span>
test: Person should require name to be set.
test: Person should require phone_number to be set.
test: Person should require unique value <span class="token keyword">for</span> name.</code></pre>
<h2 id="" tabindex="-1">前提 <a class="header-anchor" href="https://thekaiway.com/2008/05/11/shoulda-tutor3/">#</a></h2>
<p>一件需要知道的事情是一些ActiveRecord测试宏找到第一个初始的记录(通过 Class.find(:first))。你可以让这个工作通过一个具有一条记录的fixture文件,或者通过在setup中创建一条记录,类似下面这样:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UserTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>create<span class="token operator">!</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span>
<span class="token keyword">end</span>
should_require_unique_attributes <span class="token symbol">:name</span>
<span class="token keyword">end</span></code></pre>
<p>或者通过一个context,如下:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">UserTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
context <span class="token string-literal"><span class="token string">"given an existing record"</span></span> <span class="token keyword">do</span>
setup <span class="token keyword">do</span>
<span class="token variable">@user</span> <span class="token operator">=</span> User<span class="token punctuation">.</span>create<span class="token operator">!</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span>
<span class="token keyword">end</span>
should_require_unique_attributes <span class="token symbol">:name</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>但是Shoulda不止是提供了对模型的测试,还提供了很多强大的助手方法可以使用于控制器。</p>
[译文] Shoulda教程2 - Context & Nesting
2008-05-10T00:00:00Z
https://thekaiway.com/2008/05/10/shoulda-tutor2/
<p>原文:http://thoughtbot.com/projects/shoulda/tutorial/context</p>
<h1 id="" tabindex="-1">上下文 <a class="header-anchor" href="https://thekaiway.com/2008/05/10/shoulda-tutor2/">#</a></h1>
<p>在学习了Should语句之后,Shoulda的更多基本构建代码的知识就是上下文(Context)。上下文创建一个运行should语句的类似场景的fixture。Context代码块可以包含 setup/teardown 代码块,should代码块,或者其他context代码块。下面来试试在测试中使用简单的context:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">QueueTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
context <span class="token string-literal"><span class="token string">"一个队列实例"</span></span> <span class="token keyword">do</span>
setup <span class="token keyword">do</span>
<span class="token variable">@queue</span> <span class="token operator">=</span> <span class="token class-name">Queue</span><span class="token punctuation">.</span><span class="token keyword">new</span>
<span class="token keyword">end</span>
should <span class="token string-literal"><span class="token string">"响应 :push 调用"</span></span> <span class="token keyword">do</span>
assert_respond_to <span class="token variable">@queue</span><span class="token punctuation">,</span> <span class="token symbol">:push</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>这里创建了一个名为"测试:一个队列实例可以响应:push调用"的测试方法,这很漂亮和易读。</p>
<h2 id="-1" tabindex="-1">嵌套 <a class="header-anchor" href="https://thekaiway.com/2008/05/10/shoulda-tutor2/">#</a></h2>
<p>上面详尽的测试,简单测试队列的实例响应:push调用,但也提出了更多的上下文和测试。现在想看看让队列返回任何放进其中的东西。来添加一个嵌套的上下文进到其中:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">QueueTest</span> <span class="token operator">&</span>lt<span class="token punctuation">;</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
context <span class="token string-literal"><span class="token string">"一个队列实例"</span></span> <span class="token keyword">do</span>
setup <span class="token keyword">do</span>
<span class="token variable">@queue</span> <span class="token operator">=</span> <span class="token class-name">Queue</span><span class="token punctuation">.</span><span class="token keyword">new</span>
<span class="token keyword">end</span>
should <span class="token string-literal"><span class="token string">"响应 :push 调用"</span></span> <span class="token keyword">do</span>
assert_respond_to <span class="token variable">@queue</span><span class="token punctuation">,</span> <span class="token symbol">:push</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"具有一个元素"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> <span class="token variable">@queue</span><span class="token punctuation">.</span>push<span class="token punctuation">(</span><span class="token symbol">:something</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
should <span class="token string-literal"><span class="token string">"在:pop调用后返回元素"</span></span> <span class="token keyword">do</span>
assert_equal <span class="token symbol">:something</span><span class="token punctuation">,</span> <span class="token variable">@queue</span><span class="token punctuation">.</span>pop
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>上面生成了测试方法"测试:一个队列实例应该响应:push调用"和"测试:一个队列实例具有一个元素应该在:pop调用后返回元素"。</p>
<p>注意一下,上下文的setup代码块是为每个should代码块运行一次的。首先@queue实例被创建出来,然后:something被放进去,接着执行assert_equal。下面用一个同样语意的测试方法虚拟地演示一下,记住下面的代码只是为了帮助理解,并不会实际的发生:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">define_method</span> <span class="token string-literal"><span class="token string">"测试:一个队列实例有一个元素应该在:pop调用后返回元素。"</span></span> <span class="token keyword">do</span>
<span class="token variable">@queue</span> <span class="token operator">=</span> <span class="token class-name">Queue</span><span class="token punctuation">.</span><span class="token keyword">new</span>
<span class="token variable">@queue</span><span class="token punctuation">.</span>push<span class="token punctuation">(</span><span class="token symbol">:something</span><span class="token punctuation">)</span>
assert_equal <span class="token symbol">:something</span><span class="token punctuation">,</span> <span class="token variable">@queue</span><span class="token punctuation">.</span>pop
<span class="token keyword">end</span></code></pre>
<p>现在,如果使用一般的测试风格,就会在一个测试方法中,把一个元素放进队列中并在一个测试方法中把元素取出来,然后进行断言。但是将这个测试分离出来放到一个测试push的上下文时,就可以添加更多的共享同样setup的测试。</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">QueueTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
context <span class="token string-literal"><span class="token string">"一个队列实例"</span></span> <span class="token keyword">do</span>
setup <span class="token keyword">do</span>
<span class="token variable">@queue</span> <span class="token operator">=</span> <span class="token class-name">Queue</span><span class="token punctuation">.</span><span class="token keyword">new</span>
<span class="token keyword">end</span>
should <span class="token string-literal"><span class="token string">"响应 :push 调用"</span></span> <span class="token keyword">do</span>
assert_respond_to <span class="token variable">@queue</span><span class="token punctuation">,</span> <span class="token symbol">:push</span>
<span class="token keyword">end</span>
context <span class="token string-literal"><span class="token string">"具有一个元素"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> <span class="token variable">@queue</span><span class="token punctuation">.</span>push<span class="token punctuation">(</span><span class="token symbol">:something</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
should <span class="token string-literal"><span class="token string">"在:pop调用后返回元素"</span></span> <span class="token keyword">do</span>
assert_equal <span class="token symbol">:something</span><span class="token punctuation">,</span> <span class="token variable">@queue</span><span class="token punctuation">.</span>pop
<span class="token keyword">end</span>
should <span class="token string-literal"><span class="token string">"在:size调用后返回1"</span></span> <span class="token keyword">do</span>
assert_equal <span class="token number">1</span><span class="token punctuation">,</span> <span class="token variable">@queue</span><span class="token punctuation">.</span>size
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>在让测试保持可读性和防止重复代码方面,嵌套的上下文是一个强大的工具。测试文件变得更大时,更多的这种重构就会起作用。</p>
<h2 id="setup" tabindex="-1">在setup中断言 <a class="header-anchor" href="https://thekaiway.com/2008/05/10/shoulda-tutor2/">#</a></h2>
<p>另一个美妙的setup代码块的特性是,可以运行一部分测试方法,也就是可以完美地把断言放在其中。在setup代码块中调用断言来代替在should代码块中断言,并不是一个非常好的做法,但是可以起到一个明确的检查作用。来个例子:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">PersonTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
context <span class="token string-literal"><span class="token string">"A Person"</span></span> <span class="token keyword">do</span>
setup <span class="token punctuation">{</span> assert <span class="token variable">@person</span> <span class="token operator">=</span> Person<span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token symbol">:first</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
should <span class="token string-literal"><span class="token string">"rock out"</span></span> <span class="token keyword">do</span>
assert <span class="token variable">@person</span><span class="token punctuation">.</span>rocks_out<span class="token operator">!</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>在find调用失败时,在setup中看到失败的断言比出现烦人的异常要好看多了。</p>
<p>should代码块和上下文是为测试宏(test macros)不错的起点。Shoulda还有一个ActiveRecord助手方法集合,让测试简明扼要。</p>
[译文]Shoulda教程1 - Should语法
2008-05-10T00:00:00Z
https://thekaiway.com/2008/05/10/shoulda-tutor1/
<p>原文:http://thoughtbot.com/projects/shoulda/tutorial/should</p>
<h1 id="should" tabindex="-1">Should 语句 <a class="header-anchor" href="https://thekaiway.com/2008/05/10/shoulda-tutor1/">#</a></h1>
<p>Should语句是一种简洁,优雅,高可读性的方式创建测试。Should语句能轻松地创建测试方法,所以完全向后兼容一般的Test::Unit 用法</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">class</span> <span class="token class-name">QuoteTest</span> <span class="token operator"><</span> Test<span class="token double-colon punctuation">::</span>Unit<span class="token double-colon punctuation">::</span>TestCase
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">setup</span></span>
<span class="token comment"># 和一般的Test::Unit一样</span>
<span class="token keyword">end</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">test_should_be_true</span></span>
assert <span class="token boolean">true</span>
<span class="token keyword">end</span>
should <span class="token string-literal"><span class="token string">"为真"</span></span> <span class="token keyword">do</span>
assert <span class="token boolean">true</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>以上的代码片断创建了两个测试方法:"测试: 应该为真"和test_should_be_true。在这个级别,两个方法只有名字不同。一旦你学习有关Contexts的知识,就可以找到一些非常有用的技巧。</p>
Apriori算法的Ruby实现
2008-05-10T00:00:00Z
https://thekaiway.com/2008/05/10/apriori%E7%AE%97%E6%B3%95%E7%9A%84ruby%E5%AE%9E%E7%8E%B0/
<p>Apriori的算法实现,上课用到了。老师从网上找了一个300多行代码的Ruby实现,太复杂了,改了一下,太麻烦,自己重写过。</p>
<p>下面是实现代码</p>
<p>#!/usr/bin/ruby -w
# Apriori 算法实现</p>
<p>class Apriori
attr_accessor :date_set, :min_sup, :lv1_item_set</p>
<p># 从指定文件中得到整个事务集
def get_data_set(input_file)
raise "Error: File #{input_file} does not exist" unless File.exists?(input_file)
content = ''
open(input_file) {|f| content = f.read}
content.inject([]) {|set, l| set <= item.size
@lv1_item_set.each do |i1| # 第一级项集
if (item | i1).size > item.size && intersection?(date, (item | i1))
candidacy_set.push((item | i1).sort).uniq! # 候选集生成
end
end
end
candidacy_set.sort
end</p>
<p># 从候选集产生频繁集
def create_frequent_item_set(candidacy_set)
frequent_item_set = Hash.new(0)
candidacy_set.each do |e|
@date_set.each { |date| frequent_item_set[e] += 1 if intersection?(date, e)}
end
return frequent_item_set.delete_if {|k, v| v = @min_sup }
# 输出第一级项集
open(output_file, "w") do |f|
@lv1_item_set.each { |e| f.puts e + " : " + @hash[e].to_s }
end
# 迭代生成候选集
result = []
until(result.empty?) do
# ......
end
end
end</p>
初试Should
2008-05-03T00:00:00Z
https://thekaiway.com/2008/05/03/shoulda%EF%BC%9A%E8%AE%A9testunit%E4%B9%9Fbdd/
<p>Shoulda是一个Rails插件,一个让Test::Unit也BDD的框架。这是官方的广告语:</p>
<blockquote>
<p>Shoulda – Making tests easy on the fingers and eyes</p>
</blockquote>
<p>我是在MountainWest RubyConf 2008的视频看到Tammer Saleh 讲了Shoulda才知道这个框架的:
<a href="http://mwrc2008.confreaks.com/12saleh.html">http://mwrc2008.confreaks.com/12saleh.html</a></p>
<p>下面是官方资料的一点翻译:</p>
<p>Shoulda Rails插件能让你轻松地写出优雅,易懂,易维护的测试。Shoulda 包含了添加到了Test::Unit框架的宏,断言和助手方法。不需重新编写测试,完全适合已有的测试。</p>
<p>助手方法 – context方法和should方法使用了类似Rspec的测试块语法。还有,内置的contexts和一种可读性极强的语法。</p>
<p>宏 – 通过强大的宏生成上百行Controller和ActiveRecord测试。能让你快速开始,确保应用程序遵循最佳实践。</p>
<p>断言 – 许多一般的Rails测试惯用法提取了一个好用的断言集合。</p>
<p>也有一个精简的Shoulda的Gem包适合非Rails应用。</p>
<p>以后应该会继续翻译下去的。</p>
<p><a href="http://www.thoughtbot.com/projects/shoulda">Shoulda 官方主页</a>
<a href="http://dev.thoughtbot.com/shoulda">Shoulda 的RDocs</a></p>
翻译了一篇ArchLinux
2008-05-01T00:00:00Z
https://thekaiway.com/2008/05/01/%E7%BF%BB%E8%AF%91arch-wiki%E4%B8%8A%E7%9A%84wicd%E9%A1%B5/
<p>早上翻译了下面这页:
http://wiki.archlinux.org/index.php/Wicd</p>
<p>其中过程,FF3忽然崩溃,又要翻译多一次,其实里面还有点还没有翻译完。。。</p>
<p>终于为ArchLinux做出了点贡献。</p>
Story on Rspec
2008-05-01T00:00:00Z
https://thekaiway.com/2008/05/01/rspec%E7%9A%84stroy%E6%A1%86%E6%9E%B6/
<p>Stroy框架是一个在应用程序级别对测试进行描述的框架。Rspec的BDD测试中其实缺少了一个像Rails传统测试中的集成测试(integration tests)那样的东西,Rspec::Strop就是为了补上这个部分而产生的。</p>
<p>Stroy格式:</p>
<p>Title (故事标题)</p>
<p>故事的描述:
作为一个怎样的角色
我想要什么样
最后得到怎样的结果</p>
<p>验证故事的标准: (以场景(Scenario)的形式提出)</p>
<p>Scenario: 场景标题
Given [场景内容]
And [场景附加内容]…
When [发生事件]
Then [产生的后果]
And [其它后果]…</p>
<p>Scenario: …</p>
<p>以上就是故事编写的基本结构,Given后可以加多个And,Then也是。故事直接保存为文本文件就可以了。然后开始编写故事的验证机制(其实就是针对上面描述的场景以代码来表示)。</p>
<p>故事验证格式:</p>
<p>steps_for(:故事名) do
Given("场景内容") do |占位符|
代码描述
end
When("发生事件") do |占位符|
代码描述
end
Then("产生的后果") do |占位符|
代码描述
end
end</p>
<p>解释下,Given块的个数应该与故事清单中的Given语句加后面的And的个数相当,Then块也是。</p>
<p>例子:
故事login</p>
<p>Story: 以存在的用户身份登录
作为一个未认证的用户
我想要登录到网站
那么我能看见我的账户信息</p>
<p>Scenario: 正常登录
Given 我的账户为test@example.org
When 我用邮箱为test@example.org和密码是foofoo进行登录
Then 我能够登录
And 我会看到账户页面</p>
<p>故事验证login.rb:</p>
<p>require 'rubygems'
require 'spec/story'
steps_for(:login) do
Given "我的账户为$email" do |email|
@user = find_or_create_user_by_email({:email => email,
:password => 'foofoo',
:password_confirmation => 'foofoo'})
end</p>
<p>When "我用邮箱为$email和密码是$password进行登录" do |email, password|
post '/login', :user => {:email => email, :password => password}
end</p>
<p>Then("我能够登录") do
end</p>
<p>Then("我会看到账户页面") do
end
end</p>
<p>with_steps_for(:login) do
run "login"
end</p>
<p>参考:</p>
<ul>
<li><a href="http://rspec.info/">http://rspec.info/</a></li>
<li><a href="http://blog.davidchelimsky.net/articles/2007/10/22/plain-text-stories-on-rails">http://blog.davidchelimsky.net/articles/2007/10/22/plain-text-stories-on-rails</a></li>
<li><a href="http://dannorth.net/whats-in-a-story">http://dannorth.net/whats-in-a-story</a></li>
<li><a href="http://www.letrails.cn/archives/rspec-story-tutorials">http://www.letrails.cn/archives/rspec-story-tutorials</a></li>
</ul>
txt2html Script
2008-04-28T00:00:00Z
https://thekaiway.com/2008/04/28/txt2html%E8%84%9A%E6%9C%AC/
<p>一个把Markdown或(和)Textile的文本转化为Html的脚本,使用RedCloth,简单轻便。。。</p>
<p>#!/usr/bin/ruby -w
# txt2html.rb
# Usege:
# run commands like: ruby txt2html.rb the_file_you_want_to_do_covert
require "rubygems"
require "redcloth"
filename = ARGV.first
contents = ''
open("#{filename}") { |f| contents = f.read }
open("#{filename[0, filename.length-4]}.html",'w') { |f| f<<RedCloth.new(contents).to_html }</p>
家庭式小作坊
2008-04-27T00:00:00Z
https://thekaiway.com/2008/04/27/%E8%A7%81%E8%AF%86%E4%BA%86%E5%AE%B6%E5%BA%AD%E5%B0%8F%E4%BD%9C%E5%9D%8A%E5%BC%8F%E7%94%9F%E4%BA%A7/
<p>今天去惠东考察一家鞋厂,见识了什么是家庭小作坊式生产。</p>
<!--more-->
<p>一个不大的厂房,两层,几十个工人。家庭式管理,中央集权式,客户下订,来料加工。样板管理,全部由师傅(款式设计师),样板用完很快就丢弃,不去分析。供应链无序,做生意靠信用,各方面的分工不细致,整个厂的权利和事务全部集中于老板一人,剩下的管理者基本都是老板的亲戚。老板基本认为电子商务没前途,强调鞋业生产的特殊性(他的说法没错)。</p>
<p>期间,厂长说“界面要做出一个架构出来”,接着说“就是要能够看到一些框框,然后可以输入东西。”我晕忽忽,“架构”他们是这样理解。</p>
<p>由于国人的生产劳动力依然廉价,生产自动化难以普及,由于规模较小导致管理集权,生产由客户来单驱动,缺乏市场分析,分工粒度较小。不知道多久之后中国的制造业才能实现信息化自动化。</p>
ZenTest分析
2008-04-20T00:00:00Z
https://thekaiway.com/2008/04/20/zentest10%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90/
<p>ZenTest是Ruby的一个确保测试覆盖率和促进TDD实行(Ensures test coverage and accelerates TDD)的代码生成框架,并带有其它的实用工具(autotest等等)。官方主页:http://www.zenspider.com/ZSS/Products/ZenTest/</p>
<p>这次我选了ZenTest的1.0版来研究,代码文件大小只有3.7K,144行。</p>
<!--more-->
<p>下面是代码:</p>
<p>#!/usr/local/bin/ruby -w -I.
VERSION = '1.0.0'</p>
<p>puts "# Created with ZenTest v. #{VERSION}"
$AUTOTESTER = true
# 重写at_exit方法,让该方法不做任何事情
module Kernel
alias :old_at_exit :at_exit
def at_exit()
# nothing to do...
end
end</p>
<p>require 'test/unit'
require 'test/unit/ui/console/testrunner'</p>
<p># 产生文件的副本
files = ARGV.clone</p>
<p># 保存测试类的hash( klass -> klassmethods)
test_klasses = {}
# 保存待测试类的hash( klass -> klassmethods)
klasses = {}
# 保存待测试类的方法的hash( klass -> klassmethods)
all_methods = {} # fallback</p>
<p># 迭代处理参数指定的每个文件
ARGV.each do |file|</p>
<p># 导入文件,失败则抛出异常,并输出错误信息
begin
require "#{file}"
rescue LoadError => err
puts "Couldn't load #{file}: #{err}"
next
end</p>
<p># 迭代处理文件的每一行
IO.foreach(file) do |line|
# 如果该行中包含类定义(class Xxx)则处理
if line =~ /^\s*class\s+(\S+)/ then
# 保存类名
klassname = $1
# 将类名转换为Symbol后再得到该类的类型值
klass = Module.const_get(klassname.intern)
# 如果该类为单元测试类
target = klassname =~ /^Test/ ? test_klasses : klasses</p>
<p># record public instance methods JUST in this class
# 保存类的公用实例方法到一个methond -> boolean的Hash中
# 并将该Hash保存到以该类为键值的target hash中
public_methods = klass.public_instance_methods
klassmethods = {}
public_methods.each do |meth|
klassmethods[meth] = true
end
target[klassname] = klassmethods</p>
<p># record ALL instance methods including superclasses (minus Object)
# 将该类中由Object类继承的实例方法除去后保存
the_methods = klass.instance_methods(true) - Object.instance_methods(true)
# 生成同上个代码块类似的hash
klassmethods = {}
the_methods.each do |meth|
klassmethods[meth] = true
end
all_methods[klassname] = klassmethods
end
end
end</p>
<p># 上面的迭代代码块运行完成后将记录下文件中所有类和其公共的实例方法</p>
<p>print "# "
p all_methods</p>
<p>missing_methods = {} # key = klassname, val = array of methods
# 在待测试类上进行迭代(以待测试类的名字)
klasses.each_key do |klassname|
# 生成测试类类名
testklassname = "Test#{klassname}"</p>
<p>if test_klasses[testklassname] then
methods = klasses[klassname]
testmethods = test_klasses[testklassname]</p>
<p># check that each method has a test method
# 检查每个方法是否有一个测试方法
klasses[klassname].each_key do | methodname |
testmethodname = "test_#{methodname}".gsub(/[]=/, "index_equals").gsub(/[]/, "index_accessor")
unless testmethods[testmethodname] then
puts "# ERROR method #{testklassname}##{testmethodname} does not exist (1)" if $VERBOSE
missing_methods[testklassname] ||= []
missing_methods[testklassname].push(testmethodname)
end
end
# check that each test method has a method
testmethods.each_key do | testmethodname |
if testmethodname =~ /^test_(.*)/ then
methodname = $1.gsub(/index_equals/, "[]=").gsub(/index_accessor/, "[]")
# try the current name
orig_name = methodname.dup
found = false
until methodname == "" or methods[methodname] or all_methods[klassname][methodname] do
# try the name minus an option (ie mut_opt1 -> mut)
if methodname.sub!(/<em>[^</em>]+$/, '') then
if methods[methodname] or all_methods[klassname][methodname] then
found = true
end
else
break # no more substitutions will take place
end
end</p>
<p>unless found or methods[methodname] or methodname == "initialize" then
puts "# ERROR method #{klassname}##{orig_name} does not exist (2)" if $VERBOSE
missing_methods[klassname] ||= []
missing_methods[klassname].push(orig_name)
end
else
unless testmethodname =~ /^util_/ then
puts "# WARNING Skipping #{testklassname}##{testmethodname}"
end
end
end
else
puts "# ERROR test class #{testklassname} does not exist" if $VERBOSE</p>
<p>missing_methods[testklassname] ||= []
klasses[klassname].keys.each do |meth|
missing_methods[testklassname].push("test_#{meth}")
end
end
end
# 真正地创建测试类,为没有测试的类添加方法。
missing_methods.keys.sort.each do |klass|
# 判断是否为测试类
testklass = klass =~ /^Test/</p>
<p>puts "class #{klass}" + (testklass ? " < Test::Unit::TestCase" : '')</p>
<p>methods = missing_methods[klass] | []
m = []
methods.sort.each do |method|
# 为测试类生成代码
if testklass then
s = " def #{method}\n assert(false, 'Need to write #{method} tests')\n end"
else
s = " def #{method}\n # TO" + "DO: write some code\n end"
end
m.push(s)
end</p>
<p>puts m.join("\n\n")
puts "end"
puts ""
end</p>
<p>整个程序的主要分为三个部分,第一个是导入文件并收集文件中的类的信息,第二部分是收集前一部分收集到的类的方法的信息进行处理,最后是生成测试类的代码。</p>
First Laptop
2008-04-14T00:00:00Z
https://thekaiway.com/2008/04/14/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E5%8F%B0%E6%9C%AC%E6%9C%AChp6520s/
<img src="http://lh3.ggpht.com/chenk85/SAK47kDaDbI/AAAAAAAAABQ/W32IL9wxtZI/20080414261.jpg?imgmax=576" alt="2601">
<p>买回来了本本HP6520s(KS260PA)。6850RMB(一本,一电,一鼠,一包,一清洁)+135RMB(1条KS的内存)。</p>
<!--more-->
<img src="http://lh4.ggpht.com/chenk85/SAK470DaDcI/AAAAAAAAABY/QFcMY8AxAKc/20080414262.jpg?imgmax=576" alt="2602">
<img src="http://lh4.ggpht.com/chenk85/SAK470DaDdI/AAAAAAAAABg/2jmA1rOWH2c/20080414263.jpg?imgmax=576" alt="2603">
<p>买本本的经历比较简单,一开始去本地HP代理问过一下价格,然后就叫他拿货,之后就去到验机(看序列号是否一致),测试(用Ubuntu的Live CD),带回来。</p>
<p>在中关村的资料:http://detail.zol.com.cn/notebook/index139838.shtml</p>
<p>配置上的确是不错的,迅驰4.5的T8100,加了1G的内存,升到2G,加上是800的前端,速度果然是飞快。发热也不厉害,电池能顶3个小时左右。唯一不满的地方就是屏幕,显示效果有点差,好像看网上的评价,基本都是说显示效果差。</p>
<p>[chenkai@localhost ~]$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Duo CPU T8100 @ 2.10GHz
stepping : 6
cpu MHz : 800.000
cache size : 3072 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 10
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr sse4_1 lahf_lm ida
bogomips : 4194.59
clflush size : 64
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Duo CPU T8100 @ 2.10GHz
stepping : 6
cpu MHz : 800.000
cache size : 3072 KB
physical id : 0
siblings : 2
core id : 1
cpu cores : 2
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 10
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr sse4_1 lahf_lm ida
bogomips : 4191.10
clflush size : 64</p>
<p>CPU是最强的部分:)</p>
<p>买回来后就装上了Arch Linux,详见我另一日志。</p>
Install ArchLinux on HP6520s
2008-04-14T00:00:00Z
https://thekaiway.com/2008/04/14/hp6520s%E4%B8%8Earchlinux/
<p>安装Arch的方法很简单,但是记得在分区时要把swap设置到大于等于内存容量,才能休眠。一开始没有设好swap,结果怎么也休眠不了。</p>
<p>由于本本的特殊性要安装下列软件包:</p>
<ul>
<li>apci,apcid - 高级电源支持</li>
<li>dus hal udev - 自动挂载</li>
<li>pmtools - 休眠</li>
<li>powertop - 本本节能工具</li>
<li>hotkey-setup laptop-mode-tools - 笔记本支持工具</li>
<li>cpudyn cpufreqd cpufrequtils CPU支持</li>
<li>iwlwifi-3945-ucode - 无线网卡驱动</li>
</ul>
<p>参考</p>
<ul>
<li>http://wiki.archlinux.org/index.php/Laptop</li>
<li>http://wiki.archlinux.org/index.php/Pm-utils</li>
<li>http://wiki.archlinux.org/index.php/Cpufrequtils</li>
<li>http://wiki.archlinux.org/index.php/Networkmanager</li>
</ul>
<p>安装并设置了这些后就能很好的支持到笔记本的各种特性,如休眠,Fn键,自动降频,电池电量显示,无线等。</p>
Push Server
2008-03-14T00:00:00Z
https://thekaiway.com/2008/03/14/server-push/
<p>这几天在作Web聊天室的实现,看到了Server Push的概念,找到两篇国人的文章。</p>
<ul>
<li><a href="http://lightyror.thegiive.net/2007/06/comet-push-server.html">http://lightyror.thegiive.net/2007/06/comet-push-server.html</a></li>
<li><a href="http://www.javaeye.com/topic/19089">http://www.javaeye.com/topic/19089</a></li>
</ul>
autotest in Gnome"
2008-03-13T00:00:00Z
https://thekaiway.com/2008/03/13/autotest-notify-in-gnome/
<p>autotest是个方便的测试工具,ZenTest的组件之一,对Rspec支持很好。</p>
<p>在Peedcode的教学视频中很多时候看到作者用autotest时,测试结果会以桌面系统Notify的形式通知用户,每次看到都觉得好羡慕。其实在Gnome环境下的同学不用羡慕,因为在Gnome下也可以,把你<strong>Home目录下的.autotest</strong>文件(附件有),加入以下代码,并把附件中的图片文件放到Home下(把<strong>dot改为.</strong>)。<!--more--></p>
<p>require 'rnotify'
require 'gtk2'</p>
<p>module Autotest::RNotify
class Notification
attr_accessor :verbose, :image_root, :tray_icon, :notification,
:image_pass, :image_pending, :image_fail</p>
<p>def initialize(timeout = 5000,
image_root = "#{ENV['HOME']}/.autotest_images" ,
verbose = false)
self.verbose = verbose
self.image_root = image_root</p>
<p>puts 'Autotest Hook: loading Notify' if verbose
Notify.init('Autotest') || raise('Failed to initialize Notify')</p>
<p>puts 'Autotest Hook: initializing tray icon' if verbose
self.tray_icon = Gtk::StatusIcon.new
tray_icon.icon_name = 'face-monkey'
tray_icon.tooltip = 'RSpec Autotest'</p>
<p>puts 'Autotest Hook: Creating Notifier' if verbose
self.notification = Notify::Notification.new('X', nil, nil, tray_icon)
notification.timeout = timeout</p>
<p>Thread.new { Gtk.main }
sleep 1
tray_icon.embedded? || raise('Failed to set up tray icon')</p>
<p>def notify(icon, tray, title, message)
notification.update(title, message, nil)
notification.pixbuf_icon = icon
tray_icon.tooltip = "Last Result: #{message}"
tray_icon.icon_name = tray
notification.show
end</p>
<p>def passed(title, message)
self.image_pass ||= Gdk::Pixbuf.new("#{image_root}/pass.png", 48,48)
notify(image_pass, 'face-smile', title, message)
end</p>
<p>def pending(title, message)
self.image_pending ||= Gdk::Pixbuf.new("#{image_root}/pending.png",48,48)
notify(image_pending, 'face-plain', title, message)
end</p>
<p>def failed(title, message)
self.image_fail ||= Gdk::Pixbuf.new("#{image_root}/fail.png", 48,48)
notify(image_fail, 'face-sad', title, message)
end</p>
<p>def quit
puts 'Autotest Hook: Shutting Down...' if verbose
#Notify.uninit
Gtk.main_quit
end
end
Autotest.add_hook :initialize do |at|
@notify = Notification.new
end
Autotest.add_hook :ran_command do |at|
results = at.results.last
puts "the results is #{results}"
unless results.nil?
output = results[/(\d+)\s+example?,\s*(\d+)\s+failures?(,\s*(\d+)\s+peding)?/]
if $~[2].to_i > 0
@notify.failed("Tests Failed", output)
elsif $~[4].to_i > 0
@notify.pending("Tests Pending", output)
else
unless at.tainted
@notify.passed("All Tests Passed", output)
else
@notify.passed("Tests Passed", output)
end
end
end
end</p>
<p>Autotest.add_hook :quit do |at|
@notify.quit
end
end</p>
<p>ohoh~~我们来看看效果怎样,在任意一个加入rspec on rails插件的Project下(当然是写了spec的)执行autotest:
<a href="http://redworld.blog.ubuntu.org.cn/2008/03/13/autotest-notify-in-gnome/notifypng/" rel="attachment wp-att-67" title="notify.png"><img src="http://redworld.blog.ubuntu.org.cn/files/2008/03/notify.png" alt="notify.png"></a></p>
<p>是不是很有趣呢?当然这个依赖于库ruby-gnome2,ruby-libnotify,都是用各自Linux发行班的包管理软件可以轻松得到的,比如我的arch就是,yaourt ruby-gnome2 ruby-libnotify。这个其实也可以用在unit test下,不过需要修改Autotest.add_hook :ran_command中的正则。</p>
<p>Ruby一个可爱的地方就是配置文件也是代码,代码还是代码。autotest的扩展性真好,这也是得益于Ruby的哲学,要修改一个类的行为就打开它,定义它,很优雅。Code is the best firend of Programmer 。</p>
<p>参考资料:
http://www.ikhono.net/2007/12/16/gnome-autotest-notifications</p>
<p><a href="http://redworld.blog.ubuntu.org.cn/2008/03/13/autotest-notify-in-gnome/dot_autotest_imageszip/" rel="attachment wp-att-66" title="dot_autotest_images.zip">dot_autotest_images.zip</a></p>
After Eager Loading
2008-03-13T00:00:00Z
https://thekaiway.com/2008/03/13/after-eager-loading/
<p>Eager Loading是ActiveRecord的一个数据查询的优化措施,在查询model的时候连同它的关联对象都全查询出来(通过一条包含LEFT OUTER JOIN的SQL语句)。但是在你使用了Eager Loading后,如果还是用一些会触发数据库本身的统计函数的查询方法(如count,average等)的话,那前面的Eager Loading就白费了。下面给出例子:<!--more--></p>
<p>两个Model,Project和Task,关系是Project has_many Task。</p>
<p>在ProjectController的show中:</p>
<p>@project = Project.find(params[:id], :include => [:articles, :tasks])</p>
<p>这是project/show页面:</p>
<p><h1><%=h @project.title %></h1> <h2><%=h @project.description %></h2> <p>统计信息:<br/>共有<%= @project.tasks.count %>个<%=link_to '任务', project_tasks_path(@project)%>, 已完成<%= @project.completed_tasks.length %>个任务。 <hr/> <h3>最新未完成的5个任务</h3> <table> <tr> <th width="25%">任务名</th> <th width="75%">任务摘要</th> </tr> <% for task in @project.tasks.incompleted %> <tr> <td><%= link_to task.name, project_task_path(@project,task) %></td> <td><%= task.content %></td> </tr> <% end %> </table></p>
<p>来看看log吧:</p>
<p>Project Load Including Associations (0.000595) SELECT <code>projects</code>.<code>id</code> AS t0_r0, <code>projects</code>.<code>title</code> AS t0_r1, <code>projects</code>.<code>description</code> AS t0_r2, <code>projects</code>.<code>created_at</code> AS t0_r3, <code>projects</code>.<code>updated_at</code> AS t0_r4, <code>articles</code>.<code>id</code> AS t1_r0, <code>articles</code>.<code>subject</code> AS t1_r1, <code>articles</code>.<code>summary</code> AS t1_r2, <code>articles</code>.<code>content</code> AS t1_r3, <code>articles</code>.<code>project_id</code> AS t1_r4, <code>articles</code>.<code>created_at</code> AS t1_r5, <code>articles</code>.<code>updated_at</code> AS t1_r6, <code>articles</code>.<code>lock_version</code> AS t1_r7, <code>tasks</code>.<code>id</code> AS t2_r0, <code>tasks</code>.<code>name</code> AS t2_r1, <code>tasks</code>.<code>content</code> AS t2_r2, <code>tasks</code>.<code>completed</code> AS t2_r3, <code>tasks</code>.<code>project_id</code> AS t2_r4, <code>tasks</code>.<code>created_at</code> AS t2_r5, <code>tasks</code>.<code>updated_at</code> AS t2_r6 FROM <code>projects</code> LEFT OUTER JOIN <code>articles</code> ON articles.project_id = projects.id LEFT OUTER JOIN <code>tasks</code> ON tasks.project_id = projects.id WHERE (<code>projects</code>.<code>id</code> = 2)
Rendering template within layouts/application
Rendering projects/show
SQL (0.000255) SELECT count(*) AS count_all FROM <code>tasks</code> WHERE (tasks.project_id = 2)</p>
<p>可以看到在Project的Eager Loading的查询之后可以看到又有一个count()的select查询进行了。那前面的Eager Loading的效果似乎没有了。再试试把count方法调用改为调用length方法,这时就看不到另外的那个count()查询了。</p>
<p>这个可能在写代码的时候不会太留意,不过这对性能的影响还是有些的,如果Eager Loading后不会在调用统计函数就ok了。</p>
通过了英语4级考试
2008-03-06T00:00:00Z
https://thekaiway.com/2008/03/06/%E5%9B%9B%E7%BA%A7%E9%80%9A%E8%BF%87/
<p>四级在第二次考试通过了。没什么感觉。不过查询成绩那个网站的js有些问题,由于它是用js检查准考证号,对于FF来说和其他的(Opera和IE)数组下标计算方法不同,FF下总是会提示准考证号长度错误,由于它是发起Ajax调用进行查询的所以不能强制提交,最多绕开浏览器手动用curl提交。。。</p>
Rails 2.0: Nested Resource
2008-03-02T00:00:00Z
https://thekaiway.com/2008/03/02/rails-2-0-nesting-resource/
<p>Rails2.0中提供了一套更好的嵌套资源的处理方法(更加形象化)。</p>
<p>比如有两个model:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># app/model/project.rb</span>
<span class="token keyword">class</span> <span class="token class-name">Project</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base
has_many <span class="token symbol">:tasks</span>
<span class="token keyword">end</span>
<span class="token comment"># app/model/task.rb</span>
<span class="token keyword">class</span> <span class="token class-name">Task</span> <span class="token operator"><</span> ActiveRecord<span class="token double-colon punctuation">::</span>Base
belongs_to <span class="token symbol">:project</span>
<span class="token keyword">end</span></code></pre>
<p>在migrate中可以用referece类型来表示引用外键,比如在创建task的migrate中</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">create_table <span class="token symbol">:tasks</span> <span class="token keyword">do</span> <span class="token operator">|</span>t<span class="token operator">|</span>
<span class="token comment">#...</span>
t<span class="token punctuation">.</span>references <span class="token symbol">:project</span>
<span class="token comment">#...</span>
<span class="token keyword">end</span></code></pre>
<p>路由规则定义如下:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token comment"># config/routes.rb</span>
ActionController<span class="token double-colon punctuation">::</span>Routing<span class="token double-colon punctuation">::</span>Routes<span class="token punctuation">.</span>draw <span class="token keyword">do</span> <span class="token operator">|</span>map<span class="token operator">|</span>
map<span class="token punctuation">.</span>resources <span class="token symbol">:projects</span> <span class="token keyword">do</span> <span class="token operator">|</span>project<span class="token operator">|</span>
project<span class="token punctuation">.</span>resources <span class="token symbol">:tasks</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>比较好的一个方法调用Task所属Project的方法是用before_filter调用一个方法来设置:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">ArticlesController</span> <span class="token operator">&</span>lt<span class="token punctuation">;</span> ApplicationController
before_filter <span class="token symbol">:load_project</span>
<span class="token comment">#...</span>
<span class="token keyword">def</span> <span class="token method-definition"><span class="token function">load_project</span></span>
<span class="token variable">@project</span> <span class="token operator">=</span> Project<span class="token punctuation">.</span>find params<span class="token punctuation">[</span><span class="token symbol">:project_id</span><span class="token punctuation">]</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>把Controller中的涉及内嵌资源的find和new的调用改一下:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token variable">@project</span><span class="token punctuation">.</span>tasks<span class="token punctuation">.</span>find <span class="token comment">#=> 代替原来的Tasks.find</span>
<span class="token variable">@projece</span><span class="token punctuation">.</span>tasks<span class="token punctuation">.</span>build <span class="token comment">#=> 代替原来的Tasks.new</span></code></pre>
<p>还有把Controller中的redirect_to方法的参数修改一下:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">redirect_to<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token variable">@project</span><span class="token punctuation">,</span> <span class="token variable">@task</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p>那在Task的view中应该做如下处理</p>
<p>在生成表单时,传入一个数组给form_for方法:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby">form_for<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token variable">@project</span><span class="token punctuation">,</span> <span class="token variable">@task</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p>调用TasksController的new方法:</p>
<p>new_project_task_path
# => domain/projects/:id/tasks/new</p>
<p>调用TasksController的show方法:</p>
<p>link_to 'Show', [@project, @task]
# => domain/projects/:id/tasks/:id</p>
<p>调用TasksController的edit方法:</p>
<p>link_to 'Edit' edit_project_task_path
# => domain/projects/:id/tasks/:id/edit</p>
<p>或者</p>
<p>link_to 'Delete', [:edit, @project, @task]
# => domain/projects/:id/tasks/:id/edit</p>
<p>调用TasksController的destory方法:</p>
<p>link_to 'Delete', [@project, @task], :confirm => 'Are you sure?', :method => :delete
# => domain/projects/:id/tasks/:id</p>
<p>通过这些方法就能让内嵌资源使用时更加优雅,而不用在导向时指定controller和action,再传入父资源和子资源的id。这些xx_path方法会帮你查找routes.rb的路由定义,然后生成url,当然这一切都是基于Rails2.0提倡的RESTful。</p>
<p>参考文章:
<a href="http://www.akitaonrails.com/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial">http://www.akitaonrails.com/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial</a></p>
大三的课程
2008-02-27T00:00:00Z
https://thekaiway.com/2008/02/27/%E8%AF%BE%E7%A8%8B/
<p>这个学期的有几门比较贴近实际的课程(有些废话,因为大三了嘛),比如数据挖掘和数据仓库,分布式系统,和服务器配置。</p>
<!--more-->
<p>数据挖掘和数据仓库就是把一些现有的数据进行整合,然后抽取到数据仓库中,之后在作一些分析和报告的技术。其中在前后两个阶段都是难点,都是涉及了领域知识,中间做数据仓库相对简单。第一个阶段就是要对现有数据库中的数据作整合,这整合因为数据不同同步,或数据语义有差别,所以需要对领域知识有一个比较好的理解,然后在输出成元数据。最后一个阶段就是按照需求来作数据分析了,这个也是需要结合领域知识。这个就是大概了吧。</p>
<p>老师讲课讲得不错,很清楚的展示上面讲的东西。其实我很多东西有兴趣,只是限于时间不够精力不够不能学那么多,这个数据仓库也只是了解一下就OK了。</p>
<p>讲到数据库,看了豆瓣的开发者http://www.dbanotes.net/的blog,感觉现在由于长尾,小型数据库也开始受到重视,比如MySQL,风光地被SUN收购了。另,BS Ms的心理又来了:不喜欢Ms的SQL Server,好像学校的老师都是拿这个来作实验和讲课。</p>
Web前端
2008-02-24T00:00:00Z
https://thekaiway.com/2008/02/24/web%E5%89%8D%E7%AB%AF%E6%B5%85%E8%A7%81/
<p>Web前端最终传输到用户浏览器的也就是HTML页面,Css文件,JavaScript文件,图像文件。</p>
<p>HTML页面负责的是基本内容和一点点表现逻辑;Css文件负责页面布局和页面里每个元素的样式;JavaScript文件负责的是页面的一些动态表现逻辑。这好像软件架构中的分层一样,把不同职责的部分分开来。</p>
<p>而在传送到客户浏览器之前,这些Html都还是各种脚本,比如Ruby的就是erb,Java的就是Jsp。都先用编程语言来描述页面的基本内容,一般其中充满了if/else/while(用来对Model中属性的各种输出)。对于Rails来说,最直接就是用Ruby来描述这些,Java呢,很喜欢搞个标签库这种东西,说是方便美工,其实也使页面逻辑更难看懂。</p>
<p>JavaScript负责的动态表现逻辑,一般叫Ajax,用的好的话可以获得用户体验,最近看到China-pub的Ajax就做得不错。Css也是Ajax的一个重要部分,那些什么颜色变化都是通过js来修改页面元素的样式实现的,现在web2.0的观感,也是由Css实现的。像FF的插件Greasemonkey就能用js来修改页面的观感。</p>
<p>好像前端就是这些。</p>
Migrate to ArchLinux
2008-02-22T00:00:00Z
https://thekaiway.com/2008/02/22/%E5%BE%88%E5%A5%BD%E5%BE%88%E5%BF%AB%E9%80%9F%E7%9A%84arch/
<p>花了两天的时间才能安装配置好Arch的桌面,虽然很久但是觉得挺值得的。<!--more--></p>
<p>进gnome桌面的速度很快,一闪就初始化好了面板,很好很强大,其他的软件感觉也快了,不知是不是心理作用。不过还剩下自动挂载U盘没有解决,还有gvim的配色混乱。</p>
<p>作下笔记:</p>
<p>关于MySQL的:
Arch的MySQL 5.0.51-3软件包中有bug,安装后须先修改/usr/share/mysql/mysql_system_tables_data.sql,找到这行:</p>
<p>INSERT INTO tmp_user (host,user) VALUES (@@hostname,);</p>
<p>将INSERT改为REPLACE后保存,再运行初始化脚本。</p>
<p>然后再设置编码,在/etc/my.conf加上:</p>
<p>a. [client]
default-character-set = utf8</p>
<p>b. [mysqld]
default-character-set = utf8
init-connect = ‘SET NAMES UTF8′</p>
<p>之后执行/etc/rc.d/mysqld start启动,再运行mysql_secure_installation 设置一下。</p>
<p>今年的Linux课程,老师竟然选了《鸟哥的私房菜》,不错。</p>
寒假结束
2008-02-20T00:00:00Z
https://thekaiway.com/2008/02/20/%E5%AF%92%E5%81%87%E7%BB%93%E6%9D%9F/
<p>回到学校,一个月的寒假就这样结束了。
放假做了什么?放假的计划完成了吗?放假有没有作些有意义的事?<!--more--></p>
<p>放假看完了励志经典《高效能人士的七个习惯》,收获不少。很多书都是看到一半,比如《JavaScript高级程序设计》,《Ajax基础教程》,《RESTful web server》,《编写有效用例》。主要时间还是在研究wiki,blog,BBS这些web应用的特征。完成了那个翻译的发布站,感觉上好像一个没什么实际作用的应用,只是把文章分为翻译的状态不同(),再加上一个版本控制,而且都还没有富客户端编辑器。好像很失败,回到学校后自己看了看删了。不过自己做了一个简单的查询单词(用金山词霸在线的词典),和一个小小的BBS,只是有简单的发帖,回复功能。感觉要做到产品级的话就要能做出像基于PHP的Wordpress那样管理功能强大,又说到这个真的很想试用下google的blogger。剩下的时间都是在搞CSS,现在能自己弄出一个类似Ruby on Rails主页的那个界面,能弄出有些web2.0风格的界面。</p>
<p>本来放假里准备读英语和做做一些运动的,可是因为一开始就感冒了一个多星期,这些东西也就进行了两三天,到了感冒好了之后又没有继续。行动力真是差。后来回家的习惯又是发作,每天都睡到中午才起床,几次还睡了一整天,在学校时都是一点睡七点起床的标准作息。</p>
<p>职业规划在这个假期也完成了大部分,自己喜欢做什么,能做什么,希望做什么都是统一的。而在这条道路上,要怎么做,在什么时间要做什么。这些都大概有了计划。也看了一些公司的资料,有在找自己希望就职的公司。</p>
<p>这个放假买了*nway的洗洁精,回家洗碗。还有换了家里洗手盆的进排水管。还算是做了些有贡献的事:)</p>
<p>好像放假很多时间都是在睡觉,很多时间都浪费了。</p>
Pacman in ArchLinux
2008-02-14T00:00:00Z
https://thekaiway.com/2008/02/14/usepacman/
<p>pacman是什么,就是和apt-get之于Ubuntu一样,pacman就是Arch的apt-get。</p>
<!--more-->
<p>熟悉一下几个pacman的几个常用命令
<code>pacman -S packagename #安装软件包
pacman -R packagename #删除软件包
pacman -Syu #升级系统中的所有包
pacman -Ss package #查询软件包
pacman -Qs package #查询已安装的包
pacman -Sw package #下载但不安装包
pacman -U /path/package.pkg.tar.gz #安装本地包
pacman -Scc #清理包缓存
pacman -Sf pacman #重新安装包</code></p>
<p>其实要容易记的话可以自己在bashrc里配置一下alias就好。</p>
<p>再说说包下载的提速。。。
修改一下/etc/pacman.conf,把下面这句的注释去掉:</p>
<p>XferCommand = /usr/bin/wget -c --passive-ftp -c %u</p>
<p>这样就可以使用wget来下载包。</p>
<p>也可以用aria2,在配置文件中加上这句:</p>
<p>XferCommand = /usr/bin/aria2c -s 4 -m 2 -d / -o %o %u</p>
<p>-s后面是连接的服务器数量,-m是线程数。</p>
<p>wiki中提供了<a href="http://wiki.archlinux.org/index.php/Improve_Pacman_Performance">另一个脚本</a>,是用aria2下载的。</p>
<p>在wiki中还<a href="http://wiki.archlinux.org/index.php/Colored_Pacman_output">提供了几个包查询彩色输出的脚本</a>。</p>
<p>pacman也有GUI前端,不过我还没有见过用过,过几天回学校就会用上的。</p>
<blockquote>
<p>Java interfaces:</p>
<ul>
<li>jacman A nice Java-based interface.</li>
</ul>
<p>GTK2/Gnome interfaces:</p>
<ul>
<li>gtkpacman Pygtk gui to ArchLinux pacman. An -svn version is available as well (gtkpacman-svn).</li>
<li>alunn Tray notifications of new updates and news from Arch front page.</li>
<li>guzuta Yet another PyGTK frontend.</li>
<li>pacmon-svn Tray applet that notifies the user of available pacman updates.</li>
</ul>
<p>KDE/Qt interfaces:</p>
<ul>
<li>pacmanager-svn Qt4 package manager based on pacman.</li>
<li>kpacupdate Pacman update notification tool for the KDE system tray.</li>
</ul>
</blockquote>
<p>之后就慢慢研究一下启动加载的模块,然后在编译一下内核,Arch好好玩。。。</p>
一日一D(1)
2008-02-10T00:00:00Z
https://thekaiway.com/2008/02/10/%E4%B8%80%E6%97%A5%E4%B8%80D(1)/
<p>通过D语言的项目主页dsource.org上的基础教程,来学习D语言吧。。。</p>
<p>D语言已经到了2.0,DMD也到了2.0a,不过很多项目还是只支持到DMD1.0。不过也可以用另一个编译器GNU的DGC。D语言有两个标准库,默认C风格的Phobos和C++风格的Tango。更多的项目都是基于Tango开发的,一些基于Phobos的项目也开始向着Tango转移。从这里可以看出D语言的主要受众是原C/C++的用户。</p>
<p>这次有三个示例代码,三个示例:
第一个是Helloworld</p>
<p>import std.stdio;
void main() {
nbspwritef("Hello");
}</p>
<p>可以看到这里用的是<a href="http://www.digitalmars.com/d/2.0/phobos/std_stdio.html">std.stdio</a>中的输出方法writef(),一个很类似C的printf的方法,可以对输出进行格式化。</p>
<p>第二个示例展示的是代码的赋值,传统的类C式风格。</p>
<p>import std.stdio;
int magicNumber = 42;
char[6u] password = "sesame";
void main() {
writefln("Magic number: ", magicNumber);
writefln("Password: ", password);
}</p>
<p>这里要注意的是由于D语言是原生支持utf8的,所以在数组中可以使用nu这样的数量表示。</p>
<p>第三个例子是展示D语言的数据类型。</p>
<p>import std.stdio;
int main() {
// Show information about integer types...
writefln("bool\tmin: %d\tmax: %d (%d)", bool.min, bool.max, bool.sizeof);
writefln("ubyte\tmin: %d\tmax: %d (%d)", ubyte.min, ubyte.max, ubyte.sizeof);
writefln("ushort\tmin: %d\tmax: %d (%d)", ushort.min, ushort.max, ushort.sizeof);
writefln("uint\tmin: %d\tmax: %d (%d)", uint.min, uint.max, uint.sizeof);
writefln("ulong\tmin: %d\tmax: %d (%d)\n", ulong.min, ulong.max, ulong.sizeof);
writefln("byte\tmin: %d\tmax: %d (%d)", byte.min, byte.max, byte.sizeof);
writefln("short\tmin: %d\tmax: %d (%d)", short.min, short.max, short.sizeof);
writefln("int\tmin: %d\tmax: %d (%d)", int.min, int.max, int.sizeof);
writefln("long\tmin: %d\tmax: %d (%d)\n", long.min, long.max, long.sizeof);</p>
<p>// Show information about floating-point types...
writef("float (%d)\tdouble (%d)\treal (%d)\t", float.sizeof, double.sizeof, real.sizeof);
writef("ifloat (%d)\tidouble (%d)\tireal (%d)\t", ifloat.sizeof, idouble.sizeof, ireal.sizeof);
writef("cfloat (%d)\tcdouble (%d)\tcreal (%d)\t", cfloat.sizeof, cdouble.sizeof, creal.sizeof);</p>
<p>// Show information about character types...
writef("char (%d)\twchar (%d)\tdchar (%d)\t", char.sizeof, wchar.sizeof, dchar.sizeof);
return 0;
}</p>
<p>这里就看到了std.stdio.writefln()方法的格式化功能。每种数据类型都有min和max,sizeof属性,这就是D语言的特性之一的原生属性支持(现代面向对象思想)。其中,浮点数里以i开头的表示复数中的虚部,以c开头就是复数,没有前缀的就是实数,而float,double,real的精度逐渐递减,c开头的浮点数类型的长度是无前缀的和以i开头的浮点数的长度之和。字符中,无前缀的char是utf8编码的,8位,w开头的wchar是utf16的,16位,d开头的dchar是utf32,32位。具体的D语言的数据类型可以参看<a href="http://www.digitalmars.com/d/2.0/type.html">D语言规范中关于数据类型的部分</a>。</p>
<p>可以看到D语言的语句还是比较面熟的,是最流行的C风格。在之后的“一日一DD”中会继续看代码学D语言的。</p>
<p>代码来自<a href="http://www.dsource.org/projects/tutorials/wiki/TutorialFundamentals">TutorialFundamentals</a></p>
2008同学会
2008-02-08T00:00:00Z
https://thekaiway.com/2008/02/08/%E8%BF%87%E5%B9%B4%E5%90%8C%E5%AD%A6%E4%BC%9A/
<p>今晚去和高中同学聚了一下。许多同学都是出来工作了,我因为复读一年,本科四年,和当时读高中的同学就有了两年的差距。<!--more--></p>
<p>发现有些女生已经嫁了,有些还有孩子了。最近要结婚的一个同学,嫁了一个月收入5W以上的人,这让我什么都说不出。。。貌似现在做销售的就是社会的高薪人士,有些不服气,这些在广州深圳收入过W的人,现在买楼买房,很轻松,哪里想刚刚毕业的学生,几千块的月薪还要扣20%的税,扣完之后你还能剩多少?女生就是嫁得好就好了,有些刚毕业就有人娶的基本不用奋斗了,所以有人说女生要成功直接嫁给Gates就好了。</p>
<p>我呢,还是在花父母的钱的学生仔,去实习,去工作之还要再花父母多少钱,还要奋斗几年才能娶女朋友,还有要奋斗几年才能实现自己的梦想,感觉自己的梦想好像幻想。。。</p>
<p>不行,我要努力。。。要给老婆幸福,要给父母幸福,要实现自己的梦想。。。</p>
<p>第一要努力,第二还是要努力,第三还是要努力。。。</p>
<p>drive2me大哥的意见</p>
<blockquote>
<p>享受努力的过程是一大幸福。有些人走捷径就没有这种乐趣。
本来人生就是来享受过程的。对吧。</p>
</blockquote>
<blockquote>
<p>没有什么可以比的,走自己的路,对自己负责,就好了。</p>
</blockquote>
<blockquote>
<p>有些幸福的永恒的,而有些是短暂的,把握自己的人才能把握自己的幸福。依靠别人幸福的人,不会永久幸福的。</p>
</blockquote>
完全用Linux工作
2008-02-03T00:00:00Z
https://thekaiway.com/2008/02/03/%E7%BF%BB%E7%89%88%E5%AE%8C%E5%85%A8%E4%BD%BF%E7%94%A8linux%E5%B7%A5%E4%BD%9C/
<p>此文抄袭兼模仿http://www.cqacmm.com/myweb/teach/workinlinux.htm</p>
<p>下午闲着无事,又跑到Ubuntu论坛的吵架版看猴子戏,并且自己也过了一回当猴子的瘾。。。</p>
<p>用Ubuntu下半年了,感觉回到win下有种别扭的感觉。有个喜欢重装suse的同学问我,你平时用Linux做什么?我那时愣了一下,想想回答说,上网。。。</p>
<!--more-->
<p>在12月底时就把硬盘中win在玩了一次实况之后删了,因为又用不到win了所以删了。现在在VBox里开win也是上网银或者改doc(在永中里编辑后看看有没有问题)。</p>
<p>最近才看了LFS和Gentoo的文档,知道了它们是怎么安装的,好像都是满麻烦的,把系统里每个部分,包括基本的内核,常用的工具,每个核心的库都自己编译出来。虽然麻烦但是真正的自定义Linux,真正的是玩Linux。有些人说自己整出一个LFS可以用一辈子。。。</p>
<p>现在开始有空的时候就用Ubuntu的apt-get source来自己编译软件和每个库,一个一个编译构建包,试试感觉,等以后换个Gentoo做准备。</p>
<p>其实用什么OS,自己喜欢就好。我想用Linux就用嘛。</p>
<p>我就是喜欢用Linux,因为编码是utf8,因为有多个实用Unix工具,因为写代码的时候完全用鼠标,因为平时WM操作的时候可以尽量不用鼠标,因为不会中病毒,因为不用时不时就清理系统,因为不用重装系统,因为在不同的电脑上只要把硬盘插上就可以用。。。</p>
用Vim做Ruby开发
2008-02-03T00:00:00Z
https://thekaiway.com/2008/02/03/%E5%BE%88%E5%A5%BD%E5%BE%88%E5%BC%BA%E5%A4%A7%E7%9A%84vim4ruby/
<p>最新的配置文件在<a href="http://github.com/kaichen/dotfiles">http://github.com/kaichen/dotfiles</a></p>
<p>今天终于找到了最后一个插件,Ruby Snippets,终于打造出自己觉得最强Ruby开发利器的Vim,写下来记下来。。。</p>
<p>Ruby Snippets的演示:
<a href="http://eustaquiorangel.com/blog/show/438">http://eustaquiorangel.com/blog/show/438</a></p>
<p>Rails.vim的演示:
<a href="http://www.youtube.com/watch?v=30P8DSNOZuU&amp;mode=related&amp;search=rcov%20rails%20testing%20ruby">http://www.youtube.com/watch?v=30P8DSNOZuU&amp;mode=related&amp;search=rcov rails testing ruby</a></p>
<p>我的插件列表:</p>
<p>vim-ruby gem install vim-ruby --remote && vim-ruby-install.rb
rails.vim
<a href="http://www.vim.org/scripts/script.php?script_id=1567">http://www.vim.org/scripts/script.php?script_id=1567</a>
rubysnippets
<a href="http://www.vim.org/scripts/script.php?script_id=1966">http://www.vim.org/scripts/script.php?script_id=1966</a>
project
<a href="http://www.vim.org/scripts/script.php?script_id=69">http://www.vim.org/scripts/script.php?script_id=69</a>
NERD_tree
<a href="http://www.vim.org/scripts/script.php?script_id=1658">http://www.vim.org/scripts/script.php?script_id=1658</a>
bufexplorer
<a href="http://www.vim.org/scripts/script.php?script_id=42">http://www.vim.org/scripts/script.php?script_id=42</a>
taglist
<a href="http://www.vim.org/scripts/script.php?script_id=273">http://www.vim.org/scripts/script.php?script_id=273</a>
winmanager
<a href="http://www.vim.org/scripts/script.php?script_id=95">http://www.vim.org/scripts/script.php?script_id=95</a>
matchit
<a href="http://www.vim.org/scripts/script.php?script_id=39">http://www.vim.org/scripts/script.php?script_id=39</a>
svncommand
<a href="http://www.vim.org/scripts/script.php?script_id=922">http://www.vim.org/scripts/script.php?script_id=922</a>
surround
<a href="http://www.vim.org/scripts/script.php?script_id=1697">http://www.vim.org/scripts/script.php?script_id=1697</a>
supertab
<a href="http://www.vim.org/scripts/script.php?script_id=1643">http://www.vim.org/scripts/script.php?script_id=1643</a></p>
<p>加上上面的插件后,VIM就很好很强大,有rails.vim(目前支持Rails2.0)的跳转和快捷的rake执行等等的优点不用多说了,而vim- ruby则提供了高亮和代码补全等,今天找到的Ruby Snippets就实现了类似Textmate的模板补全(让我眼红了很久的功能)。nerd-tree和bufexplorer,winmanager 组成了一个代码浏览文件浏览的强大功能,svncommand和surround分别提供了svn支持和标签补全。。。</p>
<p>这样的VIM真是很好很强大。。。</p>
<p>上图:</p>
<p><img src="http://ruby-lang.org.cn/forums/attachments/month_0802/20080203_215faadbbaa0aa3e0d92RmplWRMwwMsQ.png" alt="image"></p>
<p>参考的资料:</p>
<ul>
<li><a href="http://wiki.rubyonrails.org/rails/pages/HowtoUseVimWithRails">http://wiki.rubyonrails.org/rails/pages/HowtoUseVimWithRails</a></li>
<li><a href="http://www.upulife.com/leo/?p=191">http://www.upulife.com/leo/?p=191</a></li>
<li><a href="http://www.upulife.com/leo/?p=196">http://www.upulife.com/leo/?p=196</a></li>
<li><a href="http://blog.csdn.net/wooin/archive/2007/10/31/1858917.aspx">http://blog.csdn.net/wooin/archive/2007/10/31/1858917.aspx</a></li>
</ul>
译文 Rails Cache教程2
2008-01-30T00:00:00Z
https://thekaiway.com/2008/01/30/%E7%BF%BB%E8%AF%91%E6%9D%A5%E8%87%AArails-envy%E7%9A%84rails-cache%E6%95%99%E7%A8%8B-part2/
<p>原文地址:<a href="http://railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2">http://railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2</a></p>
<p>本教程的编写顺序是按照各个缓存的效率来排序的,Page缓存最快,所以在第一篇教程就介绍了,这篇教程就介绍其它的几种缓存。</p>
<!--more-->
<p>Action 缓存</p>
<p>Action缓存和Page缓存十分相似,唯一的区别就是对页面的请求会触及Rails服务器并且filter还是会运行。类似下面代码这样设置Action缓存:</p>
<p>class BlogController < ApplicationController
layout 'base'
before_filter :authenticate # <--- Check out my authentication
caches_action :list, :show
end</p>
<p>可以从代码中看到,用户必须通过认证才能访问list action,当对list进行请求时可以从 <strong>/log/development.log</strong>看到如下日志</p>
<p>Processing BlogController#list (for 127.0.0.1 at 2007-03-04 12:51:24) [GET]
Parameters: {"action"=>"list", "controller"=>"blog"}
Checking Authentication
Post Load (0.000000) SELECT * FROM posts ORDER BY created_on LIMIT 10
Rendering blog/list
Cached fragment: localhost:3000/blog/list (0.00000)
Completed in 0.07800 (12 reqs/sec) | Rendering: 0.01600 (20%) | DB: 0.00000 (0%) | 200 OK [http://localhost/blog/list]</p>
<p>看到“Cached fragment: localhost:3000/blog/list”这行了吗?这表示有缓存文件生成,并且可以找到如下这个文件:</p>
<p>/tmp/cache/localhost:3000/blog/list.cache</p>
<p>默认的情况是, Action缓存会在/tmp/cache/目录下缓存文件,而不是像Page缓存那样直接输出.html文件,是输出.cache文件。如果你在单个应用中包含了一个以上的自域名,缓存文件的路径会包含主机和端口(localhost:3000)。这种情况下每个自域名会缓存在各自的目录下。</p>
<p>如果打开“list.cache”文件,就会看到其中的内容是完整的静态html页面,就像Page缓存的一样。那它们之间有什么区别呢?如果我们再次对页面做出请求(经过上面的请求之后),来看看<strong>/log/development.log</strong>:</p>
<p>Processing BlogController#list (for 127.0.0.1 at 2007-03-04 13:01:31) [GET]
Parameters: {"action"=>"list", "controller"=>"blog"}
Checking Authentication
Fragment read: localhost:3000/blog/list (0.00000)
Completed in 0.00010 (10000 reqs/sec) | DB: 0.00000 (0%) | 200 OK [http://localhost/blog/list]</p>
<p>如你所见, 前置过滤器Authentication执行了,接着读取了缓存文件,然后输出。所以在这个情况下,依然是输出那个完整的缓存文件,并且还做了用户认证的检查。</p>
<p>这里值得注意的是,action执行前前置过滤器必须被执行。另一方面,你也可能会缓存到一个错误的html文件。</p>
<p>怎样清理Action缓存</p>
<p>如教程Part1中,缓存必须在数据发生变化后被清除掉。这里也是用sweepers,只不过在<strong>/app/sweepers/blog_sweeper.rb</strong>中"expire_page" 要改为 "expire_action":</p>
<p># Expire the list page now that we posted a new blog entry
expire_action(:controller => 'blog', :action => 'list')
# Also expire the show page, incase we just edited a blog entry
expire_action(:controller => 'blog', :action => 'show', :id => record.id)</p>
<p>也可以用以下的rake任务来清除 Action 缓存和 Fragment 缓存:</p>
<p>rake tmp:cache:clear</p>
<p>这个rake任务会删除所有的.cache文件。</p>
<p>Fragment Caching</p>
<p>使用了缓存之后会让应用变得飞快,但是由于这是动态的web应用,缓存整个页面并且总能命中这是不实际的,所以有了Fragment缓存。Fragment缓存能缓存页面中的一部分。</p>
<p>要对Blog应用中Post列表进行Fragment进行缓存就要编辑 <strong>/app/views/blog/list.rhtml</strong>:</p>
<p><strong>My Blog Posts</strong>
<% cache do %>
<ul>
<% for post in @posts %>
<li><%= link_to post.title, :controller => 'blog', :action => 'show', :id => post %></li>
<% end %>
</ul>
<% end %></p>
<p>这段cache do的代码会创建文件/tmp/cache/localhost:3000/blog/list.cache,即使用当前的controller和action来进行命名。当下次请求来到并命中cache do部分的代码时就会读取刚刚说到那个缓存文件。让我们查看<strong>/log/development.log</strong>,来看看第一次和第二次请求中发生了什么:</p>
<p>Processing BlogController#list (for 127.0.0.1 at 2007-03-17 22:02:16) [GET]
Authenticating User
Post Load (0.000230) SELECT * FROM posts
Rendering blog/list
Cached fragment: localhost:3000/blog/list (0.00267)
Completed in 0.02353 (42 reqs/sec) | Rendering: 0.01286 (54%) | DB: 0.00248 (10%) | 200 OK [http://localhost/blog/list]
Processing BlogController#list (for 127.0.0.1 at 2007-03-17 22:02:17) [GET]
Authenticating User
Post Load (0.000219) SELECT * FROM posts
Rendering blog/list
Fragment read: localhost:3000/blog/list (0.00024)
Completed in 0.01530 (65 reqs/sec) | Rendering: 0.00545 (35%) | DB: 0.00360 (23%) | 200 OK [http://localhost/blog/list]</p>
<p>有没有感觉到一些冗余?
在第一次请求时生成了缓存,第二次时读取缓存,但是对Posts的SQL查询进行了两次。我们已经对SQL查询的结果缓存到页面中了,我们在缓存之后不用再进行SQL查询了,是吗?</p>
<p>解决这个问题的其中的一个方法就是编辑<strong>/controllers/blog_controller.rb</strong>,在查询前加上一个条件检查:</p>
<p>def list
unless read_fragment({})
@post = Post.find(:all, :order => 'created_on desc', :limit => 10) %>
end
end</p>
<p>现在只会在没有缓存的时候才有查询出现。或者有人会想,把这个请求放到view中的cache do代码块中不就好了吗?但是这个是MVC架构啊。</p>
<p>猜猜怎样清除Fragment缓存</p>
<p># Expire the blog list fragment
expire_fragment(:controller => 'blog', :action => 'list')</p>
<p>没错吧?</p>
<p>在分页中使用Fragment缓存</p>
<p>默认情况下,缓存的命名是通过直接查找当前的controller和action名字来决定的。也就是说controller 为 "blog"并且 action 为 "list"就会缓存到 "/localhost:3000/blog/list.cache"。</p>
<p>在分页的情况下,就要把分页的页码加到缓存文件中。<strong>blog_controller.rb</strong>应该类似下面那样处理:</p>
<p>def list
unless read_fragment({:page => params[:page] || 1}) # Add the page param to the cache naming
@post_pages, @posts = paginate :posts, :per_page => 10
end
end</p>
<p>在这里代码也可以为:read_fragment({:controller => 'blog', :action => 'list', :page => params[:page] || 1}),但Rails决定了前两个参数,这个无法改变,所以可以省略。</p>
<p>/views/blog/list.rhtml如下:</p>
<p><% cache ({:page => params[:page] || 1}) do %>
... All of the html to display the posts ...
<% end %></p>
<p>这段代码会在第一次访问 /blog/list时,会创建 /localhost:3000/blog/list.page=1.cache 的缓存文件,按理可知访问分页2时会创建缓存文件/localhost:3000/blog/list.page=2.cache。 Pretty cool!</p>
<p>对Fragment缓存高级命名机制</p>
<p>大多数时候要对cache进行命名,这里有个示例:</p>
<p>话说现代的Web设计中每个页面都有一个用户自定义的导航目录(nav menu)。这里是一个列出用户要完成的项目任务(task)的导航目录:</p>
<p><div id="nav-bar">
<strong>Your Tasks</strong>
<ul>
<% for task in Task.find_by_member_id(session[:user_id]) %>
<li><%= task.name %></li>
<% end %>
</ul>
</div></p>
<p>这个会在站点的每个页面中显示,如果能在用户第一次访问页面时缓存起来,在之后的访问就不用总是打扰数据库了。那就要对代码进行一点点改动:</p>
<p><div id="nav-bar">
<strong>Your Tasks</strong>
<% cache (:controller => "base", :action => "user_tasks", :user_id => session[:user_id]) do %>
<ul>
<% for task in Task.find_by_member_id(session[:user_id]) %>
<li><%= task.name %></li>
<% end %>
</ul>
<% end %>
</div></p>
<p>当user_id为1时会创建缓存文件/localhost:3000/base/user_tasks.user_id=1.cache。</p>
<p>Fragment缓存命名示例
这里有些用户命名缓存的例子:</p>
<p>cache ("turkey") => "/tmp/cache/turkey.cache"
cache (:controller => 'blog', :action => 'show', :id => 1) => "/tmp/cache/localhost:3000/blog/show/1.cache"
cache ("blog/recent_posts") => "/tmp/cache/blog/recent_posts.cache"
cache ("#{request.host_with_port}/blog/recent_posts") => "/tmp/cache/localhost:3000/blog/recent_posts.cache"</p>
<p>怎样一次sweep多个缓存</p>
<p>上面的分页的Fragment缓存最后生成了很多缓存文件,已知的清除方法只是:</p>
<p>expire_fragment(:controller => 'blog', :action => 'list', :page => 1)
expire_fragment(:controller => 'blog', :action => 'list', :page => 2)
expire_fragment(:controller => 'blog', :action => 'list', :page => 3)
.....</p>
<p>但是这里还有一个方法来做这些事情:
<code>expire_fragment(%r{blog/list.*})</code>
这样会查找blog/list下的所有cache文件。使用这个方法要明白下面几点:</p>
<ol>
<li>不能在Memcached(下面会提到)下运行。</li>
<li>这种方法会对所有的cache文件进行正则匹配,如果你的系统中有4,000+个cache文件,这会让你的系统处理很久。</li>
<li>如果你不慎写错了正则表达式,那可能会出大问题。</li>
</ol>
<p>Action/Fragment缓存的几种缓存机制</p>
<p>Page 缓存只能使用文件系统。Action 和 Fragment缓存就有几种选择:</p>
<ol>
<li>File Store - (默认) - 把缓存放在磁盘中 (默认在 /tmp/cache/ 目录)。</li>
<li>Memory Store - Rails服务器的处理方法,放在内存。</li>
<li>DRb Store - 使用DRb服务器。</li>
<li>MemCache Store - MemCache是一个高性能的缓存处理程序。注意它不是用Ruby实现的。</li>
</ol>
<p>作者认为FileStore是一开始最好的选择,在以后升级为其它的方式(如MemCache)也不难,要改变缓存文件存储的位置可以编辑config/environment.rb:
<code>ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"</code>
ActiveRecord查询缓存</p>
<p>这是第四种缓存机制,对这种机制并没有过多的文档,它只出现在 Rails Edge中,现在来介绍一下。</p>
<p>ActiveRecord并不需要做配置,在默认情况下会自动运行。看看这个action:</p>
<p>class BlogController < ApplicationController
def some_complex_thing
post = Post.find(1)
# .... Do alot of other stuff
post_again = Post.find(1)
end
end</p>
<p>在没有AR缓存的情况下,SQL查询会进行两次。现在使用了AR缓存后,在第二次的查询会读取第一次查询时生成的缓存。这样就不用担心在单个的Action中两次相同的查询会查询数据库两次了。</p>
<p>不过这里的例子不够直观,但想像复杂的认证代码。很多时候可能会在多种认证方式中进行用户数据的查询,而且这是发布在几个不同地方的代码中的,如果缓存了结果,无疑对性能有提高。</p>
<p>查询缓存在action中第一次查询时缓存,在action结束时清除掉缓存。这里没有真正的存储,当 inserts/updates/deletes 执行时,全部缓存就会刷新。如果你要在多个actions或users间缓存数据,请使用MemCache。</p>
<p>参考:</p>
<p>关于fragment缓存的失效可以参看martin推荐的<a href="http://www.ruby-lang.org.cn/forums/thread-1701-1-3.html">timed_fragment_cache插件</a><br> 关于AR的缓存,可以看看<a href="http://ryandaigle.com/articles/2007/2/7/what-s-new-in-edge-rails-activerecord-explicit-caching">这篇文章</a>的,<a href="http://yudionrails.com/2007/12/17/what-s-new-in-edge-rails-activerecord-explicit-caching">这篇文章yudi有翻译</a>。</p>
译文 Rails Cache教程1
2008-01-30T00:00:00Z
https://thekaiway.com/2008/01/30/%E7%BF%BB%E8%AF%91%E6%9D%A5%E8%87%AArails-envy%E7%9A%84rails-cache%E6%95%99%E7%A8%8B-part1/
<p>原文来自Rails Envy
<a href="http://railsenvy.com/2007/2/28/rails-caching-tutorial">http://railsenvy.com/2007/2/28/rails-caching-tutorial</a></p>
<p>整个教程有两部份,第一部分也就是这篇讲page cache,第二篇讲其它的cache。这篇文章其实主要还是讲基础,不过结合了一些示例,并配上生动的语言。</p>
<p>合适的缓存机制可以提升Rails应用的性能。而Page Cache就是Rails中最高效的缓存。Page Cahe机制可以让每次请求不用进行数据库查询,甚至不用触动到Ruby解析器,完全由前端web服务器来进行服务。</p>
<p>配置</p>
<p>如果你想在devlopment模式启用cache机制,就需要修改<code>/config/environments/development.rb</code>文件,找到下面这行并把配置项改为true:</p>
<p>config.action_controller.perform_caching = true</p>
<p>页面缓存</p>
<p>两种很适合缓存页面的情况:</p>
<ol>
<li>当页面对于所有用户都是相同的</li>
<li>当页面是公开的,而且无须用户认证</li>
</ol>
<p>假设环境是在一个不经常改动的Blog页面中。。。Controller的代码应该貌似这样:</p>
<p>class BlogController < ApplicationController
def list
Post.find(:all, :order => "created_on desc", :limit => 10)
end
...</p>
<p>如果想要缓存list action中显示的页面就在代码中加入:</p>
<p>class BlogController < ApplicationController
caches_page :list</p>
<p>def list
Post.find(:all, :order => "created_on desc", :limit => 10)
end
...</p>
<p>这样,在下次请求时就会生成并返回缓存好的list.html页面,下下次的话就是直接返回缓存页面。</p>
<p>如果使用的是mongrel,对list action进行配置后的第一次请求时,/logs/development.log会有貌似以下的记录:</p>
<p>Processing BlogController#list (for 127.0.0.1 at 2007-02-23 00:58:56) [GET]
Parameters: {"action"=>"list", "controller"=>"blog"}
SELECT * FROM posts ORDER BY created_on LIMIT 10
Rendering blog/list
Cached page: /blog/list.html (0.00000)
Completed in 0.18700 (5 reqs/sec) | Rendering: 0.10900 (58%) | DB: 0.00000 (0%) | 200 OK [http://localhost/blog/list]</p>
<p>Cached page: <code>/blog/list.html</code></p>
<p>这行记录表明了页面已被导入,并存放在<code>/public/blog/list.html</code>位置下。在这个文件中没有一丁点Ruby代码。下一次请求到来时又会绕过Rails再次返回这个静态页面,这就提升了效率,降低了服务器的开销。</p>
<p>这样的话,如果是有动态表单的页面和经常更新的页面,Page Cache就不太适合了。不过还可以采用其它的缓存机制,请留意本教程的Part2。(卖广告:>)如果在代码里再加上这一句:</p>
<p>caches_page :show</p>
<p>当URL指向<code>/blog/show/5</code>访问id为5的blog日志时,请问调用的是哪个缓存文件(或其文件名为什么)?</p>
<p>答案是:<code>/public/blog/show/5.html</code></p>
<p>看看下面的例子(URL和对应的缓存文件):</p>
<p>http://localhost:3000/blog/list => /public/blog/list.html
http://localhost:3000/blog/edit/5 => /public/edit/5.html
http://localhost:3000/blog => /public/blog.html
http://localhost:3000/ => /public/index.html
http://localhost:3000/blog/list?page=2 => /public/blog/list.html</p>
<p>伊,等等,不太对阿。第一行和最后一行怎么返回的缓存文件一样?Page Cache忽略了URL附带的参数了。</p>
<p>在采用分页的页面怎么使用Page Cahe</p>
<p>要缓存不同的文件,只能创建不同格式的URL了。使用<code>/blog/list?page=2</code>的话会出问题,那就使用<code>/blog/list/2</code>吧。这样子的话,数字2就是作为params[:id]了,那就要修改路由规则(<code>/config/routes.rb</code>)了:</p>
<p>map.connect 'blog/list/:page',
:controller => 'blog',
:action => 'list',
:requirements => { :page => /\d+/},
:page => nil</p>
<p>配合新的路由,页面的链接也要改一下:</p>
<p><%= link_to "Next Page", :controller => 'blog', :action => 'list', :page => 2 %></p>
<p>上面这句生成的URL就是<code>/blog/list/2</code>了,访问这 URL时会以下两件事会发生:</p>
<p>Rails应用把数字2作为params[:page]</p>
<p>这个页面被缓存为<code>/public/blog/list/2.html</code></p>
<p>上面这个示例告诉我们,如果要使用PageCache机制的话,那就要对附加参数做一下处理,让附加参数成为URL的一部分。</p>
<p>清理Cache</p>
<p>页面失效了怎么办?那就清理掉失效页面呗!</p>
<p>以下两行代码可以清除上面例子中生成的Cache:</p>
<p># This will remove /blog/list.html
expire_page(:controller => 'blog', :action => 'list')
# This will remove /blog/show/5.html
expire_page(:controller => 'blog', :action => 'show', :id => 5)</p>
<p>那就要在每次添加/改动/删除blog日志时都进行这些操作。要把这两行代码加入action中吗?不,有优雅的解决方法。。。</p>
<p>Sweepers</p>
<p>Sweepers是一些能在页面失效是删除旧的缓存的代码。Sweepers监视Model的一举一动,当Model进行CRUD时,Sweepers得知后就会去把相应的缓存删除掉。</p>
<p>Sweepers的操作应该放在一个Controller中,而且作者认为还应该与其它的controller分离开。那就要改动配置文件<code>/config/environment.rb</code>:</p>
<p>Rails::Initializer.run do |config|
# ...
config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )
# ...
end</p>
<p>友情提示,改动环境变量之后记得重启服务器。</p>
<p>如上改动后在/app/sweepers创建sweepers,文件<code>/app/sweepers/blog_sweeper.rb</code>应该是这样:</p>
<p>class BlogSweeper < ActionController::Caching::Sweeper
observe Post # This sweeper is going to keep an eye on the Post model
# If our sweeper detects that a Post was created call this
def after_create(post)
expire_cache_for(post)
end</p>
<p># If our sweeper detects that a Post was updated call this
def after_update(post)
expire_cache_for(post)
end</p>
<p># If our sweeper detects that a Post was deleted call this
def after_destroy(post)
expire_cache_for(post)
end</p>
<p>private
def expire_cache_for(record)
# Expire the list page now that we posted a new blog entry
expire_page(:controller => 'blog', :action => 'list')</p>
<p># Also expire the show page, incase we just edited a blog entry
expire_page(:controller => 'blog', :action => 'show', :id => record.id)<
end
end</p>
<p>生成Sweepers可以使用插件Sweeper Generator,可以参看<a href="http://www.ruby-lang.org.cn/forums/thread-1085-1-5.html">martin的介绍</a>。</p>
<p>友情提示:用after_save方法可以代替上面的after_create和after_update两个方法。</p>
<p>要调用Sweepers,在文件<code>/app/controllers/BlogController.rb</code>中应这些写代码:</p>
<p>class BlogController < ApplicationController
caches_page :list, :show
cache_sweeper :blog_sweeper, :only => [:create, :update, :destroy]
...</p>
<p>当创建一个Blog日志时,会在<code>logs/development.log</code>中发现这样的记录:</p>
<p>Expired page: /blog/list.html (0.00000)
Expired page: /blog/show/3.html (0.00000)</p>
<p>hoho~ sweepers生效了。</p>
<p>在Apache/Lighttpd的漂亮演出</p>
<p>许多Rails应用都会使用Apache作为前端,用Mongrel / Lighttpd处理动态的RoR请求。要使Rails的Page Cache机制生效,告诉服务器当请求来的时候去哪里查找缓存页面。下面是配置Apache为例,修改<code>httpd.conf</code>文件:</p>
<p><VirtualHost <em>:80>
...
# Configure mongrel_cluster
<Proxy balancer://blog_cluster>
BalancerMember [url]http://127.0.0.1:8030[/url]
</Proxy>
RewriteEngine On
# Rewrite index to check for static
RewriteRule ^/$ /index.html [QSA]
# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ $1.html [QSA]
# Redirect all non-static requests to cluster
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.</em>)$ balancer://blog_cluster%{REQUEST_URI} [P,QSA,L]
...
</VirtualHost></p>
<p>在lighttpd中应该是类似这样:</p>
<p>server.modules = ( "mod_rewrite", ... )
url.rewrite += ( "^/$" => "/index.html" )
url.rewrite += ( "^([^.]+)$" => "$1.html" )</p>
<p>这样代理服务器就会/public目录下查询cache文件,这样你可能会想要修改cache文件的目录。</p>
<p>把Cache文件分离处理</p>
<p>首先这样修改<code>/config/environment.rb</code>:</p>
<p>config.action_controller.page_cache_directory = RAILS_ROOT + "/public/cache/"</p>
<p>这样就让Rails在<code>/public/cache/</code> 下生成缓存文件了。接着就修改前端服务器Apache的配置文件<code>httpd.conf</code>:</p>
<p># Rewrite index to check for static
RewriteRule ^/$ cache/index.html [QSA]
# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ cache/$1.html [QSA]</p>
<p>清理单个局部或者全部缓存</p>
<p>当开始使用页面缓存的时候可能会发现,一旦对模型有CRUD操作,基本上所有的缓存都要被清除掉。那直接删除了生成的缓存文件岂不是更好更快。</p>
<p>首先要把cache文件分离出来,这在上一步已经做了。下面的代码直接删除cache文件夹下的所有文件,并记录事件到日志中:</p>
<p>class BlogSweeper < ActionController::Caching::Sweeper
observe Post
def after_save(record)
self.class::sweep
end</p>
<p>def after_destroy(record)
self.class::sweep
end</p>
<p>def self.sweep
cache_dir = ActionController::Base.page_cache_directory
unless cache_dir == RAILS_ROOT+"/public"
FileUtils.rm_r(Dir.glob(cache_dir+"/*")) rescue Errno::ENOENT
RAILS_DEFAULT_LOGGER.info("Cache directory '#{cache_dir}' fully sweeped.")
end
end
end</p>
<p>FileUtils.rm_r 方法删除目录下所有文件。这就相当于执行了多次的expire操作。也可以删词cache目录的子目录下的文件,如下面代码展示的对 /public/blog目录下所有文件进行删除:</p>
<p>cache_dir = ActionController::Base.page_cache_directory
FileUtils.rm_r(Dir.glob(cache_dir+"/blog/*")) rescue Errno::ENOENT</p>
<p>更高级的Page Cache技巧?</p>
<p>在大型Web应用中Page Cache的处理将会是非常复杂的。</p>
<p>Rick Olson写了<a href="http://svn.techno-weenie.net/projects/plugins/referenced_page_caching/">Referenced Page Caching Plugin</a>用数据库来对缓存页面进行跟踪。README中有一些示例展示。</p>
<p>Max Dunn写了篇文章<a href="http://blog.maxdunn.com/articles/2006/09/16/ruby-on-rails-advanced-page-caching">Advanced Page Caching</a>,向我们展示了他如何使用cookies动态地改变页面缓存处理基于用户角色的wiki页面。</p>
<p>最后,Page Cache还无法解决缓存xml文件,Mike Zornek讲述了这个问题并提出了一些方法,详见<a href="http://clickablebliss.com/blog/2006/02/17/rails_caching_xml_bad_mime_types/">http://clickablebliss.com/blog/2006/02/17/rails_caching_xml_bad_mime_types/</a>。</p>
<p>Page Cache怎么测试?</p>
<p>Rails本身并没有提供给我们。Damien Merenne写了一个插件<a href="http://blog.cosinux.org/pages/page-cache-test">http://blog.cosinux.org/pages/page-cache-test</a>试图解决这个问题。试一下。</p>
Rails的用户认证
2008-01-28T00:00:00Z
https://thekaiway.com/2008/01/28/Rails%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81/
<p>最近自己写了写代码,研究了一下关于Rails中用户认证方面的知识。</p>
<p>Rails中的用户认证解决方法,看到大多数是靠插件实现,比如:</p>
<ul>
<li><code>LoginGenerator</code></li>
<li><code>Acts_as_authenticated</code></li>
<li><code>restful_authentication</code></li>
</ul>
<p>其中采用的方法都类似于《Agile Web 2e》的dopet示例的处理方法:</p>
<ul>
<li>通过虚拟的password属性来作为密码</li>
<li>通过随机生成的salt和密码明文(或者MD5等方法)加密后存入数据库</li>
<li>在controller中通过before_filter来阻止未通过认证的用户对action进行调用</li>
<li>在view中通过如islogin?,isadmin?等方法来让保护需要登录或者为管理员的行为</li>
</ul>
<p>插件中一般都是做出一个Model和两个Controller,处理用户数据的User。而Controller,一个负责登录和登出,另一个User的负责用户创建和删除。</p>
<p>关于记住用户状态的实现,大部分用户认证的插件都有。参考<a href="http://codingbitch.com/p/comboy/User+authentication+in+Ruby+on+Rails">User+authentication+in+Ruby+on+Rails</a>:
添加一个<code>cookie_hash</code>字段到user中:</p>
<p>class AddUserCookieHash < ActiveRecord::Migration
def self.up
add_column :users, :cookie_hash, :string
end
def self.down
remove_column :users, :cookie_hash
end
end</p>
<p>接着在登录页面,如login.html.erb中,加入:</p>
<p>remember me</p>
<p>然后在管理login的controller中添加:</p>
<p>def login
if request.post?
@user = User.find_by_username(params[:login])
if @user and @user.password_is? params[:password]
session[:uid] = @user.id
# 当用户需要被记住时,开始对cookie进行处理
# 对cookie生成一个密钥之后放入cookie和存入数据库(user表中)
# 其中还指定了一个cookies失效时间,默认为30天,其实可以把这个参数提出来
if params[:remember]
cookie_pass = [Array.new(9){rand(256).chr}.join].pack("m").chomp
cookie_hash = Digest::MD5.hexdigest(cookie_pass + @user.password_salt)
cookies[:userapp_login_pass] = { :value => cookie_pass, :expires => 30.days.from_now }
cookies[:userapp_login] = { :value => @user.username, :expires => 30.days.from_now }
User.update(@user.id, :cookie_hash => cookie_hash)
end
redirect_to :controller => 'panel', :action => 'secret'
else
@auth_error = 'Bad username or password'
end
end
end</p>
<p>最后在ApplicationController中加入:</p>
<p>session :session_key => '_userapp_session_id'
before_filter :check_cookie
def check_cookie
return if session[:uid]
if cookies[:logowanie_login]
@user = User.find_by_username(cookies[:userapp_login])
return unless @user
cookie_hash = Digest::MD5.hexdigest(cookies[:userapp_login_pass] + @user.password_salt)
if @user.cookie_hash == cookie_hash
flash[:info] = 'You've been automatically logged in' # annoying msg
session[:uid] = @user.id
else
flash[:error] = 'Something is wrong with your cookie'
end
end
end</p>
<p>而关于角色的认证可以使用插件:<a href="http://active-rbac.rubyforge.org/">ActiveRBAC </a></p>
Ruby 1.9的新语法
2008-01-25T00:00:00Z
https://thekaiway.com/2008/01/25/ruby19%E7%9A%84%E5%8F%98%E5%8C%96%EF%BC%88%E4%B8%80%EF%BC%89-%E6%96%B0%E7%9A%84%E8%AF%AD%E6%B3%95%E5%92%8C%E8%AF%AD%E6%84%8F/
<p>资料来自:<a href="http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9">http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9</a></p>
<p>文中有实验性标记的说明很可能会在短时间内被拿掉,原文中解释为”crazy ideas“。</p>
<p>新的语法和语意</p>
<p>新的字面量hash语法[Ruby2]</p>
<p>{a: "foo"} # => {:a=>"foo"}</p>
<p>代码块局部变量 [实验性]</p>
<p>如下使用:</p>
<p># {normal args一般参数; 局部变量local variables}
d = 2
a = lambda{|;d| d = 1}
a.call()
d # => 2</p>
<p>当一个变量被隐藏时,Ruby1.9会给出一个警告:</p>
<p>-:2: warning: shadowing outer local variable - d</p>
<p>代码块参数的作用域总是在代码块内</p>
<p>a = 1
10.times{|a| } # !> shadowing outer local variable - a 代码块外的局部变量a被隐藏
a # => 1</p>
<p>新的lambdas语法 [非常实验性]</p>
<p>a = ->(b,c){ b + c }
a.call(1,2) # => 3</p>
<p>注意这里没有说是替换目前传统的代码块语法。Matz已经明确说明了后者会永远有效。新的语法允许为代码块参数指定默认值,如</p>
<p>{|a,b=1| ... }</p>
<p>这种语法在目前的基于bison的Ruby解析器LALR是不能实现的.</p>
<p>这种新语法可以不用括号包裹参数:</p>
<p>-> { }.call # => nil
-> a, b { a + b }.call(1,2) # => 3
c = 1; -> a, b; c { c = a + b }.call(1,2); c # => 1</p>
<p>还可以更加诡异地使用:</p>
<p>c = 2; -> ;c { c = 1 }.call; c # => 2</p>
<p>甚至是</p>
<p>c = 2; -> *d ; c { d }.call(1,2,3) # => [1, 2, 3]
c = 2; -> ; c { c = 1 }.call; c # => 2</p>
<p>用<code>.()</code>替代使用call方法或者<code>[]</code>方法来调用Procs[实验性]</p>
<p>现在可以这样做:</p>
<p>a = lambda{|*b| b}
a.(1,2) # => [1, 2]</p>
<p>注意你需要句点:</p>
<p>a = lambda{|*b| b}
a(1,2) # =>
# (eval):2: syntax error...
# (a)(1,2)...
~> -:2: undefined method `a' for main:Object (NoMethodError)</p>
<p>可以在括号内使用任何表达式:</p>
<p>(lambda{|a,b| a + b}).(1,2) # => 3</p>
<p><code>.()</code>用法会无视接受者而调用其call方法:</p>
<p>"foo".(1,2) # ~> undefined method `call' for "foo":String (NoMethodError)</p>
<p>Block arguments代码块参数</p>
<p>代码块可以使用&block参数:</p>
<p>define_method(:foo){|&b| b.call(bar)}</p>
<p><a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/23533">ruby-dev:23533</a></p>
<p>新的代码块参数语意</p>
<p>|v|现在会像|v,|的形式工作:</p>
<p>[RUBY_VERSION, RUBY_RELEASE_DATE] # => ["1.8.5", "2006-08-25"]
def m; yield 1, 2; end
m{|v| v} # => [1, 2] # !> multiple values for a block parameter (2 for 1)</p>
<p>相对应的:</p>
<p>[RUBY_VERSION, RUBY_RELEASE_DATE] # => ["1.9.0", "2007-08-03"]
def m; yield 1, 2; end
m{|v| v} # => 1</p>
<p>to_splat 方法使用*参数</p>
<p>用to_splat方法代替to_a方法</p>
<p><code>nil.to_splat</code>返回 [].</p>
<p>允许多个*操作符</p>
<p>经Audrey Tang建议,Ruby1.9允许在方法调用是使用多个*操作符</p>
<p>def foo(*a)
a
end<br>
foo(1, *[2,3], 4, *[5,6]) # => [1, 2, 3, 4, 5, 6]</p>
<p>表达式也是:</p>
<p>[RUBY_VERSION, RUBY_RELEASE_DATE] # => ["1.9.0", "2007-08-03"]
a = [1,2,3]
b = [4,5,6]
[*a, *b] # => [1, 2, 3, 4, 5, 6]</p>
<p>允许在可选参数后加上必要参数</p>
<p><a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/29014">ruby-dev:29014</a></p>
<p>def m(a, b=nil, *c, d)
[a,b,c,d]
end
m(1,2) # => [1, nil, [], 2]</p>
<p>?c的语意</p>
<p>?a现在返回一个单字符字符串代替原来的整数值:</p>
<p>?a # => "a"</p>
<p><code>[]</code>方法的参数</p>
<p>可以在[]方法中使用星号,"assocs"(不用方括号的hash)和代码块参数:</p>
<p>RUBY_VERSION # => "1.9.0"
RUBY_RELEASE_DATE # => "2006-06-11"
class Foo; def [](*a, &block); block.call(a) end end</p>
<p>a = (0..3).to_a
Foo.new[*a, :op => :+]{|x| x } # => [0, 1, 2, 3, {:op=>:+}]</p>
<p>printf风格的格式化字符串(%)</p>
<p>%c 可以输出单字符字符串
%u 为负数提供类似%d的格式<a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/11575">ruby-core:11575</a></p>
<p>允许三元运算符( ? : )扩展到新行</p>
<p>p 1 == 2 ?
0
:
1
# >> 1</p>
<p>[ruby-dev:29189](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/29189</p>
<p>defined?方法和局部变量</p>
<p>RUBY_VERSION # => "1.8.5"
RUBY_RELEASE_DATE # => "2006-08-25"
a = 0
defined? a # => "local-variable"
1.times do |i|
defined? i # => "local-variable(in-block)"
end</p>
<p>相对应的:</p>
<p>RUBY_VERSION # => "1.9.0"
RUBY_RELEASE_DATE # => "2007-08-03"
a = 0
defined? a # => "local-variable"
1.times do |i|
defined? i # => "local-variable"
end</p>
10分钟在Netbean 6上完成Rails 2.0的Weblog
2008-01-23T00:00:00Z
https://thekaiway.com/2008/01/23/10%E5%88%86%E9%92%9F%E5%9C%A8netbeans6%E4%B8%8A%E5%88%9B%E5%BB%BArails20%E7%9A%84weblog-2/
<p>原文来自:<a href="http://blogs.sun.com/divas/entry/creating_a_rails_2_0">http://blogs.sun.com/divas/entry/creating_a_rails_2_0</a></p>
<p>本文是之前NetBean6关于Ruby部分的教程(<a href="http://www.ruby-lang.org.cn/forums/thread-1896-1-1.html">http://www.ruby-lang.org.cn/forums/thread-1896-1-1.html</a>)其中的一个例子,如何在10分钟创建一个Weblog的升级版。<!--more-->我会把和之前的不同之处用<font color="Blue">蓝色</font>标记出来</p>
<p>Rails2.0到来之后带来了很多新的特性,新的变化,这样就导致了以前大量的Rails1.2.X的示例无法在升级到Rails2.0之后运行。 NetBeans6关于Ruby部分的教程中的例子就是这样,由于之前采用的是Rails1.2.5还有ActionController的脚手架方法(scaffold),这些与Rails 2.0并不兼容,故作者写了一篇文章,也就是之前那篇的升级版,一边学习Rails 2.0的新特性新变化,一边修改例子。</p>
<p>首先创建一个Rails项目,这个和以前一样,也就是File > New Project,名字取weblog2.0吧,具体参考上面提到的那篇旧的文章!</p>
<p>创建出项目后自动打开database.yml,真是神奇,NB6竟然帮我们修改好了,使用了以前默认的MySQL,莫非它知道我们对MySQL比较熟?配置一下用户名和密码吧。然后就<font color="Blue">右键点击项目选择Rake Task => db => create</font>来创建数据库。</p>
<p>接着开始使用Rails 2.0的脚手架创建Model和Controller。右击项目后选择Generate,<font color="Blue">接着在下拉列表中选择scaffold,再在Model栏中输入Post title:string。注意在Rails 2.0下不能在此对话框的Controller和Action栏里填入字符。</font>这样就创建好了Post model和PostsController还有<font color="Blue">相关的view(可以看到后缀都改为.html.erb了)</font>。在生成的迁移任务(migrate)中可以看到Rails 2.0的新脚手架已经帮你填好了,如下代码:</p>
<p># db/migrate/001_create_posts.rb
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.timestamps
end
end
def self.down
drop_table :posts
end
end</p>
<p>那就直接可以运行migrate了,和之前一样,右击项目=> Migrate Database => To current version,或者 Rake Task => db => migrate。</p>
<p>接下来就可以运行一下看看这个应用了。不过先要修改默认路由,注释掉默认路由(welcome),然后添加新的路由:</p>
<p># config/routes.rb
# map.root :controller => "welcome"
map.root :controller => "posts"</p>
<p>Rails 2.0脚手架默认生成的规则是REST的,以后添加自定义的Controller的Action要用REST化的方式编写路由。这里可以看到用的也是 <font color="Blue">具名路由,map.root</font>。接着删除了public/ index.html后按下NB6工具栏上面大大的三角形运行服务器,这就可以用浏览器打开那localhost:3000看到我们刚刚完成的一切了。</p>
<p>按照之前的教程里,下一步就是添加Post的字段。右击Database Magrate => Generate,接着在Argument栏中填入AddBodyToPost body:text。这样Rails 2.0又帮我们写好了migrate任务,剩下的就是执行migrate任务。添加了新的字段后,就要修改view了,让我们再次打开Generate对话框,在下拉框中选择scaffold,在Model栏输入Post title:string body:text --skip-migration,并且这次要在下面的单选按钮中选择overwrite。然后就可以打开浏览器看看生效了没有。</p>
<p>下一步就是让Rails对创建Post时进行输入校验。这里展示的是对Post的属性进行非空校验,在Post Model源码(app/model/post.rb)中新建一行,输入vp再按tab,NB6的模板完成功能就体现了,vp自动扩展为 validates_presence_of,那接着就输入:title, :body。(由此可得vu就是 validates_uniqueness_of,试了一下,果然,具体模板完成请看NB6中选项里关于模板的部分)下来就是打开浏览器试验一下。</p>
<p>最后一步就是稍微打扮一下weblog,打开app/view/posts/index.html.erb,把文件里的内容替换为以下内容:</p>
<p><h1>The Ruby Blog</h1>
<% @posts.each do |post| %>
<h2><%= post.title %></h2>
<p><%= post.body %></p>
<small> <%= link_to 'Permalink', post_path(post) %></small>
<hr>
<% end %>
<br />
<%= link_to 'New post', new_post_path %></p>
<p>最后就是让Posts按时间顺序显示,把上面第3行代码改为:</p>
<p><% @posts.reverse.each do |post| %></p>
<p>可以看到在视图中,新的脚手架创建的这句代码<%= link_to 'New post', new_post_path %>中的new_post_path,这是Rails2.0中REST化的体现之一。另外,原文中第6行代码还是以前那种写法,也就是指定controller和action的写法,我改了过来,让它和下面New Post的写法统一,我在原文下面留言提了这个。</p>
<p>很多同学都有问我,Rails升级到2.0后NB6中关于Ruby的这个10分钟创建weblog的教程怎么做,这里就是答案。有什么错误请大家指出来哦。</p>
暑假开始
2008-01-21T00:00:00Z
https://thekaiway.com/2008/01/21/%E5%AF%92%E5%81%87%E5%BC%80%E5%A7%8B/
<p>2008年的寒假,一回到家就感冒了。。。有点郁闷,明明学校比家冷那么多我都没有感冒,怎么一回家就感冒了。<!--more-->
另外对家里电脑的龟速真是。。。,内存不足是一回事,P4真的不如超了频的A3000+,而且宽带对比学校的校园网也是逊色很多。。。不能一边开bt和电驴,一边编译一些东西,再一边听歌。有时gvim还会卡一卡,rails的rake任务也是很慢。。。期待暑假买的HP6510b。。。</p>
<p>回到家里觉得少了安全感,回来那天上楼时发现一家人给入室盗窃了,又听说几个月前对面那栋楼也给入室了。</p>
<p>小区里的大树都给锯了,整个小区有种和以前不同的感觉,凄凉的感觉。。。</p>
<p>寒假开始了,又有一堆时间要自己支配,自己学习。要实现自己在一个月前定下的计划。</p>
<p>给自己加油!</p>
暑假书单
2008-01-21T00:00:00Z
https://thekaiway.com/2008/01/21/%E5%AF%92%E5%81%87%E4%B9%A6%E5%8D%95/
<p>这是寒假的看书计划</p>
<p>励志类:
《高效能人士的七个习惯》《要事第一》,科维的两本修身的书。</p>
<p>技术类:
《RESTful Web Services》,深入认识REST,这本书的大部分例子都是用rails写的。DHH作序推荐的。</p>
<p>《JavaScript高级程序设计》,是时候看看JS了,不懂JS的话,做Web开发是有点辛苦。</p>
<p>还有把《Rails Cookbook》的笔记整理出来。</p>
Thin简洁
2008-01-10T00:00:00Z
https://thekaiway.com/2008/01/10/thin%E7%AE%80%E4%BB%8B/
<img src="http://code.macournoyer.com/thin/images/logo.gif">
<p>官方主页:<a href="http://code.macournoyer.com/thin/">http://code.macournoyer.com/thin/</a></p>
<p>thin是个合成品,分别使用了来自mongrel的解析器,Every Machine的网络IO库,Rack的web服务器和Ruby框架的接口。<!--more--><br> <br> 也就是说thin有mongrel的速度和安全性,有Every Machine的高伸缩性,性能和稳定性。</p>
<img src="http://chart.apis.google.com/chart?cht=bvg&chd=t:14.98,54.8723076923077,48.9184615384615,79.9276923076923%7C14.8692307692308,65.0615384615385,70.4446153846154,89.5553846153846%7C14.9476923076923,35.1123076923077,70.18,88.6769230769231&chbh=16&chs=350x150&chl=WEBrick%7CMongrel%7CEvented%20M.%7CThin&chco=000000,666666,cccccc&chdl=1%20c%20req.%7C10%20c%20req.%7C100%20c%20req.">
<p>那如何在你的Rails中使用thin呢?首先安装thin:
gem install thin
然后要运行thin服务器就在你的根目录下执行:
thin start
运行成功:
>> Thin web server (v0.5.0)
>> Listening on 0.0.0.0:3000, CTRL+C to stop
另外一个运行thin的方法(通过Rack的rackup命令):<br> 在你的Rails应用根目录下创建一个名为config.ru的文件:</p>
<p>require 'rubygems'
require 'thin'
app = proc do |env|
[ 200, { 'Content-Type' => 'text/html' }, ['hi'] ]
end
run app</p>
<p>然后就执行</p>
<p>rackup -s thin</p>
<p>这样也可以运行thin服务器。</p>
<p>本文相关资料:</p>
<ul>
<li>http://code.macournoyer.com/thin/</li>
<li>http://mongrel.rubyforge.org/</li>
<li>http://rubyeventmachine.com/</li>
<li>http://rack.rubyforge.org/</li>
</ul>
<p>对岸的同学的讨论:
http://lightyror.thegiive.net/2008/01/thin-mongrel-app-server.html</p>
Rails 2.0中分离出来的Plugins
2008-01-09T00:00:00Z
https://thekaiway.com/2008/01/09/rails20%E7%9A%84%E5%AE%98%E6%96%B9plugins%E5%88%97%E8%A1%A8-%E4%BB%8E12%E4%B8%AD%E5%88%86%E7%A6%BB%E5%87%BA%E6%9D%A5%E7%9A%84%E4%B8%80%E4%BA%9B%E5%8A%9F%E8%83%BD/
<p>Rails2.0中把Rails1.2中的一些功能分离出来作为插件。<!--more-->最近论坛上就有很多人问关于Rails2.0怎么缺失了一些功能的问题,这里就是答案,以后看到什么功能不见了,看看这贴就OK了。比如acts_as_list,scaffolding,还有一些Ajax的辅助方法等。</p>
<p>下载地址:<a href="http://svn.rubyonrails.org/rails/plugins/">http://svn.rubyonrails.org/rails/plugins/</a></p>
<p>列表</p>
<ul>
<li>account_location/</li>
<li>acts_as_list/</li>
<li>acts_as_nested_set/</li>
<li>acts_as_tree/</li>
<li>atom_feed_helper/</li>
<li>auto_complete/</li>
<li>continuous_builder/</li>
<li>deadlock_retry/</li>
<li>exception_notification/</li>
<li>http_authentication/</li>
<li>in_place_editing/</li>
<li>javascript_test/</li>
<li>legacy/</li>
<li>localization/</li>
<li>open_id_authentication/</li>
<li>scaffolding/</li>
<li>scriptaculous_slider/</li>
<li>ssl_requirement/</li>
<li>token_generator/</li>
<li>tzinfo_timezone/</li>
<li>tztime/</li>
<li>upload_progress/</li>
</ul>
安装配置DMD
2007-12-31T00:00:00Z
https://thekaiway.com/2007/12/31/dmd%E5%9C%A8ubuntu%E4%B8%8B%E7%9A%84%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/
<p>D语言是下一个主流静态语言霸主的强有力候选人。DMD是D语言的一个编译器。<!--more-->
到下面的地址下载dmd,后解压。(其中dmd/src/dmd是dmd的源码,dmd/sample/d是d语言的几个简单示例。)</p>
<p><a href="http://www.digitalmars.com/d/dcompiler.html">http://www.digitalmars.com/d/dcompiler.html</a></p>
<ul>
<li>安装配置文件,复制<code>dmd/bin/dmd.conf</code>到<code>/usr/local/bin</code>下。</li>
<li>为dmd/bin下的这几个文件添加执行权限,dmd,dumpobj,obj2asm,rdmd。</li>
<li>安装编译器,复制上面的几个文件到 /usr/local/bin</li>
<li>安装lib,复制<code>dmd/lib/libphobos2.a</code> 到 /usr/local/lib</li>
<li>安装源码,复制dmd/src/phobos到/usr/local/src</li>
<li>安装man,复制dmd/man/man1下的所有文件到/usr/local/man/man1下。</li>
</ul>
<p>这样就OK了。</p>
<p>dmd.conf也可以放在/etc下不过要把lib和src的路径改为绝对路径,原文件中的路径是用%@P%来标示当前路径。</p>
<p>测试一下编译,没有报找不到文件就OK了。</p>
<p>其实D语言也是红色的。。。D语言应该是接任 Java和C#的下个静态语言王者。现在几个主流的编辑器都支持D语言,比如vim,emacs,scite。</p>
<p>dmd中带的几个例子中有几个是只能在windows下运行的,有点不爽。</p>
<p>D语言的几个资源:</p>
<ul>
<li><a href="http://www.digitalmars.com/d/index.html">http://www.digitalmars.com/d/index.html</a></li>
<li><a href="http://www.dnaic.com/d/doc/d/index.html">http://www.dnaic.com/d/doc/d/index.html</a></li>
<li><a href="http://www.dsource.org">http://www.dsource.org</a></li>
</ul>
康抽C
2007-11-29T00:00:00Z
https://thekaiway.com/2007/11/29/%E5%BA%B7%E6%8A%BD%E8%A5%BF/
<p>今天上《操作系统》课时,老师忽然大放厥词:<!--more-->
“其实你们不要把Linux看的那么神秘,和windows是一样的。它有一个X-Windows图形界面。。。微软的windows有世界上最好的界面。。。Linux是什么F什么G的软件,都是免费的,开放源代码的,所以界面很难看。。。商业软件才是最好的。。。我们实验室用的红帽子就是一个Linux系统,你们看了之后就知道界面很难看,和现在用的windows没法比的。。。Linux就是不好。。。gcc是个很强大编译软件,也是免费的。它和一般的编译器不一样,在windows下要进行编译链接,gcc直接就输出二进制文件,不用链接,很先进。。。为什么要用Linux做系统调用的实验而不用windows呢?因为Linux是用C写的,可以用函数调用的形式进行系统调用的。”</p>
<p>对于上面的言论,我想每个Linux Fan都会听到很不爽,包括我。不是不爽windows和Linux比较,是不爽她的无知。其实又提界面的这个常见的口水战主题的话,讲太多也是没意义的。只是她不是那同个时代的东西来比较,根本没有可比性,RedHat Linux 9同时间段的微软windows产品是windows 2000(或者NT),界面一比校,色盲都看得出哪个好看。到了现在的“畏死它”和同时期的装上了Bery和Compiz的Ubuntu,这次无论在性能上还是在美观上,还是Linux领先,而且不止领先一个身位。每次她都是提到X-window都是叫做“X-windows”,我前两次都是说出来纠正一下,她基本不理会我的意见,后来基本我习惯了,觉得算了。基本上看国外的视频教程,他们都是用Mac OSX或者Linux的,我上次也在一篇文章里说过,一个不经常用Linux外国佬他竟然懂得很多很多Linux的知识。对于外国的学术和教育机构不用 windows的说法我也听说了很多次。为什么中国会这样呢?</p>
<p>这门课的老师是个副教授,还是西电大毕业的研究生。都快50(或者过50)的人了,知识面还那么小,感觉她很多时候都是在误导人。有时不是我喜欢逃课,是去那里听课,听到老师一些讲错的地方实在会让我很难受。我总觉得一个从事计算机相关行业的人,对技术应该体会很深,至少对某个方面应该是很熟的,可是我发现很多老师都没有。有一次某老师在说:“我很喜欢研究编程语言,我懂得很多种语言,比如C啊,C++,Java,Pacal,还有你们应该没有听说的elisp,那是一种编辑器的扩展语言,还有Fortran我也会。其实编程语言都是一样的,差不多的。”我觉得说编程语言都一样的人,一般都不会是个很厉害的人。以后写篇日志讲讲这个。</p>
<p>看到这里看这篇文章的人会问,题目为什么叫“康抽西”?因为这位老师讲到Linux怎么中断程序时,说Ctrl+C的时候就念“康抽西”。</p>
SVN入门
2007-11-25T00:00:00Z
https://thekaiway.com/2007/11/25/svn%E7%AE%80%E5%8D%95%E5%9B%BE%E6%96%87%E6%95%99%E7%A8%8B/
<p>这个教程主要面向还对svn操作一窍不通的同学。下面的操作环境是Linux下的,windows下安装svn后也可。</p>
<p>首先checkout出来。</p>
<p>命令语法为</p>
<p>svn co URL dir(会提示输入用户名和密码)URL为svn仓库地址,dir为本地文件夹(即存放checkout出来文件的本地文件夹)</p>
<p>然后添加文件进去。(先把要上传的文件复制到对应的文件夹里,然后再执行命令)</p>
<p>执行添加命令:</p>
<p>命令语法为</p>
<p>svn add uploadfile</p>
<p>最后执行commit完成提交。</p>
<p>命令语法为</p>
<p>svn ci -m "提交信息"</p>
<p>以后每次更新到最新的版本只要在仓库目录下执行svn update就可以了。</p>
<p>checkout和commit可以简写为co和ci。</p>
<p>要查看版本信息可以执行svn log:</p>
<p>一般我们在中文化工作中要做的就是这些了。</p>
简洁的Sinatra
2007-11-12T00:00:00Z
https://thekaiway.com/2007/11/12/sinatra%EF%BC%9A%E4%B8%80%E4%B8%AA%E4%BC%98%E9%9B%85%E5%9C%B0%E5%8C%85%E8%A3%85%E4%BA%86web%E5%BC%80%E5%8F%91%E7%9A%84dsl/
<p>你相信用100行代码可以实现一个博客吗?用Sinatra框架就可以做到。</p>
<p>下面就是这样子的一个Web应用:</p>
<p><a href="http://journal.redflavor.com/reprise---a-minimalistic-blog">Reprise - A Minimalistic Blog</a></p>
<p>这个应用的代码:http://redflavor.com/reprise.rb 一个简单的hello world的web应用要写多少代码?用Sinatra只需5行代码:
CODE:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">require</span> <span class="token string-literal"><span class="token string">'rubygems'</span></span>
<span class="token keyword">require</span> <span class="token string-literal"><span class="token string">'sinatra'</span></span>
get <span class="token string-literal"><span class="token string">'/'</span></span> <span class="token keyword">do</span>
<span class="token string-literal"><span class="token string">'Hello World'</span></span>
<span class="token keyword">end</span></code></pre>
<p>Sinatra官方主页:http://sinatra.rubyforge.org/</p>
翻译其实很难
2007-10-28T00:00:00Z
https://thekaiway.com/2007/10/28/%E7%BF%BB%E8%AF%91%E5%A5%BD%E9%9A%BE/
<p>昨天翻译了drive2me推荐的一篇文章作为翻译练习。今天在drive2me大哥(大叔)的耐心指导下完成了修改和校对。</p>
<!--more-->
<p>看懂一篇英文的文章不难,把它翻译出来就难了。信达雅自己说的多,也上过china-pub骂过一些译者不负责任翻译得不好(再次谴责一下TDD中译本的译者),到自己翻译的时候就知道翻译是什么滋味了。</p>
<p>由于我的英语基础不好,对于比较口语化的英语,英语的分词,时态,还有介词都处理得不好,遇到它们时我就有点不知所措了:(</p>
<p>drive2me 大哥在指导中,教会了我翻译的几个要点。翻译要注意细节,细节决定成败。英文文章和中文一样,整个文章都是围绕着一个中心,每一句都是和其上下文紧紧相关的,开始觉得之前高中到小学的语文课教的对文章的分析有用了,比如这个词在这里意义不明显,但是结合全文来看这个词的含义就出来了。还有对于句式的处理,比如转折,比较等等,在英文中一两个词就可以表达出来,中文可能要花好几个词还有语气来表达。最后还有一点,要忠于原文,忠于作者原意,作者表达出来的就翻译出来,作者没有说的就不要多加上去。最后这点看上去好像很废话,可是到了真正在翻译时就能理解了。</p>
<p>嗯嗯,就是经验,这对我以后很有帮助。我相信我以后的翻译更好的。组织文档翻译小组是一次机会,一次让我提高的机会,我想我能做好的。</p>
Rails 2.0 Multiple View
2007-10-28T00:00:00Z
https://thekaiway.com/2007/10/28/%E7%BF%BB%E8%AF%91rails-20-features-multiple-views/
<p>作者: madpilot</p>
<p>原文链接:
http://www.sitepoint.com/blogs/2007/10/26/rails-20-features-multiple-views/</p>
<p>下一个主要版本的Ruby on Rails(指2.0)加入了复合视图这个新特性。大约上个月底,预览版发布了,我有幸试用了一下,我想及时地概述一下一些新特点。</p>
<p>复合视图</p>
<p>在rails 1.2中,支持多种数据类型的respond_to块被引入,它可以轻松地支持象XML或者JSON。你需要做就是像下面这样:
CODE:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">index</span></span>
<span class="token variable">@stories</span> <span class="token operator">=</span> Story<span class="token punctuation">.</span>find <span class="token symbol">:all</span>
respond to <span class="token keyword">do</span> <span class="token operator">|</span>format<span class="token operator">|</span>
format<span class="token punctuation">.</span>html <span class="token punctuation">{</span> render <span class="token punctuation">}</span>
format<span class="token punctuation">.</span>xml <span class="token punctuation">{</span>
render <span class="token symbol">:xml</span> <span class="token operator">=></span> <span class="token variable">@stories</span><span class="token punctuation">.</span>to_xml
<span class="token punctuation">}</span>
format<span class="token punctuation">.</span>json <span class="token punctuation">{</span>
render <span class="token symbol">:json</span> <span class="token operator">=></span> <span class="token variable">@stories</span><span class="token punctuation">.</span>to_json
<span class="token punctuation">}</span>
<span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre>
<p>然后在浏览器上,如果你加上了扩展文件(比如/stories/index.xml)并得到包括请求格式的内容,那么你可以在environment.rb文件的最后添加MIME::Type.register,来创建自定义的类型。</p>
<p>这种做法的一个问题是,基于扩展文件这种方式没有办法支持不同的HTML页面。因为MIME::Type解析器工作方式是,添加其他MIME类型为 text/html的内容标识(handler)会扰乱默认的标识(handler),这意味着上面的代码会提供错误的视图。</p>
<p>输入Mime::Type.register_alias</p>
<p>现在你可以告诉Rails用HTML响应你希望使用的多种文件类型。就是说你想设计一个mobile版本和iPhone版本的站点,你能通过添加下面的代码到/config/initializers/mime_types.rb文件创建两个新的格式:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> Mime<span class="token double-colon punctuation">::</span>Type<span class="token punctuation">.</span>register_alias <span class="token string-literal"><span class="token string">"text/html"</span></span><span class="token punctuation">,</span> <span class="token symbol">:iphone</span>
Mime<span class="token double-colon punctuation">::</span>Type<span class="token punctuation">.</span>register_alias <span class="token string-literal"><span class="token string">"text/html"</span></span><span class="token punctuation">,</span> <span class="token symbol">:mobile</span></code></pre>
<p>这使下面代码可以运作:</p>
<pre class="language-ruby" tabindex="0"><code class="language-ruby"> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">index</span></span>
<span class="token variable">@stories</span> <span class="token operator">=</span> Story<span class="token punctuation">.</span>find <span class="token symbol">:all</span>
respond to <span class="token punctuation">{</span> <span class="token operator">|</span>format<span class="token operator">|</span>
format<span class="token punctuation">.</span>html <span class="token punctuation">{</span><span class="token punctuation">}</span>
format<span class="token punctuation">.</span>xml <span class="token punctuation">{</span>
render <span class="token symbol">:xml</span> <span class="token operator">=</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> <span class="token variable">@stories</span><span class="token punctuation">.</span>to_xml
<span class="token punctuation">}</span>
format<span class="token punctuation">.</span>json <span class="token punctuation">{</span>
render <span class="token symbol">:json</span> <span class="token operator">=</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> <span class="token variable">@stories</span><span class="token punctuation">.</span>to_json
<span class="token punctuation">}</span>
format<span class="token punctuation">.</span>iphone <span class="token punctuation">{</span>
<span class="token operator">/</span><span class="token operator">/</span> Serve up the iPhone version
<span class="token punctuation">}</span>
format<span class="token punctuation">.</span>mobile <span class="token punctuation">{</span>
<span class="token operator">/</span><span class="token operator">/</span> Serve up the mobile version
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">end</span></code></pre>
<p>当然,必须在每个respond_to代码块用手工提交不同的版本的做法不太符合DRY原则,所以已经为所有的视图文件创建了一种新的命名惯例。相对于在上面的index.rhtml中调用视图文件,根据你要使用的方式建立三种不同的版本的视图文件更好,比如, index.html.erb, index.iphone.erb 和 index.mobile.erb。</p>
<p>当rails找到一个匹配的视图就使用它;当找不到时就使用默认的.html.erb或者.rhtml文件。这让你的站点方便地用不同的版本的视图提供服务。</p>
<p>PS: 这篇文章有drive2me大哥帮我校对,谢谢drive2me帮我耐心地讲解,和校对。</p>
三本大书
2007-10-23T00:00:00Z
https://thekaiway.com/2007/10/23/%E4%B8%89%E6%9C%AC%E5%A4%A7%E4%B9%A6%E6%9D%A5%E4%BA%86/
<p>今天买的三本大书来了。邮局的快递下午送来的,三本,《Ruby cookbook》和《Rails cookbook》,还有《head first 设计模式》。Rail的cookbook是影印版,其他两本是中文版。<!--more--></p>
<p>今晚看了Rail cookbook,看了第六章,讲REST的,每个Recipe都有一个生动的例子做解释,代码最具表现力的。过几天想把这一章翻译出来,放在论坛给大家看看。还有这本书感觉上和《Rails Recipes》很类似,虽然Recipes只是粗略地浏览一下而已,不过感觉是一样的。Cookbook和Recipes配合着《滑板之道》一起看好像效果比较好,因为Rails的每个特性,都在这两本书上有很好应用示例。这样的书里的只是很容易应用到实际开发上,嗯,实用主义。</p>
<p>Ruby Cookbook也是比较实用,我买这本书的用意就是想看看很多api的例子。我自己在写一个叫RStyle的项目,现在才0.1,是个代码格式化器,里面涉及了一个代码分析器。在写代码分析器时,我开始有点不知所措了,所以想找点Ruby编程的书看看,好像《Ruby Way》这本书里讲的一些数据结构也挺适合的,嗯下次买这本。还没有开始看,应该会很有趣。</p>
<p>《HF设计模式》就是我垂涎已久的书了,中文版跳票了很久,一年前就在等这本书的中文版,现在终于到手了。这系列的书就是有趣生动,多图片,一边学习一边玩,很好的感觉。比如适配器模式,这里的图片用了插座的例子,一个两头的插头,要插进一个三孔的插座,要怎么办?答案是用一个两头转三头的插头,是不是很生动?我一直在想设计模式在Ruby中会怎么表现,正好一边看这本书可以一边思考这个问题。</p>
<p>最近想看多一点源码,最近在看redmine的源码,觉得这个系统很经典,很多代码值得玩味,以后写点心得和研究出来。我要学多一点,写多一点,总结多一点,我要变得更厉害。</p>
英语哑巴
2007-10-21T00:00:00Z
https://thekaiway.com/2007/10/21/%E7%A2%B0%E8%A7%81%E9%AC%BC%E4%BD%AC%E5%8F%98%E6%88%90%E5%93%91%E5%B7%B4%E7%9A%84%E6%95%85%E4%BA%8B/
<p>昨天和同学去帮一个外教把Ubuntu搞上网,本来想着去到那里和他好好聊一下Ubuntu的相关话题的。</p>
<!--more-->
<p>其实去到只是安装一个库(so)。他还叫了他一个朋友来帮忙,也是鬼佬。另外这个鬼佬比较厉害,他说他使用Linux From 1998,好像很厉害的样子,会用svn下载软件,对Linux的图形界面操作很熟,对命令行的操作就不熟。我同学感叹到,外国人对电脑这东西的熟悉程度比国人高太多了,从小就象一般家庭电器那样接触电脑。</p>
<p>其实很多地方我对他的操作有很多建议,但是都说不出,平时真是太少读口语。他问我改名的命令是什么,我回答是连V的发音都发不准,真失败。在外国佬面前我好像是个哑巴,太没用了。在平时我可以在不借助翻译软件的情况下看懂英文原版书的,也可以很好地翻译英文的,自认为是这样。好像我的英语能力还在书面英语阶段。</p>
<p>回来之后我很失落,而且下定决心要读多点英语。在整个暑假,我在背新概念英语,大概是两天一篇,看来成效还没有出现。以后没事不听歌了,听英语好了,一个星期要背两三篇新概念英语。在以后做软件开发这行的话,交流是很重要的,和客户交流,和团队中的同事交流,其中很难说没有外国人,那这样英语就是交流的重要工具了。我需要达到能独立和外国人用英语交流的水平。</p>
<p>我不想再在鬼佬面前当哑巴了。</p>
Ubuntu 7.10
2007-10-19T00:00:00Z
https://thekaiway.com/2007/10/19/%E6%96%B0%E9%B2%9C%E5%87%BA%E7%82%89%E7%9A%84ubuntu710/
<p>昨天18号,Ubuntu官方发布了Ubuntu的最新版本7.10。</p>
<!--more-->
<p>昨天一大早我就在ubuntu中文社区和一大班人一边等一边jjww。等到中午时,我等不及了,就开始Upgrade了。好像cn99的源一直都连不上,只能用大陆的官方源来升级了。按照官方的说法是用更新管理器来做版本升级的,官源的速度不是很快只是一百多K而已。大概一个钟的时间就能完成版本的 upgrade,中间的时间去睡了一下下。</p>
<p>升级完重启,一堆问题开始来了。首先Grub只能认到整个扩展分区,认不到每个分区。不知是不是我的硬盘的分区表坏了。然后就是因为显卡驱动的原因,新内核进不去。再之后就是gnome不能启动,好在还有个xfce和fvwm,进去把Gnome重装一下,OK。进去Gnome后发现字体很难看,接着越调整越有问题,后来刷新了一下字体缓存就正常了。后来又发现Firefox的字体显示出来很小,调整了还是这样,卸了之后装了swiftfox好像好一点。不过用firefox看flash会假死。</p>
<p>完成了升级后,感觉变化不大,不过很明显新的Gnome的cpu占用小了,反应快了。还没有体验到新内核的提速,等到Ati的8.42.4来临再来。说到 8.42.4,ati拖了几天了,win的都出了好多天了,真是不公平。本来还想着U7.10能集成到新的官方驱动的,结果还是不行,只能寄望于8.04 了。</p>
<p>总的来说升级带来的问题不多,升级后的效果也不错,我升级的时候还很紧张呢:)</p>
<p>PS:这篇blog写了三次,第一次没有登录,登录完回来就没有,第二次时,快写完的时候开了InfoQ准备看flash,结果firefox死了。</p>
惊艳的Compiz Fusion
2007-10-09T00:00:00Z
https://thekaiway.com/2007/10/09/ubuntu%E4%B8%8B%E5%BC%80%E5%90%AFcompiz-fusion%E6%95%88%E6%9E%9C%E5%9B%BE/
<p>前天用我的X1650GT和同学换了一块7300GT,在Ubuntu折腾了一下,终于如愿以偿地开启了Compiz Fusion的效果,并且用gDesklets在桌面加了一些实用工具挺好玩的。</p>
<!--more-->
<p>过程也挺折腾的,先是安装了 ati的显卡驱动后要装nv的驱动碰到问题,然后再是开启特效后窗口没有了边框。在安装ati驱动后装nv的驱动先要删除几个库文件(libGL.so),看他提示哪个就删除哪个,然后nv驱动才可以正常安装。nv显卡开特效的边框的问题 只要运行一下sudo nvidia-xconfig --add-argb-glx-visuals就OK了。</p>
<p>现在上图看看:
<img SRC="http://www.blogjava.net/images/blogjava_net/maninred/1.jpeg"></p>
<img SRC="http://www.blogjava.net/images/blogjava_net/maninred/4.jpeg">
<img SRC="http://www.blogjava.net/images/blogjava_net/maninred/3.jpeg" HEIGHT="768" WIDTH="960">
<img SRC="http://www.blogjava.net/images/blogjava_net/maninred/2.jpeg">
Ruby与Java对比
2007-10-09T00:00:00Z
https://thekaiway.com/2007/10/09/ruby-lang%E4%B8%8Aruby%E4%B8%8Ejava%E7%9A%84%E5%AF%B9%E6%AF%94%E6%96%87%E7%AB%A0/
<p><a HREF="http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/to-ruby-from-java/">原文地址</a>
下午无聊翻译一下,看看ruby官方怎么和java对比</p>
<!--more-->
<p>相同点:</p>
<p>类似于java,在ruby中:</p>
<p>内存管理都是由GC(garbage collector)负责。
都是强类型语言。
都有public,private,protected的方法可见性之分。
都有嵌入式文档工具(ruby的叫做RDoc)。rdoc生成的文档看起来非常像由javadoc生成的。</p>
<p>不同:</p>
<p>于java不同的是,在ruby中:</p>
<p>你不必编译代码,你只需要直接运行代码。
Gui开发包不同。比如,ruby的用户会尝试WxRuby,FXRuby,Ruby-GNOME2,或者基于Ruby Tk库。
你要在定义任何事物(像class)后面加上关键字end,而不是用花括号({})来包围代码块。
使用了require代替了import。
所有的成员变量(属性)都是private级的。在类外访问任何事物要通过方法调用。
什么都是对象,包括2和3.14159。
没有静态类型检查。
变量名都只是标签,它们没有与类型相关。
不用声明变量类型,你只需要赋值给新的变量名就可以了(例子,a = [1,2,3] 相当于 int[] a = {1,2,3};)。
没有类型强制转换这个概念,只管调用方法就好了。
用 foo = Foo.new( "hi") 取代这样新建对象的方法 Foo foo = new Foo( "hi" )。
构造方法一直都叫“initialize”,不是和class同名的方法。
使用“混入” 代替 “接口”。
YAML 比 XML 更受欢迎。
这里用nil代替null。
==和equals()方法处理方法不同。当你想要测试相等性时就使用 == 操作符(就像Java的equals()方法)。当要想要知道两个对象是否是同一个时就使用equal?()方法(就像Java中的==)。 [点击图片可在新窗口打开]</p>
zh周蟒
2007-10-08T00:00:00Z
https://thekaiway.com/2007/10/08/zhpy%E5%91%A8%E8%9F%92%EF%BC%8C%E5%AE%9E%E7%8E%B0%E7%94%A8%E4%B8%AD%E6%96%87%E5%86%99%E4%BB%A3%E7%A0%81%E7%9A%84%E6%84%BF%E6%9C%9B/
<p>周蟒是中文的Python,支持简体和繁体,用它就能进行中文来写代码。</p>
<p>周蟒的官方主页:
<a href="http://code.google.com/p/zhpy/">http://code.google.com/p/zhpy/</a></p>
<blockquote>
<p>簡介</p>
<p>周蟒 (zhpy) 是一種簡單易學、功能強大的程式語言。它具有高效率的高階資料結構、簡單而有效的物件導向程式設計方式。並讓你可以使用純中文語句 (繁體或簡體) 來編寫程式。 馬上照著 咬一口周蟒語言 一書學習吧!
周蟒是什麼?</p>
<p>* 友善而易於學習的中文程式語言
* 清晰可讀的語法
* 直觀的物件導向能力
* 方便的動態物件類型
* 透過模組有架構地組織程式
* 多平台適用(Windows, Linux, Mac)的程式語言
* 全功能的中文版本 Python 語言</p>
<p>特點</p>
<p>* 周蟒是全功能的 Python 程式語言。
o 周蟒本身就是個 python 模組
o 擁有所有 python 程式語言的功能與特性
o 可以在周蟒中使用所有的 python 模組
* 安裝容易
o 可使用 easy_install 命令線上安裝
o 檔案體積小 (=0.8)
o 可在作業系統中當作中文腳本執行
o 可在 Python 互動式直譯器中測試周蟒中文程式
* 擴充性好
o 可以混用繁簡中英 關鍵詞/保留字
o 可以自訂參考字表
* 發展性高
o 周蟒, python 程式碼可互相轉換 (>=0.8)
o 可用周蟒程式碼生成英文 python source
o 可用 python 程式碼生成繁簡體周蟒程式
o 產生的 python source 可應用在任何 python 程式中
o zhpy 換上日韓關鍵字也可以擴展成日蟒, 韓蟒
* 完整測試
o 超過60筆單元測試(unittest)與文件測試(doctest)
o 完整的範例測試與命令行測試</p>
<p>周蟒背後的哲學有三:</p>
<p>* 把事情做好 (Getting Things Done)
* 程式碼被閱讀的機會比被修改的機會多 (Code is read much more than it it written)
* 一致的表達 (There should be one obvious way to do it)</p>
<p>程式語言再怎麼修改得接近自然語言, 都仍然具有程式語言的邏輯與規則, 周蟒語言的目的並不是接近中文自然語言,而是做出一個實用的中文程式語言。</p>
<p>周蟒的長處是在於發揮 "完全相容 Python 程式語言" 的中文程式語言的優點, 所有語法,關鍵詞都依照 Python 的風格。學習周蟒後要橋接到 Python 語言相當容易。</p>
<p>由設計目的跟設計哲學產生出的周蟒,其寫作特色有:</p>
<p>* Python 程式語言 + 中文支援
* 互動式直譯器
* 白話關鍵詞
* 縮排與關鍵詞前後留白
* 使用數學運算符號, 英文標點</p>
</blockquote>
<p>最简单的例子:
印出 '哈啰, 世界'
这就是中文的HelloWorld,是不是看起来很有亲切感。这句和以下等价:
print '哈囉, 世界'
函数定义也是中文的:</p>
<p>#!/usr/bin/env zhpy
# 檔名: function1.py
定義 說哈囉():
印出 '哈囉, 世界!' # 函式主體
說哈囉() # 呼叫函式</p>
<p>想了解多一点可以看看这个“咬一口周蟒中文程式語言”教程,看看不会花很多时间,大概一个下午就可以了。</p>
<p>安装文件:
http://zhpy.googlecode.com/files/zhpy-1.1.zip</p>
<p>api文档:
http://zhpy.googlecode.com/files/apidocs1.1.zip</p>
TDD by Example书中的例子Ruby版
2007-10-03T00:00:00Z
https://thekaiway.com/2007/10/03/tddbyex%E4%B9%A6%E4%B8%AD%E8%B5%84%E9%87%91%E4%BE%8B%E5%AD%90ruby%E5%AE%8C%E6%95%B4%E9%87%8D%E7%8E%B0/
<p>早上看了<a HREF="http://www.ruby-lang.org.cn/forums/thread-1491-1-4.html">blackanger写的TDD by Ex这本书里的资金例子</a>,自己也想写一写。和他不同,我是全过程详细写出来。第一次用Ruby写代码,第一次用Ruby的Unit框架,而且下午睡醒后迷迷糊糊写的,可能有很多错误,请多多指正。</p>
<p>第一次迭代后的代码(简单的TDD代码)</p>
<p># file tc_doller.rb
$:.unshift File.join(File.dirname(<strong>FILE</strong>), "..", "src")
require 'test/unit'
require 'dollar'
class TestMoney < Test::Unit::TestCase
def testMultiplication
five = Dollar.new(5)
five.times(2)
assert_equal(10, five.amount)
end
end</p>
<p># file doller.rb
class Dollar
def initialize(amount)
@amount = amount
end
def times(multiplier)
@amount = @amount * multiplier
end
def amount
return @amount
end
end</p>
<p>第二次迭代后的代码</p>
<p># file tc_doller.rb
$:.unshift File.join(File.dirname(<strong>FILE</strong>), "..", "src")
require 'test/unit'
require 'dollar'
class TestMoney < Test::Unit::TestCase
def testMultiplication
five = Dollar.new(5)
product = five.times(2)
assert_equal(10, product.amount)
product = five.times(3)
assert_equal(15, product.amount)
end
end</p>
<p># file doller.rb
class Dollar
attr_reader :amount
protected :amount
def initialize(amount)
@amount = amount
end</p>
<p>def times(multiplier)
return Dollar.new @amount * multiplier
end
end</p>
<p>第三,四次迭代后的代码(添加了相等性测试,刚好Ruby中的equal?和==的语意和Java相反)</p>
<p># file tc_doller.rb
$:.unshift File.join(File.dirname(<strong>FILE</strong>), "..", "src")
require 'test/unit'
require 'dollar'</p>
<p>class TestMoney < Test::Unit::TestCase
def testMultiplication
five = Dollar.new(5)
product = five.times(2)
assert_equal(10, product.amount)
product = five.times(3)
assert_equal(15, product.amount)
end
def testEquality
assert(Dollar.new(5) == Dollar.new(5))
assert(Dollar.new(5) != Dollar.new(6))
end
end</p>
<p># file doller.rb
class Dollar
attr_reader :amount
protected :amount
def initialize(amount)
@amount = amount
end
def times(multiplier)
return Dollar.new @amount * multiplier
end
def ==(obj)
return obj.amount == @amount
end
end</p>
<p>第五,六,七次迭代后的代码(短命的Franc对象登场)</p>
<p># file tc_doller.rb
$:.unshift File.join(File.dirname(<strong>FILE</strong>), "..", "src")
require 'test/unit'
require 'money'
require 'dollar'
require 'franc'</p>
<p>class TestMoney < Test::Unit::TestCase
def testMultiplication
five = Dollar.new(5)
assert_equal(Dollar.new(10), five.times(2))
assert_equal(Dollar.new(15), five.times(3))
end</p>
<p>def testEquality
assert(Dollar.new(5) == Dollar.new(5))
assert(Dollar.new(5) != Dollar.new(6))
assert(Franc.new(5) == Franc.new(5))
assert(Franc.new(5) != Franc.new(6))
assert(Franc.new(5) != Dollar.new(5))
end</p>
<p>def testFrancMultiplication
five = Franc.new(5)
assert_equal(Franc.new(10), five.times(2))
assert_equal(Franc.new(15), five.times(3))
end
end</p>
<p># file doller.rb
class Dollar < Money
def initialize(amount)
super(amount)
end
def times(multiplier)
return Dollar.new(@amount * multiplier)
end
end</p>
<p># file franc.rb
class Franc < Money
def initialize(amount)
super(amount)
end
def times(multiplier)
return Franc.new(@amount * multiplier)
end
end</p>
<p># file money.rb
class Money
attr_reader :amount
protected :amount
def initialize(amount)
@amount = amount
end
def ==(obj)
return obj.amount.equal?(@amount)
end
end</p>
<p>第八,九,十,十一次迭代(消除子类,很巧妙的一步)</p>
<p># file tc_doller.rb
$:.unshift File.join(File.dirname(<strong>FILE</strong>), "..", "src")
require 'test/unit'
require 'money'</p>
<p>class TestMoney < Test::Unit::TestCase
def testMultiplication
five = Money.dollar(5)
assert_equal(Money.dollar(10), five.times(2))
assert_equal(Money.dollar(15), five.times(3))
end</p>
<p>def testFrancMultiplication
five = Money.franc(5)
assert_equal(Money.franc(10), five.times(2))
assert_equal(Money.franc(15), five.times(3))
end</p>
<p>def testEquality
assert(Money.dollar(5) == Money.dollar(5))
assert(Money.dollar(5) != Money.dollar(6))
assert(Money.franc(5) != Money.dollar(5))
end</p>
<p>def testCurrency
assert_equal("USD", Money.dollar(1).currency)
assert_equal("CHF", Money.franc(1).currency)
end
end</p>
<p># file money.rb
class Money
attr_reader :amount, :currency
protected :amount</p>
<p>def initialize(amount, currency)
@amount = amount
@currency = currency
end</p>
<p>def self.dollar(amount)
return Money.new(amount, "USD")
end</p>
<p>def self.franc(amount)
return Money.new(amount, "CHF")
end</p>
<p>def times(multiplier)
return Money.new(@amount*multiplier, @currency)
end</p>
<p>def plus(addend)
return Money.new(@amount + addend.amount, currency)
end</p>
<p>def ==(obj)
return obj.amount.equal?(@amount) && (obj.currency == @currency)
end
end</p>
<p>最后的一部分
最后是完成不同货币之间的计算,引入了两个新的对象负责处理汇率的Bank和货币相加的Sum对象。</p>
<p>由于Ruby的动态性无须让Sum和Money实现同一接口,反正原书中的Expression对象也是为了Sum和Money对象可以通信。或许可以把Sum简化掉,有空时再想想。</p>
<p># file tc_doller.rb
$:.unshift File.join(File.dirname(<strong>FILE</strong>), "..", "src")
require 'test/unit'
require 'money'
require 'bank'
require 'sum'</p>
<p>class TestMoney < Test::Unit::TestCase
def testDollarMultiplication
five = Money.dollar(5)
assert_equal(Money.dollar(10), five.times(2))
assert_equal(Money.dollar(15), five.times(3))
end</p>
<p>def testFrancMultiplication
five = Money.franc(5)
assert_equal(Money.franc(10), five.times(2))
assert_equal(Money.franc(15), five.times(3))
end</p>
<p>def testEquality
assert(Object.new != Money.dollar(1))
assert(Money.dollar(5) == Money.dollar(5))
assert(Money.dollar(5) != Money.dollar(6))
assert(Money.franc(5) != Money.dollar(5))
end</p>
<p>def testCurrency
assert_equal("USD", Money.dollar(1).currency)
assert_equal("CHF", Money.franc(1).currency)
end</p>
<p>def testSimpleAddition
five = Money.dollar(5)
sum = five.plus(Money.dollar(5))
bank = Bank.new()
reduced = bank.reduce(sum, "USD")
assert_equal(Money.dollar(10), reduced)
end</p>
<p>def testReduceSum
sum = Sum.new(Money.dollar(3), Money.dollar(4))
bank = Bank.new()
result = bank.reduce(sum, "USD")
assert_equal(Money.dollar(7), result)
end</p>
<p>def testReduceMoney
bank = Bank.new
result = bank.reduce(Money.dollar(1), "USD")
assert_equal(Money.dollar(1), result)
end</p>
<p>def testReduceMoneyDiffentCurrency
bank = Bank.new
bank.addRate("CHF", "USD", 2)
result = bank.reduce(Money.franc(2), "USD")
assert_equal(Money.dollar(1), result)
end</p>
<p>def testIndentityRate
bank = Bank.new
bank.addRate("USD", "CHF", 0.5)
assert_equal(1, bank.rate("USD", "USD"))
assert_equal(0.5, bank.rate("USD", "CHF"))
assert_equal(2, bank.rate("CHF", "USD"))
end</p>
<p>def testMixedAddition
fiveBucks = Money.dollar(5)
tenFrancs = Money.franc(10)
bank = Bank.new
bank.addRate("CHF", "USD", 2)
result = bank.reduce(fiveBucks.plus(tenFrancs), "USD")
assert_equal(Money.dollar(10), result)
end</p>
<p>def testSumTimes
fiveBucks = Money.dollar(5)
tenFrancs = Money.franc(10)
bank = Bank.new
bank.addRate("CHF", "USD", 2)
sum = Sum.new(fiveBucks, tenFrancs).times(2)
result = bank.reduce(sum, "USD")
assert_equal(Money.dollar(20), result)
end
end</p>
<p># file money.rb
class Money
attr_reader :amount, :currency</p>
<p>def initialize(amount, currency)
@amount = amount
@currency = currency
end</p>
<p>def self.dollar(amount)
return Money.new(amount, "USD")
end</p>
<p>def self.franc(amount)
return Money.new(amount, "CHF")
end</p>
<p>def times(multiplier)
return Money.new(@amount*multiplier, @currency)
end</p>
<p>def plus(addend)
return Sum.new(self, addend)
end</p>
<p>def ==(obj)
return obj.amount.equal?(@amount) && (obj.currency == @currency)
end</p>
<p>def reduce(bank, to)
rate = bank.rate(@currency, to)
return Money.new(@amount / rate, to)
end
end</p>
<p># file bank.rb
class Bank
attr_accessor :rates
@@rates = {}</p>
<p>def reduce(source, to)
return source.reduce(self, to)
end</p>
<p>def addRate(from, to, rate)
@@rates["#{from}-#{to}"] = rate
@@rates["#{to}-#{from}"] = 1 / rate
end</p>
<p>def rate(from, to)
return 1 if(from == to)
return @@rates["#{from}-#{to}"]
end
end</p>
<p># file sum.rb
class Sum
attr_reader :augend, :addend</p>
<p>def initialize(augend, addend)
@augend = augend
@addend = addend
end</p>
<p>def reduce(bank, to)
amount = augend.reduce(bank, to).amount + addend.reduce(bank, to).amount
return Money.new(amount, to)
end</p>
<p>def times(multiplier)
return Sum.new(augend.times(multiplier), addend.times(multiplier))
end
end</p>
Ubuntu一星期用后体验
2007-09-10T00:00:00Z
https://thekaiway.com/2007/09/10/%E7%94%A8ubuntu%E4%B8%80%E4%B8%AA%E5%A4%9A%E6%98%9F%E6%9C%9F%E6%84%9F%E5%8F%97/
<p>用了Ubuntu7.04一个多星期了觉得挺很不错,很多方面比在windows下好很多。方便的硬盘安装。一开始是在家里的电脑安装的,之后把硬盘拿到学校的电脑一插上去就可以用了,只需在命令行界面配置显卡和校园网就OK了。Ubuntu的硬件的兼容性真是没得说,还有人还制作了一个可以放在U盘的精简版本。Linux的整个系统加上所有的常用软件比windows加上所有常用软件的磁盘占用小很多。</p>
<p>记得刚刚接触电脑的时候就用了一段时间的RH9,觉得很神秘很帅。后来来到学校后发现校园网上不到网,用xrgsu要么掉线要么上不到,试了sesu和fc 都是这样。因为万恶的锐捷又不能用虚拟机上网。这次是因为有个师兄用ubuntu7.04成功的上到了所以我又用回了Linux,用回了久违的VI。我不知为什么学了vi和emacs两个编辑器,但是一直都用不惯emacs,vi就用得很顺手。Linux下的编辑器就好在可以双手不离开键盘,不用去用到鼠标,不过太久没有用了,时不时手还是会想去摸鼠标,然后想一想手又缩回来按快捷键。</p>
<p>Ubuntu的中文社区很活跃,中文的资料又多得不得了,一般有什么问题搜索一下就可以查找到。</p>
<p>Ubuntu下用得最爽的是FireFox,好像没有windows下的内存泄漏现象。Linux下的BT软件就不怎样,还是用wine+比特精灵比较好。相对于bt,linux下的电驴软件就很厉害了,MLDonkey很快,最高能过1M/s。</p>
<p>下载安装软件,Ubuntu继承了Debian系的apt-get系列的方便,很大得方便了我这类懒人。只要一个命令就可以简单地在非图形界面安装软件,而且自动安装软件源中最新的版本,一并安装配置。只是把软件安装到哪里要自己去查找,有时是挺麻烦的,比如在安装后要修改配置文件的时候,特别是安装时文件分得很散,在配置radrails时要求输入rails和rake的路径。不过用了一段时间后熟悉了目录的配置之后就要找程序文件就简单多了,程序文件一般放在/usr/bin下。</p>
<p>不过有时一些软件还是自己手动安装好。第一有个权限问题,我的电脑还是主要用来做开发和学习的,而且又是我自己一个人用,对于Linux系统一向来的用户安全机制对我来说有点多余。自动安装的一些软件,有时要设置权限,比较麻烦。第二就是版本问题,软件源里的软件虽说都是稳定版本,但是我有时想要新一点的版本,比如Eclilpse3.3,NetBeans6。</p>
<p>在开发时最大的好处就是编码问题在 Linux下对utf-8的支持很好。还有好像一些ide在Linux下好像速度快一点,还有ruby也是,反应快了很多。正在试着Gvim+ rails.vim的感觉。不过还是习惯eclipse。Linux下的软件,要么是gtk的,要么是Qt,要么java的,感觉上至少比win32程序用起来觉得舒服一点,完全是心理作用。</p>
<p>最后有两件事让我不爽,一个是特效开不到,我的显卡是Ati 的X1650,万恶的ati官方驱动不支持XGL,开源驱动又不支持X1650。另一件事是我用eva和QQ2006上了几天之后登录的时候竟然说我“您的IP不能用低版本的QQ”,真是万恶的腾讯。我最后找了飘云版的QQ2007(因为不带键盘锁)才能用wine跑QQ2007。用wine+qq的时候发现打字那个提示框很快会不见,我用的是fcitx,还有我不能发起语音请求,发起时qq会崩溃,别人发起我接受就正常。</p>
<p>Ubuntu好好玩。</p>
这个暑假
2007-08-10T00:00:00Z
https://thekaiway.com/2007/08/10/%E8%BF%99%E4%B8%AA%E6%9A%91%E5%81%87/
<p>这个暑假过了一大半了,过得比较郁闷。</p>
<p>今天是暑假第一次在家上网,因为家里电脑坏了,主板拿去保修,修了20天后拿回来发现坏得更严重,又要不知道修到什么时候。(体验了华硕的售后实在不敢恭维)只好上个星期叫同学在网上帮我买了块二手主板,现在终于有电脑用一下。</p>
<p>暑假前已经计划好了要做什么,就是要把这个学期还没有做好的几个应用做好它,把picocontainer的1.0版的代码分析和文档汉化做完(在期末考试前因为无聊所以就做了这个事),还有再写一些有趣的代码,比如一个简单的XML仓库。</p>
<p>读书的计划提前完成了,看完了《领域模型驱动》,《web开发敏捷之道》(新鲜热辣),《Ruby for rails》。看了一半《对象设计》,还有把《重构》又看了一遍。</p>
<p>还好带了一些英语资料,手机里也存了一些英语的对话,每天背一篇短文,没事练习听着英语大声念几句。感觉还好。</p>
<p>今天看了一下敏捷大会的消息,又看了一下Thoughtworks里的强人的博客,觉得毕业后去那里的希望很渺茫。英语又差,技术和那些强人差了几个数量级,看来要更加更加努力。</p>