<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>dotnil (小格鸡)</title>
    <link>https://ruby-china.org/dotnil</link>
    <description>mux.alimama.com/join</description>
    <language>en-us</language>
    <item>
      <title>[杭州] 阿里妈妈广告技术部前端招新</title>
      <description>&lt;p&gt;职位描述&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;参与部门所负责的 Web 应用前后端开发，比如&lt;a href="https://chuangyi.taobao.com" rel="nofollow" target="_blank" title=""&gt;阿里妈妈创意中心&lt;/a&gt;、&lt;a href="https://iconfont.cn" rel="nofollow" target="_blank" title=""&gt;iconfont&lt;/a&gt; 等等；&lt;/li&gt;
&lt;li&gt;参与广告/联盟业务中的创意、bp 系统、中后台等功能或者应用的前端开发，相关介绍详见&lt;a href="https://www.alimama.com/index.htm" rel="nofollow" target="_blank" title=""&gt;阿里妈妈官网&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;参与人工智能前沿领域与广告业务相结合的业务场景探索；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;职位要求&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;大学本科及以上学历，一年以上工作经验；&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;专业技能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;必备：熟悉 Web 开发基础知识，包括但不限于 HTML、CSS、JavaScript、DOM API、Browser Object Model、Web Assembly 等内容；&lt;/li&gt;
&lt;li&gt;必备：了解 Node.js 开发基础知识，具备相关应用开发经验；&lt;/li&gt;
&lt;li&gt;必备：熟悉计算机基础知识，了解进程、网络传输模型、关系型数据库等方面；&lt;/li&gt;
&lt;li&gt;加分：熟悉 Canvas、WebGL、WebGPU 等 Web 图形应用开发，有相关工具研发经验；&lt;/li&gt;
&lt;li&gt;加分：有相似领域的开源项目，是相关项目的核心开发者；&lt;/li&gt;
&lt;li&gt;加分：有平面设计、视频剪辑等方面的工具使用或者开发经验；&lt;/li&gt;
&lt;li&gt;加分：熟悉 Rust，有相关项目开发经验；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;能力素质：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;关注业界前沿技术，持续学习、有探索精神；&lt;/li&gt;
&lt;li&gt;有高度的责任心及良好的团队协作精神；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;投递方式
邮箱：yicai.cyj@alibaba-inc.com&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Tue, 23 May 2023 14:27:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/43105</link>
      <guid>https://ruby-china.org/topics/43105</guid>
    </item>
    <item>
      <title>用 Capistrano 部署 Rabel </title>
      <description>&lt;p&gt;&lt;a href="https://gist.github.com/dotnil/5181417" rel="nofollow" target="_blank"&gt;https://gist.github.com/dotnil/5181417&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# encoding: utf-8&lt;/span&gt;

&lt;span class="c1"&gt;# config/deploy.rb&lt;/span&gt;
&lt;span class="c1"&gt;# &lt;/span&gt;
&lt;span class="c1"&gt;#  - 使用 Unicorn&lt;/span&gt;
&lt;span class="c1"&gt;#  - 链接 rabel 的配置文件和用户上传内容&lt;/span&gt;
&lt;span class="c1"&gt;#  - 使用 RVM&lt;/span&gt;

&lt;span class="c1"&gt;# Add RVM's lib directory to the load path.&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rvm/capistrano"&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rvm_ruby_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1.9.3-p392'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rvm_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;

&lt;span class="c1"&gt;# http://gembundler.com/deploying.html&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'bundler/capistrano'&lt;/span&gt;

&lt;span class="c1"&gt;# set this to keep from missing any password prompts&lt;/span&gt;
&lt;span class="n"&gt;default_run_options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:pty&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:ssh_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:forward_agent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rabel'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:scm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'git'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:scm_verbose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:deploy_via&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:remote_cache&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:deploy_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/var/www/wenyi.me"&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:unicorn_pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/pids/unicorn.pid"&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"git@github.com:daqing/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'BRANCH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'v2.0'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:use_sudo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wenyi.me'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;

&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:web&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:primary&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:deploy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"启动服务"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:roles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; bundle exec unicorn_rails --port 3800 --env &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --daemonize"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"停止服务"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:roles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"kill `cat &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;unicorn_pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"重新加载服务"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:reload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:roles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"kill -s USR2 `cat &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;unicorn_pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"重新启动服务"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:roles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;stop&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'收拾房间，链接文件'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:housekeeping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:roles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"rm -f &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/initializers/siteconf.rb"&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"ln -s &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/siteconf.rb &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/initializers/siteconf.rb"&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"rm -f &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/database.yml"&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"ln -s &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/database.yml &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/database.yml"&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"rm -rf &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/settings.yml"&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"ln -s &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/settings.yml &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/settings.yml"&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"rm -rf &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/public/uploads"&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"ln -s &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/uploads &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/public/uploads"&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; RAILS_ENV=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bundle exec rake assets:clean"&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; RAILS_ENV=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bundle exec rake assets:precompile"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:create_symlink'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deploy:housekeeping'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deploy:migrate'&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deploy:cleanup'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="/daqing" class="user-mention" title="@daqing"&gt;&lt;i&gt;@&lt;/i&gt;daqing&lt;/a&gt;&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Sun, 17 Mar 2013 21:10:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/9511</link>
      <guid>https://ruby-china.org/topics/9511</guid>
    </item>
    <item>
      <title>翻译：Gitolite 工作原理</title>
      <description>&lt;p&gt;&lt;a href="/saito" class="user-mention" title="@saito"&gt;&lt;i&gt;@&lt;/i&gt;saito&lt;/a&gt; 同学在某次杭州 Ruby Tuesday 分享过
&lt;a href="https://github.com/sitaramc/gitolite" rel="nofollow" target="_blank" title=""&gt;Gitolite&lt;/a&gt;
的工作原理，他也是开发 GitLab 的组织 &lt;a href="https://github.com/gitlabhq" rel="nofollow" target="_blank" title=""&gt;gitlabhq&lt;/a&gt; 的成员。
我当时理解的并不透彻，算是一知半解；恰好最近公司也搭了 GitLab 服务，但因为各种安全方面的顾虑，有些限制，让人很摸不着头脑，于是翻出 gitolite 的文档 看了一遍。挑几篇翻译一下。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sitaramc.github.com/gitolite/how.html" rel="nofollow" target="_blank" title=""&gt;How does gitolite work?&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="“Gitolite 仓库”长什么样？"&gt;“Gitolite 仓库”长什么样？&lt;/h3&gt;
&lt;p&gt;Gitolite 仓库与普通 Git 服务器上裸（bare）仓库看起来没什么两样。仓库里会有一些额外的文件，
文件名前缀是 &lt;code&gt;gl-&lt;/code&gt;，同时在 &lt;code&gt;hooks&lt;/code&gt; 子目录，还有个更新钩子（update hook），
在很大程度上，仅此而已。&lt;/p&gt;

&lt;p&gt;换句话说，你可以把 Gitolite 维护的仓库当做普通裸仓库一般对待，只要你别碰上面说的这些文件。&lt;/p&gt;
&lt;h3 id="行文约定"&gt;行文约定&lt;/h3&gt;
&lt;p&gt;--//--&amp;gt;    网络连接
    ------&amp;gt;    exec() 或者 system() 调用
    --??--&amp;gt;    有条件的 exec() 或者 system() 调用&lt;/p&gt;

&lt;p&gt;“有条件的”的意思是，调用者会先做些判断，并依据其结果决定是调用方法，还是中止。&lt;/p&gt;
&lt;h3 id="通过纯 SSH 连接的 Git"&gt;通过纯 SSH 连接的 Git&lt;/h3&gt;
&lt;p&gt;如果你用过 rsync 或者其他类似需要使用 ssh 到远程主机的程序，那其实你已经懂得了通过 SSH
连接 Git 的基本原理。&lt;/p&gt;

&lt;p&gt;具体发生的细节如下：&lt;/p&gt;

&lt;p&gt;# 读取操作
    git clone ------&amp;gt; ssh --//--&amp;gt; sshd ------&amp;gt; git-upload-pack
    git fetch ------&amp;gt; ssh --//--&amp;gt; sshd ------&amp;gt; git-upload-pack&lt;/p&gt;

&lt;p&gt;# 写入操作
    git push  ------&amp;gt; ssh --//--&amp;gt; sshd ------&amp;gt; git-receive-pack&lt;/p&gt;

&lt;p&gt;本地 git 客户端只不过是执行了类似
&lt;code&gt;ssh git@host git-receive-pack 'path/to-reponame.git'&lt;/code&gt; 的命令（上例中的推送），
等连接建立之后，本地的 &lt;code&gt;git-push&lt;/code&gt; 与远端的 &lt;code&gt;git-receive-pack&lt;/code&gt; 通过此连接交流。&lt;/p&gt;
&lt;h3 id="通过 Gitolite 连接的 Git"&gt;通过 Gitolite 连接的 Git&lt;/h3&gt;
&lt;p&gt;安装 Gitolite 只是在 &lt;code&gt;sshd&lt;/code&gt; 与 &lt;code&gt;git-receive-pack&lt;/code&gt;（或者 &lt;code&gt;git-upload-pack&lt;/code&gt;，
读操作的时候）之间加了一层，以检查是允许此操作，还是中止掉。&lt;/p&gt;

&lt;p&gt;以推送为例，真个过程如下：&lt;/p&gt;

&lt;p&gt;git push ------&amp;gt; ssh --//--&amp;gt; sshd ------&amp;gt; gitolite-shell --??--&amp;gt; git-receive-pack&lt;/p&gt;

&lt;p&gt;注意&lt;strong&gt;客户端&lt;/strong&gt; git 一点都没变；事实上，它都不会知道这世上竟有 Gitolite 插了一脚。&lt;/p&gt;

&lt;p&gt;下面是我们搞定服务器如上行为的方法：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;我们让 sshd 调用 &lt;code&gt;gitolite-shell&lt;/code&gt;，即便客户端 git 要求调用的是 &lt;code&gt;git-receive-pack&lt;/code&gt;
（或者 &lt;code&gt;git-upload-pack&lt;/code&gt;，读取时）。&lt;/li&gt;
&lt;li&gt;我们还让 sshd 给 &lt;code&gt;gitolite-shell&lt;/code&gt; 提供一个参数，即该 Gitolite 用户的名称
（注意这是个虚拟的用户，并非实质的 UNIX 用户）。&lt;/li&gt;
&lt;li&gt;最后，sshd 自己会把原始的命令，即客户端 git 所要执行的
&lt;code&gt;git-receive-pack 'path/to/reponame.git&lt;/code&gt;），放到一个特殊的环境变量里面，
叫做 &lt;code&gt;SSH_ORIGINAL_COMMAND&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;（所有这些都是通过 ssh 特性搞定的；更多细节请阅读
&lt;a href="http://sitaramc.github.com/gitolite/glssh.html" rel="nofollow" target="_blank" title=""&gt;gitolite and ssh&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="gitolite-shell"&gt;gitolite-shell&lt;/h3&gt;
&lt;p&gt;因此 &lt;code&gt;gitolite-shell&lt;/code&gt; 有两条信息。通过参数 1，它知道了用户名，通过该环境变量，
它知道了是要哪个仓库，和要做什么操作（也就是，是要读取还是写入）。&lt;/p&gt;

&lt;p&gt;其时，它检查用户的存取操作，决定是否允许。&lt;/p&gt;

&lt;p&gt;（详细内容可以看 &lt;a href="http://sitaramc.github.com/gitolite/rules.html" rel="nofollow" target="_blank" title=""&gt;rules&lt;/a&gt; 页面，
尤其是 when are rules checked 与 how are the rules matched 章节）&lt;/p&gt;

&lt;p&gt;对读取操作而言，Gitolite 的介入到此结束。如果允许，&lt;code&gt;git-upload-pack&lt;/code&gt; 跑起来，干它的活，
然后退出。&lt;/p&gt;
&lt;h3 id="更新钩子（update hook）"&gt;更新钩子（update hook）&lt;/h3&gt;
&lt;p&gt;对写操作而言，如果有更新钩子的话，git 在更新索引之前会先执行它。所有 Gitolite
“精美绝伦的读写控制”即通过此钩子发生，每个它维护的仓库都有装。&lt;/p&gt;

&lt;p&gt;当 git 调用这个更新钩子时，它提供了三个重要的信息作为参数。我们已知的（用户、仓库），
加上这三条信息（本更新的索引名称，旧的 SHA，和新的 SHA），
告诉了我们实现任何你想要的读写控制所需要的信息，比如“只有爱丽丝与呆伯特才能推送到主分支”，
“瓦利（Wally）只能推送名字以 &lt;code&gt;wally/&lt;/code&gt; 开头的分支，或者”只有呆伯特才能推送改动到
&lt;code&gt;license.txt&lt;/code&gt; 文件“，等等。&lt;/p&gt;

&lt;p&gt;（再次，详细规则在 &lt;a href="http://sitaramc.github.com/gitolite/rules.html" rel="nofollow" target="_blank" title=""&gt;rules&lt;/a&gt; 页面）&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;周六翻译的，放在&lt;a href="http://cyj.me" rel="nofollow" target="_blank" title=""&gt;我的博客&lt;/a&gt;&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Mon, 28 Jan 2013 09:38:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/8423</link>
      <guid>https://ruby-china.org/topics/8423</guid>
    </item>
    <item>
      <title>有几篇帖子值得恢复啊</title>
      <description>&lt;p&gt;难道你们还没学 Haskell &lt;/p&gt;

&lt;p&gt;&lt;a href="http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece76310569036524380156d8d8b492cc3933fce240e5c013ba1e076630d01d8c77a7344f2090ae5ae71296e5e60e08cc8f9108efacb7f71d6&amp;amp;p=9d65e70385cc40aa08e2977c075ecc24&amp;amp;newp=8b2a944097dd0afd08e2977c075e9f755c5bc5356087bd157dded256d836&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=1" rel="nofollow" target="_blank"&gt;http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece76310569036524380156d8d8b492cc3933fce240e5c013ba1e076630d01d8c77a7344f2090ae5ae71296e5e60e08cc8f9108efacb7f71d6&amp;amp;p=9d65e70385cc40aa08e2977c075ecc24&amp;amp;newp=8b2a944097dd0afd08e2977c075e9f755c5bc5356087bd157dded256d836&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;屠龙之技&lt;/p&gt;

&lt;p&gt;&lt;a href="http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece76310569036524380156d8d8b492cc3933fce240e5c013ba1e076630d0ed1cf7e7344f2090ae5ae71296e5e60e08cc8f9108efacb7f71d6&amp;amp;p=98759a4fd59413fa09be9b7f074c81&amp;amp;newp=9a70841a86cc4bac0eb4c7710c1c8a231611d63f6cbad01f7482ca&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=6" rel="nofollow" target="_blank"&gt;http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece76310569036524380156d8d8b492cc3933fce240e5c013ba1e076630d0ed1cf7e7344f2090ae5ae71296e5e60e08cc8f9108efacb7f71d6&amp;amp;p=98759a4fd59413fa09be9b7f074c81&amp;amp;newp=9a70841a86cc4bac0eb4c7710c1c8a231611d63f6cbad01f7482ca&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=6&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;系统批量吐槽一下各种编辑器&lt;/p&gt;

&lt;p&gt;&lt;a href="http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece76310569036524380156d8d8b492cc3933fce240e5c013ba1e076630d01d9c77d7344f2090ae5ae71296e5e60e08cc8f9108efacb7f71d6&amp;amp;p=882a9045cd8614f10be2957b425390&amp;amp;newp=c26fd70785cc40aa07aec47108168c231611d63f6cbad3107f8bd0&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=7" rel="nofollow" target="_blank"&gt;http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece76310569036524380156d8d8b492cc3933fce240e5c013ba1e076630d01d9c77d7344f2090ae5ae71296e5e60e08cc8f9108efacb7f71d6&amp;amp;p=882a9045cd8614f10be2957b425390&amp;amp;newp=c26fd70785cc40aa07aec47108168c231611d63f6cbad3107f8bd0&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=7&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;看起来我好像 &lt;a href="/luikore" class="user-mention" title="@luikore"&gt;&lt;i&gt;@&lt;/i&gt;luikore&lt;/a&gt; 的脑残粉&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;难道你们还没学 Clojure&lt;/p&gt;

&lt;p&gt;&lt;a href="http://cache.baidu.com/c?m=9d78d513d99d06f401b0d4291a16a63c4a15d9346289c4523f8a9c12d52219564615fea66765404fc4c50a365bf4150ffdf0412f77563df1c68ad31b9ca6972d2a82&amp;amp;p=c97ac64ad5c457ff57ee9778525d8520&amp;amp;newp=8b2a964280d911a05bee922b5657907a5c5bc13451d4d31f7e8289&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=9" rel="nofollow" target="_blank"&gt;http://cache.baidu.com/c?m=9d78d513d99d06f401b0d4291a16a63c4a15d9346289c4523f8a9c12d52219564615fea66765404fc4c50a365bf4150ffdf0412f77563df1c68ad31b9ca6972d2a82&amp;amp;p=c97ac64ad5c457ff57ee9778525d8520&amp;amp;newp=8b2a964280d911a05bee922b5657907a5c5bc13451d4d31f7e8289&amp;amp;user=baidu&amp;amp;fm=sc&amp;amp;query=luikore+haskell&amp;amp;qid=&amp;amp;p1=9&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;以上帖子及后续讨论是我印象中觉得比较精彩的&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Mon, 07 Jan 2013 16:25:37 +0800</pubDate>
      <link>https://ruby-china.org/topics/7844</link>
      <guid>https://ruby-china.org/topics/7844</guid>
    </item>
    <item>
      <title>发现有张 RubyConf China 的照片拍得还不错诶</title>
      <description>&lt;p&gt;&lt;img src="//l.ruby-china.com/photo/0425dcbb848396bbee8749a7016dfb24.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/saberma" class="user-mention" title="@saberma"&gt;&lt;i&gt;@&lt;/i&gt;saberma&lt;/a&gt;&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Tue, 04 Dec 2012 21:10:37 +0800</pubDate>
      <link>https://ruby-china.org/topics/7301</link>
      <guid>https://ruby-china.org/topics/7301</guid>
    </item>
    <item>
      <title>我也拍了一些照片</title>
      <description>&lt;p&gt;&lt;a href="http://www.flickr.com/photos/dotnil/sets/72157632044516691/" rel="nofollow" target="_blank"&gt;http://www.flickr.com/photos/dotnil/sets/72157632044516691/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;套头、位置偏等等原因，照片差强人意，不过还是拍到了 &lt;a href="/saberma" class="user-mention" title="@saberma"&gt;&lt;i&gt;@&lt;/i&gt;saberma&lt;/a&gt; &lt;a href="/saito" class="user-mention" title="@saito"&gt;&lt;i&gt;@&lt;/i&gt;saito&lt;/a&gt; 等&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Mon, 19 Nov 2012 16:48:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/6907</link>
      <guid>https://ruby-china.org/topics/6907</guid>
    </item>
    <item>
      <title>上海归来，写点感受，及读音的重要性</title>
      <description>&lt;p&gt;有幸参加 RubyConf China，看到许多大牛，学到许多知识，特此小结，顺带吐槽。&lt;/p&gt;

&lt;p&gt;10 月份的时候，在同一个会场，这里还办过 HuJS &lt;a href="http://www.hujs.org/" rel="nofollow" target="_blank"&gt;http://www.hujs.org/&lt;/a&gt; ，不知道有没有人跟我一样蛋疼，两个都参加了的。与彼大会相比，RubyConf 有好有坏。午餐、茶歇比 HuJS 的好太多，披萨和吉野家的外卖都如那位负责饮食的志愿者所说，非常美味，茶歇时提供的咖啡，也很好，帮我挺过第二天（周六晚上只睡了 5 个小时）。&lt;/p&gt;

&lt;p&gt;坏处也挺明显的，大会没有给所有与会者提供 Wifi；不过话说回来，创智天地的 Wifi 本来就不给力，有连接数限制，而且限得比较低，如果开放连接，也不是所有人都连得上。&lt;/p&gt;

&lt;p&gt;议题与演讲者方面，HuJS 更加国际化一些，但这方面无可厚非，本来就是大陆自己的 RubyConf。&lt;/p&gt;

&lt;p&gt;然后就是两天议题听下来的看法，在说之前，我要有个 disclaimer：跟代码评审的时候讨论代码风格一样，做一个 grammar nazi 或者 pronunciation nazi 是个很囧的事情，争过头了都只能是浪费彼此时间；我自然也无意挑口水或者什么，只是建议：&lt;/p&gt;

&lt;p&gt;在演讲的时候，把一些可能提到的外文词汇念对，是一件很重要的事情。它直接影响到听众对你的观感，一个鲜有纰漏的演说，更容易让听众接受你的观点，不是么？&lt;/p&gt;

&lt;p&gt;同时，Ruby、Rails 本身，本来就很看重程序的可读性，因此比较好的代码风格，通常都会有合适的方法、变量名，这些代码连着一块，都能顺畅阅读的；也正是因为这个初衷，我们才能理解，为何 ActiveSupport 会给 Integer 提供 &lt;code&gt;1.year&lt;/code&gt;、&lt;code&gt;2.years&lt;/code&gt;（注意这里的单复数）这种用法，为何有了 &lt;code&gt;str.blank?&lt;/code&gt; 又会提供 &lt;code&gt;str.present?&lt;/code&gt; ，为何 model 是单数，而 controller 是复数，等等。&lt;/p&gt;

&lt;p&gt;现在你明白为啥 RSpec 会搞成这样，又为啥 &lt;a href="/poshboytl" class="user-mention" title="@poshboytl"&gt;&lt;i&gt;@&lt;/i&gt;poshboytl&lt;/a&gt; 会纠结一下 Test Case 的写法了吧。&lt;/p&gt;

&lt;p&gt;然后，我要开始吐槽啦：&lt;/p&gt;

&lt;p&gt;首先是本次大会的官网 &lt;a href="http://rubyconfchina.org/" rel="nofollow" target="_blank"&gt;http://rubyconfchina.org/&lt;/a&gt; ，得统一一下 RubyConf 这个名头，&lt;code&gt;RubyConf&lt;/code&gt; 这个词是个 branding，在 logo 中不应该中间插入空格写成“Ruby Conf”的；然后 China 算是这个 RubyConf 的区域后缀，参考 RubyConf Agentina，应该与 RubyConf 隔开。所以本次大会官网的 title，RubyConfChina 是个错误的写法。&lt;/p&gt;

&lt;p&gt;当然，如果本意就是要避开 RubyConf 这个牌头（因为授权之类的原因？），那就当我没提哈。&lt;/p&gt;

&lt;p&gt;然后是一些常用词的读法，我听到最多的就是 cache，它读作 cash 而非 catch，我还听到有把 height 读错的。另外，比较常会提及的 gem、数据库、工具的读法，也同样如此，比如 PostgreSQL，&lt;a href="http://en.wikipedia.org/wiki/PostgreSQL" rel="nofollow" target="_blank"&gt;http://en.wikipedia.org/wiki/PostgreSQL&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;恕我不能一一列举，好多听过就忘记了。我知道这种帖子肯定挨骂，所以继续 disclaimer，Ken Thompson 曾被问，如果给你机会再实现一次 UNIX，你会改变什么，他说，我会给 &lt;code&gt;creat&lt;/code&gt; 加上一个 &lt;code&gt;e&lt;/code&gt; 。&lt;a href="http://en.wikiquote.org/wiki/Kenneth_Thompson" rel="nofollow" target="_blank"&gt;http://en.wikiquote.org/wiki/Kenneth_Thompson&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最后，读错单词是最普通不过的错误，我曾把 width 的 i 读作 ai，曾把 roommate 说成 cellmate（Prison Break 看多了）；下回读对就好啦。&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Mon, 19 Nov 2012 11:59:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/6893</link>
      <guid>https://ruby-china.org/topics/6893</guid>
    </item>
    <item>
      <title>我的 Emacs 配置，抛砖引玉</title>
      <description>&lt;p&gt;都在这里：&lt;a href="https://github.com/dotnil/dotemacs" rel="nofollow" target="_blank"&gt;https://github.com/dotnil/dotemacs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;比较常用且喜欢的插件&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;yasnippet&lt;/li&gt;
&lt;li&gt;tabbar&lt;/li&gt;
&lt;li&gt;linum&lt;/li&gt;
&lt;li&gt;emms（貌似没在仓库里）&lt;/li&gt;
&lt;li&gt;js、python、django 等代码高亮之类的东东&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这套配置我一直用到 2011 年初，后来开始搞 Ruby on Rails 之后，因为觉得 rails.vim 更好使，就转用 Vim 了……&lt;/p&gt;

&lt;p&gt;所以我对这俩的立场你们该知道了，哪个好用用哪个，都装都用也无妨&lt;/p&gt;

&lt;p&gt;目前用的是 Sublime Text 2，服务器上还是 Vim  &lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Tue, 10 Jul 2012 09:56:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/4218</link>
      <guid>https://ruby-china.org/topics/4218</guid>
    </item>
    <item>
      <title>method_missing，一个 Ruby 程序员的梦中情人</title>
      <description>&lt;p&gt;小感冒，晕乎乎，翻译一篇文章攒人品，望自己明天生龙活虎&lt;/p&gt;

&lt;p&gt;地址：&lt;a href="http://www.alfajango.com/blog/method_missing-a-rubyists-beautiful-mistress/" rel="nofollow" target="_blank"&gt;http://www.alfajango.com/blog/method_missing-a-rubyists-beautiful-mistress/&lt;/a&gt;
标题：method_missing: 
A Rubyist’s Beautiful Mistress
译者：逸才&lt;/p&gt;

&lt;p&gt;我最近读了些文章（比如&lt;a href="http://rubylearning.com/blog/2010/10/07/do-you-know-rubys-chainsaw-method/" rel="nofollow" target="_blank" title=""&gt;这篇&lt;/a&gt;），宣传在 Ruby 里使用 &lt;code&gt;method_missing&lt;/code&gt; 的。&lt;/p&gt;

&lt;p&gt;很多人都与 &lt;code&gt;method_missing&lt;/code&gt; 干柴烈火，但在并没有小心处理彼此之间的关系。所以，我想来探讨一下这个问题：&lt;/p&gt;

&lt;p&gt;** 我该怎么用 &lt;code&gt;method_missing&lt;/code&gt; **&lt;/p&gt;

&lt;p&gt;什么时候该抵挡 &lt;code&gt;method_missing&lt;/code&gt; 的诱惑&lt;/p&gt;

&lt;p&gt;首先，永远不要在还没花时间考虑你用得够不够好之前，就向 &lt;code&gt;method_missing&lt;/code&gt; 的魅力屈服。你知道，在日常生活中，很少会让你以为的那样亟需 &lt;code&gt;method_missing&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;日常：方法代理&lt;/p&gt;

&lt;p&gt;案例：我需要让这个类能够使用另一个类的方法&lt;/p&gt;

&lt;p&gt;这是我所见过最普遍的使用 &lt;code&gt;method_missing&lt;/code&gt; 的情况。这在 gems 与 Rails 插件里头尤其流行。它的模型类似这样：&lt;/p&gt;

&lt;p&gt;class A
      def hi
        puts "Hi from #{self.class}"
      end
    end&lt;/p&gt;

&lt;p&gt;class B
      def initialize
        &lt;a href="/b" class="user-mention" title="@b"&gt;&lt;i&gt;@&lt;/i&gt;b&lt;/a&gt; = A.new
      end&lt;/p&gt;

&lt;p&gt;def method_missing(method_name, *args, &amp;amp;block)
        &lt;a href="/b.send" class="user-mention" title="@b.send"&gt;&lt;i&gt;@&lt;/i&gt;b.send&lt;/a&gt;(method_name, *args, &amp;amp;block)
      end
    end&lt;/p&gt;

&lt;p&gt;A.new.hi #=&amp;gt; Hi from A
    B.new.hi #=&amp;gt; Hi from A&lt;/p&gt;

&lt;p&gt;如此，B 就拥有了 A 的所有实例方法。但是让我们想想，在调用 &lt;code&gt;@b.hi&lt;/code&gt; 的时候都发生了什么。你的 ruby 环境沿着继承链一路找 &lt;code&gt;hi&lt;/code&gt; 这个方法，到最后，恰恰在丢出个 &lt;code&gt;NoMethodError&lt;/code&gt; 前，它调了 &lt;code&gt;method_missing&lt;/code&gt; 这个方法。&lt;/p&gt;

&lt;p&gt;在上例中，情况并不坏，毕竟这里就两个微不足道的类需要查。但通常，我们是在 Rails 或者其他一些框架的上下文中编程。而你的 Rails 模型继承自 &lt;code&gt;ActiveRecord&lt;/code&gt;，而它又集成自其他一大坨的类，于是现在你就有了一坨高高的堆栈要爬⋯⋯ 在你每次调用 &lt;code&gt;@b.hi&lt;/code&gt; 的时候！&lt;/p&gt;

&lt;p&gt;你的好基友：&lt;code&gt;define_method&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;估计现在你在抱怨，“但是史蒂夫，我需要 &lt;code&gt;method_missing&lt;/code&gt;”我告诉你，别忘了其实除了情妇之外，你还有个忠诚的好基友，叫做 &lt;code&gt;define_method&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;它允许你动态地定义一个方法（顾名思义）。它的伟大之处在于，在它执行过之后（通常在你的类们加载之后），这些方法就存在你的类中了，简单直接。在你创建这些方法的时候，也没有什么继承链需要爬。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;define_method&lt;/code&gt; 很有爱很可靠，并且能够满足你的日常生活。不信我？接着看⋯⋯&lt;/p&gt;

&lt;p&gt;class B
      define_method(:hi) do
        &lt;a href="/b.hi" class="user-mention" title="@b.hi"&gt;&lt;i&gt;@&lt;/i&gt;b.hi&lt;/a&gt;
      end
    end&lt;/p&gt;

&lt;p&gt;“可是我有一大坨方法要定义！”你抱怨&lt;/p&gt;

&lt;p&gt;“没问题！”我卖萌眨眼&lt;/p&gt;

&lt;p&gt;class B
      [:hi, :bye, :achoo, :gesundheit].each do |name|
        define_method(name) do
          &lt;a href="/b.send" class="user-mention" title="@b.send"&gt;&lt;i&gt;@&lt;/i&gt;b.send&lt;/a&gt;(name)
        end
      end
    end&lt;/p&gt;

&lt;p&gt;可是我懒得把它们一个个写出来！&lt;/p&gt;

&lt;p&gt;你有点难搞哦&lt;/p&gt;

&lt;p&gt;class A
      # ... lots of methods in here
    end
    class B
      A.instance_methods.each do |name|
        define_method(name) do
          &lt;a href="/b.send" class="user-mention" title="@b.send"&gt;&lt;i&gt;@&lt;/i&gt;b.send&lt;/a&gt;(name)
        end
      end
    end&lt;/p&gt;

&lt;p&gt;那假如我要定义的方法跟原本的有那么一些些不一样呢？&lt;/p&gt;

&lt;p&gt;容易&lt;/p&gt;

&lt;p&gt;class A
      def hi
        puts "Hi."
      end
    end&lt;/p&gt;

&lt;p&gt;class B
      A.instance_methods.each do |name|
        define_method("what_is_#{name}") do
          if &lt;a href="/b.respond_to" class="user-mention" title="@b.respond_to"&gt;&lt;i&gt;@&lt;/i&gt;b.respond_to&lt;/a&gt;?(name)
            &lt;a href="/b.send" class="user-mention" title="@b.send"&gt;&lt;i&gt;@&lt;/i&gt;b.send&lt;/a&gt;(name)
          else
            false
          end
        end
      end
    end&lt;/p&gt;

&lt;p&gt;B.new.what_is_hi #=&amp;gt; "Hi."
    B.new.what_is_wtf #=&amp;gt; false&lt;/p&gt;

&lt;p&gt;呃，代码看起来不优雅啊&lt;/p&gt;

&lt;p&gt;那就没办法了，凑合得了。如果你想要代码更易读，可以看看我们的 &lt;a href="http://railsmagazine.com/articles/4" rel="nofollow" target="_blank" title=""&gt;ruby delegation library&lt;/a&gt; 和 &lt;a href="http://www.simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate/" rel="nofollow" target="_blank" title=""&gt;Rails ActiveRecord delegation&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;好，我们总结一下，看看 &lt;code&gt;define_method&lt;/code&gt; 的真正威力。&lt;/p&gt;

&lt;p&gt;修改自 ruby-doc.org 上的 &lt;a href="http://ruby-doc.org/core/classes/Module.html#M001654" rel="nofollow" target="_blank" title=""&gt;例子&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;class A
      def fred
        puts "In Fred"
      end
      def create_method(name, &amp;amp;block)
        self.class.send(:define_method, name, &amp;amp;block)
      end
      define_method(:wilma) { puts "Charge it!" }
    end
    class B &amp;lt; A
      define_method(:barney, instance_method(:fred))
    end&lt;/p&gt;

&lt;p&gt;a = B.new
    a.barney                                #=&amp;gt; In Fred
    a.wilma                                 #=&amp;gt; Charge it!
    a.create_method(:betty) { p self.to_s }
    a.betty                                 #=&amp;gt; B&lt;/p&gt;

&lt;p&gt;什么时候用 &lt;code&gt;method_missing&lt;/code&gt;？&lt;/p&gt;

&lt;p&gt;现在你估计在想，总有该用它的时候吧，不然还要它干嘛？没错。&lt;/p&gt;

&lt;p&gt;动态命名的方法（又名，元方法）&lt;/p&gt;

&lt;p&gt;案例：我要依据某种模式提供一组方法。这些方法做的事情顾名思义。我可能从来没有调用过这些可能的方法，但是等我要用的时候，它们必须可用。&lt;/p&gt;

&lt;p&gt;现在才是人话！这其实正是 ActiveRecord 所采用的方式，为你提供那些基于属性的动态构建的查找方法，比如 &lt;code&gt;find_by_login_and_email(user_login, user_email)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;def method_missing(method_id, *arguments, &amp;amp;block)
      if match = DynamicFinderMatch.match(method_id)
        attribute_names = match.attribute_names
        super unless all_attributes_exists?(attribute_names)
        if match.finder?
          # ...you get the point
        end # my OCD makes me unable to omit this
        # ...
      else
        super # this is important, I'll tell you why in a second
      end
    end&lt;/p&gt;

&lt;p&gt;权衡利弊&lt;/p&gt;

&lt;p&gt;当你有一大堆元方法要定义，又不一定用得到的时候，&lt;code&gt;method_missing&lt;/code&gt; 是个完美的折衷。&lt;/p&gt;

&lt;p&gt;想想 ActiveRecord 中基于属性的查找方法。要用 &lt;code&gt;define_method&lt;/code&gt; 从头到脚定义这些方法，ActiveRecord 需要检查每个模型的表中所有的字段，并为每个可能的字段组合方式都定义方法。&lt;/p&gt;

&lt;p&gt;find_by_email
    find_by_login
    find_by_name
    find_by_id
    find_by_email_and_login
    find_by_email_and_login_and_name
    find_by_email_and_name
    # ...&lt;/p&gt;

&lt;p&gt;假如你的模型有 10 个字段，那就是 10! （362880）个查找方法需要定义。想象一下，在你的 Rails 项目跑起来的时候，有这么多个方法需要一次定义掉，而 ruby 环境还得把它们都放在内存里头。&lt;/p&gt;

&lt;p&gt;老虎·伍兹都做不来的事情。&lt;/p&gt;

&lt;p&gt;** 正确的 &lt;code&gt;method_missing&lt;/code&gt; 使用方式&lt;/p&gt;

&lt;p&gt;（译者猥琐地注：要回家了，以下简要摘译）&lt;/p&gt;

&lt;p&gt;1、先检查&lt;/p&gt;

&lt;p&gt;并不是每次调用都要处理的，你应该先检查一下这次调用是否符合你需要添加的元方法的模式：&lt;/p&gt;

&lt;p&gt;def method_missing(method_id, *arguments, &amp;amp;block)
      if method_id.to_s =~ /^what_is_[\w]+/
        # do your thing
      end
    end&lt;/p&gt;

&lt;p&gt;2、包起来&lt;/p&gt;

&lt;p&gt;检查好了，确实要处理的，请记得把函数体包在你的好基友，&lt;code&gt;define_method&lt;/code&gt; 里面。如此，下次就不用找情妇了：&lt;/p&gt;

&lt;p&gt;def method_missing(method_id, *arguments, &amp;amp;block)
      if method_id.to_s =~ /^what_is_[\w]+/
        self.class.send :define_method, method_id do
          # do your thing
        end
        self.send(method_id)
      end
    end&lt;/p&gt;

&lt;p&gt;3、擦屁股&lt;/p&gt;

&lt;p&gt;自己处理不来的方法，可能父类有办法，所以 &lt;code&gt;super&lt;/code&gt; 一下：&lt;/p&gt;

&lt;p&gt;def method_missing(method_id, *arguments, &amp;amp;block)
      if method_id.to_s =~ /^what_is_[\w]+/
        self.class.send :define_method, method_id do
          # do your thing
        end
        self.send(method_id)
      else
        super
      end
    end&lt;/p&gt;

&lt;p&gt;4、昭告天下&lt;/p&gt;

&lt;p&gt;def respond_to?(method_id, include_private = false)
      if method_id.to_s =~ /^what_is_[\w]+/
        true
      else
        super
      end
    end&lt;/p&gt;

&lt;p&gt;要告诉别人，你的类虽然暂时还没有这个方法，但是其实是能够响应这方法的。&lt;/p&gt;

&lt;p&gt;** 总结 **&lt;/p&gt;

&lt;p&gt;在每个 Ruby 程序员的生活中，这仨方法扮演了重要的角色。&lt;code&gt;define_method&lt;/code&gt; 是你的好基友，&lt;code&gt;method_missing&lt;/code&gt; 是个如胶似漆但也需相敬如宾的情妇，而 &lt;code&gt;respond_to?&lt;/code&gt; 则是你的爱子，如此无虞。&lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Tue, 22 May 2012 20:38:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/3434</link>
      <guid>https://ruby-china.org/topics/3434</guid>
    </item>
    <item>
      <title>Cross Site Request Forgery 的疑问</title>
      <description>&lt;p&gt;Rails 提供的保护方式是使用 csrf-token，如果非 GET 请求中没有这个参数或者参数值无效，就重置掉当前的 session。&lt;/p&gt;

&lt;p&gt;打开上述行为的方法是在控制器里加上 &lt;code&gt;protect_from_forgery&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;现在我的问题是，我在 &lt;code&gt;application_controller&lt;/code&gt; 里头加上了，终端里遇到不合法的请求时，也却是验证没过，显示了个 &lt;code&gt;WARNING: Can't verify CSRF token authenticity&lt;/code&gt;，但是似乎 session 没有被重置，而且这个请求也还是被认为有效的。&lt;/p&gt;

&lt;p&gt;我要怎么做，才能在出现这个 warning 的时候，不再执行其他步骤，并重置 session？  &lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Thu, 10 May 2012 20:01:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/3200</link>
      <guid>https://ruby-china.org/topics/3200</guid>
    </item>
    <item>
      <title>37signals 测试七忌</title>
      <description>&lt;p&gt;据说今晚 Ruby Tuesday 杭州的话题是测试，可惜我最近都要忙项目。赶巧看到 37signals 也发博文聊测试（&lt;a href="http://37signals.com/svn/posts/3159-testing-like-the-tsa" rel="nofollow" target="_blank"&gt;http://37signals.com/svn/posts/3159-testing-like-the-tsa&lt;/a&gt;），就顺便翻译一下它提的测试七忌。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;不要以 100% 覆盖率为目标。&lt;/li&gt;
&lt;li&gt;代码·测试比超过 1:2 就味道不对了，超过 1:3 则臭不可闻。&lt;/li&gt;
&lt;li&gt;如果测试占用了你超过 1/3 的时间，那你很可能就没搞对。如果超过一半时间，那你肯定弄糟了。&lt;/li&gt;
&lt;li&gt;不要测试标准的 Active Record 的关联、验证或者 scope。&lt;/li&gt;
&lt;li&gt;把集成测试留到对独立元素们做集成出现问题时在搞（也就是说，可以单元测试的，就不要做集成测试）。&lt;/li&gt;
&lt;li&gt;不要用 Cucumber，除非你生活在梦幻之城，那里的非程序员们要写测试（如果你在那儿，请务必给我寄一瓶仙境之沙）。&lt;/li&gt;
&lt;li&gt;不要强迫自己对每个控制器、模型与视图都测试先行（我一般是 20% 测试先行，80% 先上车后补票）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;原文：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t aim for 100% coverage.&lt;/li&gt;
&lt;li&gt;Code-to-test ratios above 1:2 is a smell, above 1:3 is a stink.&lt;/li&gt;
&lt;li&gt;You’re probably doing it wrong if testing is taking more than 1/3 of your time. You’re definitely doing it wrong if it’s taking up more than half.&lt;/li&gt;
&lt;li&gt;Don’t test standard Active Record associations, validations, or scopes.&lt;/li&gt;
&lt;li&gt;Reserve integration testing for issues arising from the integration of separate elements (aka don’t integration test things that can be unit tested instead).&lt;/li&gt;
&lt;li&gt;Don’t use Cucumber unless you live in the magic kingdom of non-programmers-writing-tests (and send me a bottle of fairy dust if you’re there!)&lt;/li&gt;
&lt;li&gt;Don’t force yourself to test-first every controller, model, and view (my ratio is typically 20% test-first, 80% test-after).&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>dotnil</author>
      <pubDate>Tue, 17 Apr 2012 19:07:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/2667</link>
      <guid>https://ruby-china.org/topics/2667</guid>
    </item>
    <item>
      <title>mysqlbinlog 与 nginx 记的访问日志你们都是怎么处理的？</title>
      <description>&lt;p&gt;我有个 Linode VPS，最便宜的那个，磁盘容量 20G，&lt;a href="http://luoo.net" rel="nofollow" target="_blank"&gt;http://luoo.net&lt;/a&gt; 在上面，访问量比我预期的要大，现在日志文件快 1G 了&lt;/p&gt;

&lt;p&gt;du -h -c --max-depth=1 /var/log&lt;/p&gt;

&lt;p&gt;发现，除了这个 access.log，有好几坨 mysql-bin.00000X 文件也超大。暂时想不到该拿这些文件怎么办。前者可以做一些访问统计，&lt;a href="http://www.ruanyifeng.com/blog/2012/01/a_bash_script_of_apache_log_analysis.html" rel="nofollow" target="_blank"&gt;http://www.ruanyifeng.com/blog/2012/01/a_bash_script_of_apache_log_analysis.html&lt;/a&gt; ；后者据说可以用来恢复数据，搜了几篇文章，还没细看？&lt;/p&gt;

&lt;p&gt;大家都是怎么处理这俩货的？  &lt;/p&gt;</description>
      <author>dotnil</author>
      <pubDate>Fri, 09 Mar 2012 10:44:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/1733</link>
      <guid>https://ruby-china.org/topics/1733</guid>
    </item>
  </channel>
</rss>
