<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>yangyuqian (Ruby功城狮)</title>
    <link>https://ruby-china.org/yangyuqian</link>
    <description>Ruby真是个性感的小尤物</description>
    <language>en-us</language>
    <item>
      <title>[北京] 集智金融招聘 Ruby 工程师 (10-20k)</title>
      <description>&lt;h2 id="关于我们"&gt;关于我们&lt;/h2&gt;
&lt;p&gt;成立 1 年的创业团队，自主研发的企业供应链金融 SAAS 平台马上上线，产业联盟、产业投资基金的上百家企业将成为我们的用户，包括上市公司、投资机构、政府部门。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.raismart.com" rel="nofollow" target="_blank" title=""&gt;www.raismart.com&lt;/a&gt; , icity.raismart.com&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/e8d4bd79ed9b92a0c315a25118c621d6.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/85e415d49af4cf7d2bc06a98fdff3c6c.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在我们的模式中，不仅有企业金融中国际领先者的身影，还有产业投资基金、银行、交易银行、P2P、保险、融资租赁、保理等就是银行职员也难以轻易把它们联系起来的专业机构。&lt;/p&gt;

&lt;p&gt;啥？！开发工程师要懂这些？！&lt;/p&gt;

&lt;p&gt;不须要！&lt;/p&gt;

&lt;p&gt;但如果想懂，在这里就太有可能了，而且会让你真的多了不一样的牛 X！&lt;/p&gt;

&lt;p&gt;谁都能想像如果懂了这些，可不光是装逼、把妹能用，说不定你能宅在家里就能和一帮金融才子、产业高手跨界打劫~~（就是调动很大资金资源干更大更牛的项目，位高钱重啊！哈哈）&lt;/p&gt;

&lt;p&gt;当然，你首先需要是一个不错的 Ruby 工程师，经验丰富的牛当然好！经验还不丰富的话，你应该能展现你能补足它们的潜力和更丰富的素质让我们同样产生惊喜！
听上去似乎弹性略大啊？！&lt;/p&gt;

&lt;p&gt;对！因为这里是一个以新经济模式进行思维的，风口行业上的创造性团队，所以&lt;/p&gt;
&lt;h2 id="如果你是XXX中的战斗机，享受挑战、创造与突破，来试试吧！!"&gt;如果你是 XXX 中的战斗机，享受挑战、创造与突破，来试试吧！!&lt;/h2&gt;
&lt;p&gt;说白了，团队 TOP GUN 的战斗机牛逼范真不是茶点、咖啡、休假这样泡出来的，目前没有，这个时点有的是可以因你而量身打造的母舰跑道！&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/bc81b26d5fce6c9e078da4c2c20e0631.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;互联网唯快不破的理论在此也适用。&lt;/p&gt;

&lt;p&gt;那就是：优秀的你如果犹豫，跑道上起飞就会是别的战机。&lt;/p&gt;
&lt;h2 id="想找份好工作的，请出门绕行看别的贴^_^"&gt;想找份好工作的，请出门绕行看别的贴^_^&lt;/h2&gt;
&lt;p&gt;这里讲的是一种生活方式，是一段人生&lt;/p&gt;

&lt;p&gt;因为崇尚自由、协作、学习、创新&lt;/p&gt;

&lt;p&gt;所以&lt;/p&gt;

&lt;p&gt;你工作的地方经常可以在公司：中关村金融大厦（丹棱 SOHO），图片如下：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/d30b6c5efffa585f7bda254c6aa05699.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;也经常可以在某咖啡、你自己的家、别人的家 (啥？！就当写错了吧~~哈哈)……，图片自备^_^&lt;/p&gt;
&lt;h2 id="知识、技能要求"&gt;知识、技能要求&lt;/h2&gt;
&lt;p&gt;1、精通 Ruby On Rails 进行 Web 开发&lt;/p&gt;

&lt;p&gt;2、熟练掌握 MySQL（懂优化更好）&lt;/p&gt;

&lt;p&gt;3、CSS、HTML5、JavaScript、jQuery、Git、Linux（Ubuntu）&lt;/p&gt;

&lt;p&gt;4、其它宽泛了解的内容，如：&lt;/p&gt;

&lt;p&gt;PostgreSQL、NoSQL、MongoDB、Redis&lt;/p&gt;

&lt;p&gt;CSS3、BootStrap、Github、jQueryUI&lt;/p&gt;

&lt;p&gt;iOS、Android&lt;/p&gt;

&lt;p&gt;作为首批上舰战机，&lt;/p&gt;

&lt;p&gt;10K－20K（税后）+ 五险一金 + 期权，有底线无上限。&lt;/p&gt;

&lt;p&gt;舰队开到加勒比时会变成什么目前保密&lt;/p&gt;

&lt;p&gt;我们现在可以提前告诉你的是：&lt;/p&gt;

&lt;p&gt;在你翱翔在迈阿密上空时，你会听到 Enya 的那首：Caribbian Blue&lt;/p&gt;
&lt;h2 id="工作职责"&gt;工作职责&lt;/h2&gt;
&lt;p&gt;网站前后台开发、移动端（app、公众号等）开发……&lt;/p&gt;

&lt;p&gt;或者这个部门……&lt;/p&gt;

&lt;p&gt;或者企业的未来……&lt;/p&gt;
&lt;h2 id="发展空间"&gt;发展空间&lt;/h2&gt;
&lt;p&gt;包括但不限于：首席工程师、产品经理、CTO、项目经理、开发工程总监（产品、开发、测试）…………&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/a5ed0604b458c981406086fd4b211309.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;p&gt;邮件联系：发送至 carlven@126.com&lt;/p&gt;

&lt;p&gt;技术能力展示，请加 QQ：594378159，注明“集智 Ruby 开发”，交流时间会安排在晚上。&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Tue, 20 Oct 2015 11:52:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/27746</link>
      <guid>https://ruby-china.org/topics/27746</guid>
    </item>
    <item>
      <title>小领悟 － 20150726</title>
      <description>&lt;p&gt;当我能...&lt;/p&gt;

&lt;p&gt;发自内心接受别人的缺点时
坦然面对别人的非议时
真诚接受批评时
知错就改时
不计前嫌时&lt;/p&gt;

&lt;p&gt;天空一片晴朗！&lt;/p&gt;

&lt;p&gt;一言以蔽之，“接受世界的不公平”。不受外界干扰，发自内心的去做好自己想做好的事情吧！为自己欢呼！&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Sun, 26 Jul 2015 00:49:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/26648</link>
      <guid>https://ruby-china.org/topics/26648</guid>
    </item>
    <item>
      <title>《没有银弹》读书随想 － 标准化开发环境的意义</title>
      <description>&lt;h2 id="摘要"&gt;摘要&lt;/h2&gt;
&lt;p&gt;《没有银弹》软件开发中时间耗损的可以被分为 2 个范畴：必要耗损和意外耗损。&lt;/p&gt;

&lt;p&gt;其中必要耗损：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;软件复杂度带来的学习成本&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;软件作为人类思维的产物，不直观&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;意外耗损：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;产品设计随意性带来的实现成本（不考虑基础架构能力，随即给出设计，只能给出一些临时解决方案）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;需求变更&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于软件生产活动中的必要耗损似乎没有什么特别好的解决方案，但 30 年前作者就提到了一点，让我非常吃惊：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;标准化的开发环境&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="标准化开发环境"&gt;标准化开发环境&lt;/h2&gt;
&lt;p&gt;产品复杂的配置、开发环境搭建已经成为了很多人的痛点，以我目前的公司为例：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;rails 相关的项目有 4 个，其中涉及不同版本的 ruby（ree &amp;amp; ruby 2），这些项目直接存在业务上的耦合，以及 url 之间的复杂跳转&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;还涉及一些非 ruby（java 等）语言开发的模块（3 个）&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这样的产品结构对技术人员，尤其是新入职的技术人员提出了非常高的挑战。&lt;/p&gt;

&lt;p&gt;从团队整体效率来看，搭建一个完整环境变得那么的奢侈，但又不得不承认：完整的开发环境对于 debug、开发、开发后自测，甚至质量保证人员都具有很大的价值&lt;/p&gt;

&lt;p&gt;从大项目的角度看，要求每个人对产品业务及实现都了如指掌可能是个伪命题，通常来讲大家都只能关注与产品的一些点，每次开发也都只涉及一些子模块&lt;/p&gt;

&lt;p&gt;从而一个标准化的开发环境变得至关重要，它应该满足以下条件：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;稳定：稳定是第一位的，当开发一个模块的时候，整体环境的其他部分应该能够正确运行&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;灵活：同一个程序员的工作有时候会涉及多个子模块，应该要做到“想开发谁，就能够迅速搭建起来相应的环境”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;成本：成本要可控：标准化开发环境的好处在于能保证较好的机动性，人力成本也随之降低，降低的人力成本也减少了代码混乱的风险&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们采用 vagrant 封装了一些统一的工具，并用 docker 来运行相应的代码&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Tue, 07 Jul 2015 23:24:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/26376</link>
      <guid>https://ruby-china.org/topics/26376</guid>
    </item>
    <item>
      <title>采用视图减少 Rails 底层的复杂的表连接操作是否可取？</title>
      <description>&lt;p&gt;有时候，复杂的业务需求中，为了获取一些额外信息，需要去 join 很多表，比如一个经典的 user role permission 的 5 表设计：
user user_role role role_permission permission&lt;/p&gt;

&lt;p&gt;如果想要获取某个 User 的所有权限，可以借助以下 SQL:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT u.id, u.username, r.name, p.name FROM user u 
INNER JOIN user_role ur ON ur.user_id = u.id
INNER JOIN role r ON r.id = ur.role_id
INNER JOIN role_permission rp ON rp.role_id = r.id
INNER JOIN permission p ON rp.permission_id = p.id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;采用 ActiveRecord 定义时需要加入很多额外的 association，极大地提升了业务复杂度。&lt;/p&gt;

&lt;p&gt;如果能创建一个 view，建立从 user -&amp;gt; permission 的关系：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE VIEW user_permission AS SELECT u.id uid, r.id rid, p.id pid, u.username username, r.name rname, p.name pname FROM user u 
INNER JOIN user_role ur ON ur.user_id = u.id
INNER JOIN role r ON r.id = ur.role_id
INNER JOIN role_permission rp ON rp.role_id = r.id
INNER JOIN permission p ON rp.permission_id = p.id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只需要定义一个 UserPermission 就能查询相应的 view 了：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserPermission &amp;lt; ActiveRecord::Base; end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;目前看到这种方案是可以降低业务代码的复杂度的，但是否会对性能造成影响呢？&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Thu, 02 Jul 2015 11:05:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/26283</link>
      <guid>https://ruby-china.org/topics/26283</guid>
    </item>
    <item>
      <title>用 Bundler 管理任意 App 的 Gem</title>
      <description>&lt;h2 id="引言"&gt;引言&lt;/h2&gt;
&lt;p&gt;Bundler 提供了复杂的第三方 Gem 与项目代码的 $LOAD_PATH 自动化管理的解决方案。&lt;/p&gt;
&lt;h2 id="基于 Bundler 的 Gem 管理机制"&gt;基于 Bundler 的 Gem 管理机制&lt;/h2&gt;
&lt;p&gt;Rails App 在 Gemfile 声明的 Gem 在启动的时候就会通过 Bundler 整理好并构建正确的 $LOAD_PATH, 大体分为 2 步：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;启动时会执行 $APP_ROOT/config/boot.rb:

&lt;ul&gt;
&lt;li&gt;根据 Gemfile 初始化 $LOAD_PATH , 并将 Gemfile 中声明的 gem 的 lib 目录加入 $LOAD_PATH&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# $APP_ROOT/config/boot.rb
require 'rubygems'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;执行 app/config/application.rb

&lt;ul&gt;
&lt;li&gt;根据 RAILS_ENV 将 Gemfile 中符合要求的 Gem group 加入 $LOAD_PATH&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(*Rails.groups(:assets =&amp;gt; %w(development test)))
# 此处省略module定义，内容和具体的项目相关
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样一个 Rails App 就知道自己在运行时的 $LOAD_PATH, 接下来就可以对类文件按需加载了。&lt;/p&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://guides.rubyonrails.org/initialization.html" rel="nofollow" target="_blank" title=""&gt;The Rails Initialization Process&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://yehudakatz.com/2010/05/09/the-how-and-why-of-bundler-groups/" rel="nofollow" target="_blank" title=""&gt;How and Why Bundler Groups&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://yehudakatz.com/2010/05/09/the-how-and-why-of-bundler-groups/" rel="nofollow" target="_blank" title=""&gt;How and Why Bundler Groups&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices/" rel="nofollow" target="_blank" title=""&gt;Gem Packaging: Best Practices&lt;/a&gt;&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Tue, 16 Jun 2015 00:58:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/26038</link>
      <guid>https://ruby-china.org/topics/26038</guid>
    </item>
    <item>
      <title>Ruby 常量查找</title>
      <description>&lt;h2 id="引言"&gt;引言&lt;/h2&gt;
&lt;p&gt;在 Ruby 中访问一个常量 A 时，最终是怎么找到的呢？来看几个例子：&lt;/p&gt;

&lt;p&gt;例 1:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A
  CREF = "*** CREF in module A"
end 

module A
  module B
    puts CREF
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例 2:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A
  CREF = "*** CREF in module A"
end

module A::B
  puts CREF
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例 3:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  CREF = "*** CREF in module A"
end

B = Class.new(A) do
  puts CREF
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上几个例子基本涵盖了本文将要介绍的解释器中的“常量查找”(Constant Lookup) 机制，这些例子中哪些是可以正确执行的呢？如果报错，具体会报什么错？&lt;/p&gt;

&lt;p&gt;这里先不揭晓答案，读者可以自行在 irb console 中去执行这几段例程，看看结果是怎么样的。&lt;/p&gt;
&lt;h2 id="Ruby 常量查找"&gt;Ruby 常量查找&lt;/h2&gt;
&lt;p&gt;很多开发语言中，“常量”的行为很简单，而 Ruby 中的“常量查找”机制给这个已经司空见惯的概念披上了神秘的面纱。&lt;/p&gt;
&lt;h2 id="nesting"&gt;nesting&lt;/h2&gt;
&lt;p&gt;nesting 是解释器用来编排 (compose) Class/Module 上下层级关系的，以下的定义方式中 B 获得的 nesting 就是自身以及上层的 namespace（不含 Object，这是一个全局回溯的点），即：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A
  module B
    Module.nesting == [A::B, A]
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以将 nesting 看成一颗多叉树，具体怎么编排树中的节点和具体写法有关，以下的定义方式中 B 获得的 nesting 仅包含自身：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A::B
  Module.nesting == [A::B]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nesting 是解释器维护的一个 Class/Module 私有栈，这个栈的读写有很多限制。如传入的 block 不能读取这个栈（得到一个空数组）:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
end

B = Class.new(A) do
  def self.hi
    Class.nesting
  end
end

B.hi

class C &amp;lt; A
  puts Class.nesting.join(', ')

  def self.hi
    yield
  end
end

C.hi { Class.nesting }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有意思的，用匿名类的方式定义的 singleton 方法读到的 nesting 和直接在 class 里面定义的结果不同，这个匿名类会先被 push 到 nesting 中，然后又 pop 出来：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  class &amp;lt;&amp;lt; self
    Class.nesting
  end
end
# -&amp;gt; [#&amp;lt;Class:A&amp;gt;, A]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;总之，nesting 和代码的具体写法有关，而且对 block 存在读取限制。&lt;/p&gt;
&lt;h2 id="常量查找算法"&gt;常量查找算法&lt;/h2&gt;
&lt;p&gt;内核中的常量查找算法的伪代码如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if 从 Module.nesting 找到常量定义？
  很开心的退出，返回常量引用
else
  while 遍历到整个结构树的顶层节点？
    if 从 Module.nesting.first.ancestor 找到常量定义?
      很开心的退出，返回常量引用
    else
      继续往 Module.nesting.first.ancestor 查找
    end
  end
end

if 上面的方式找不到
  去 Object 里面查找常量定义，这个时候再找不到就直接抛 NameError 了.
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在回过头去看&lt;strong&gt;前言&lt;/strong&gt;中提到的几个例子：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;B 中 Module.nesting == [A::B, A]，所以可以找到 A::CREF&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;B 中 Module.nesting == [A::B], 所以找不到任何 A::CREF 定义，抛异常&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;B 中 Module.nesting == [], 所以就算在 B 中定义了 CREF, block 中也是无法读取的&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://cirw.in/blog/constant-lookup.html" rel="nofollow" target="_blank" title=""&gt;Everything you ever wanted to know about constant lookup in Ruby&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://makandracards.com/makandra/20633-ruby-constant-lookup-the-good-the-bad-and-the-ugly" rel="nofollow" target="_blank" title=""&gt;Ruby constant lookup: The good, the bad and the ugly&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://guides.rubyonrails.org/autoloading_and_reloading_constants.html" rel="nofollow" target="_blank" title=""&gt;Autoloading and Reloading Constants&lt;/a&gt;&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Tue, 16 Jun 2015 00:57:40 +0800</pubDate>
      <link>https://ruby-china.org/topics/26037</link>
      <guid>https://ruby-china.org/topics/26037</guid>
    </item>
    <item>
      <title>Ruby 内核类加载</title>
      <description>&lt;h2 id="$LOAD_PATH"&gt;$LOAD_PATH&lt;/h2&gt;
&lt;p&gt;在一个 Rails 项目中，有很多的第三方类库（Gem）, 还有项目自身的文件，App 如何管理这些类库？&lt;/p&gt;

&lt;p&gt;在项目中完成业务代码后，怎么告诉 Ruby 的解释器说，把某个类加载进来？&lt;/p&gt;

&lt;p&gt;以上这两个问题是同质的，显然应该有一个相同的逻辑来处理它们。&lt;/p&gt;

&lt;p&gt;如果要对第三方的类库和项目代码进行统一管理，又不是灵活性，就势必要对文件做一个抽象，就好像有一个文件系统，下面挂了很多个类库，以及业务代码，加载类的时候，只需要去这些地方找就行了，这个文件系统的抽象就是本章要介绍的 $LOAD_PATH.&lt;/p&gt;

&lt;p&gt;Ruby 内核并不要求统一管理类，理论上我们的类文件可以分布在系统的各个角落，意味着我们需要为每个文件制定一个绝对路径活着相对路径，想想都那么蛋疼！&lt;/p&gt;

&lt;p&gt;首先，打开一个 irb console, 默认的 $LOAD_PATH，实际上是一个 String Array:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.1.5 :001 &amp;gt; $:
 =&amp;gt; ["/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby/2.1.0", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby/2.1.0/x86_64-linux", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/vendor_ruby/2.1.0", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/vendor_ruby/2.1.0/x86_64-linux", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/vendor_ruby", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0", "/home/vagrant/.rvm/rubies/ruby-2.1.5/lib/ruby/2.1.0/x86_64-linux"] 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就可以很容易将新的类库对应的跟路径加入 $LOAD_PATH，以下代码会将当前的目录加入 $LOAD_PATH:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$:.unshift File.dirname(__FILE__)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，即便有 $LOAD_PATH 还是不够的，很多第三方类库里面的依赖使用者显然是不清楚的，要让程序员写出好代码，又要维护本不是自己开发的一些第三方类库的依赖，那我还是回去写汇编好了。&lt;/p&gt;

&lt;p&gt;Ruby 的世界不能容忍每个人都去手动维护一坨 $LOAD_PATH 初始化脚本，那么就有了 Bundler, 提供了一系列优雅地管理第三方类库 $LOAD_PATH 的解决方案，详情参见 &lt;a href="https://ruby-china.org/topics/26038" title=""&gt;基于 Bundler 的 Gem 管理机制&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Ruby Kernel 中的类加载"&gt;Ruby Kernel 中的类加载&lt;/h2&gt;
&lt;p&gt;Ruby 内核提供了 4 个类加载命令，分别是 load, autoload, require, require_relative, 分别对应了不同的使用场景，可谓做到了“小的可以打蚊子，大的可以打飞机”.&lt;/p&gt;
&lt;h2 id="Kernel.load(filename, wrap=false) → true/false"&gt;Kernel.load(filename, wrap=false) → true/false&lt;/h2&gt;
&lt;p&gt;load 命令提供了一种最原始的方法，即每次都会重新加载整个文件，刷新内存中的类定义。&lt;/p&gt;

&lt;p&gt;新建一个 calendar.rb, 内容如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# calendar.rb
class Calendar
  def initialize(month, year)
    @month = month
    @year  = year
  end

  # A simple wrapper around the *nix cal command.
  def to_s
    IO.popen(["cal", @month.to_s, @year.to_s]) { |io| io.read }
  end
end

puts Calendar.new(8, 2011)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开一个 irb console 直接加载 calendar.rb:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 这里可以给绝对路径，也可以是相对路径
irb(main):001:0&amp;gt; load './calendar.rb'
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这存在一个问题，即 calendar.rb 加载进来后可能会影响当前 namespace 的一些状态，也可能被当前 namespace 的状态影响，比如常量的值等等。如果希望 calendar.rb 的内容 悄悄的加载，不影响当前 namespace 中的状态，load 命令支持用一个匿名 Module 包装被加载的内容，从而保证了这个文件里面的东西都是在限定范围内执行：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; load './calendar.rb', true
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;觉得相对/绝对路径太麻烦？可以将当前路径加入 $LOAD_PATH:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; $:.unshift File.dirname(__FILE__)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后加载文件只需要给出文件名：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):002:0&amp;gt; load 'calendar.rb'
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Kernel.autoload(module, filename) → nil"&gt;Kernel.autoload(module, filename) → nil&lt;/h3&gt;
&lt;p&gt;load 命令每次都加载类有些浪费，很多类并不是一开始就需要，可以用 autoload 来先创建一个钩子，等到真的访问到的时候再加载：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;autoload :Calendar, './calendar.rb'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但这种方式有个问题：相同常量如果多次定义 autoload 钩子，只有最后一个会被触发。设想在实际开发中，类定义可能分布在多个文件中，所以这种方式并不常用。&lt;/p&gt;
&lt;h3 id="Kernel.require(name) → true or false"&gt;Kernel.require(name) → true or false&lt;/h3&gt;
&lt;p&gt;和 autoload 一样，require 想解决的也是性能问题：require 只在第一次被调用的时候被触发，之后针对相同文件的 require 就不会真正执行了：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; require './calendar.rb'
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
irb(main):002:0&amp;gt; require './calendar.rb'
=&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;require 比 load 更强大一些，load 是必须给出文件后缀的，而 require 可以不给出后缀，且相同的名字对 .so .o .dll 都是有效的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; require './calendar.rb'
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
irb(main):002:0&amp;gt; require './calendar'
=&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;require 和 load 也都会读取 $LOAD_PATH，因此如果将当前目录加入 $LOAD_PATH，require 也就可以不给相对路径，只给一个文件名了：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; $:.unshift File.dirname(__FILE__)
=&amp;gt; [".", "/Library/Ruby/Site/2.0.0", "/Library/Ruby/Site/2.0.0/x86_64-darwin14", "/Library/Ruby/Site/2.0.0/universal-darwin14", "/Library/Ruby/Site", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/vendor_ruby/2.0.0", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/vendor_ruby/2.0.0/x86_64-darwin14", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/vendor_ruby/2.0.0/universal-darwin14", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/vendor_ruby", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/x86_64-darwin14", "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/universal-darwin14"]
irb(main):002:0&amp;gt; require 'calendar'
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Kernel.require_relative(string) → true or false"&gt;Kernel.require_relative(string) → true or false&lt;/h3&gt;
&lt;p&gt;require_relative 相当于是默认将当前路径加入了 $LOAD_PATH，不用给相对路径或绝对路径，其他和 require 是一致的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; require_relative 'calendar'
    August 2011
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

=&amp;gt; true
irb(main):002:0&amp;gt; require_relative 'calendar.rb'
=&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://practicingruby.com/articles/ways-to-load-code" rel="nofollow" target="_blank" title=""&gt;Ways to load code&lt;/a&gt;&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Tue, 16 Jun 2015 00:56:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/26036</link>
      <guid>https://ruby-china.org/topics/26036</guid>
    </item>
    <item>
      <title>Rails 中的类加载机制</title>
      <description>&lt;h2 id="摘要"&gt;摘要&lt;/h2&gt;
&lt;p&gt;本文从一些实例代码入手，尽可能全面地介绍了 Rails 类加载及涉及的相关技术知识：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ruby 内核类加载&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ruby 常量查找&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ActiveSupport 对内核类加载的扩展&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rails 自动类加载&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rails 类加载中的常见误区&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="Ruby 内核类加载"&gt;Ruby 内核类加载&lt;/h2&gt;
&lt;p&gt;Ruby 内核类加载机制已经提供了类加载所需要的所有能力，具体参见 &lt;a href="https://ruby-china.org/topics/26036" title=""&gt;Ruby 内核类加载机制&lt;/a&gt;, 而 Rails 等框架提供的能力就是用“启发式”的去找到一个类定义的文件的位置，将其自动加载到内存中。&lt;/p&gt;
&lt;h2 id="Ruby “常量查找”(Constant Lookup)"&gt;Ruby“常量查找”(Constant Lookup)&lt;/h2&gt;
&lt;p&gt;“启发式”的类加载方式需要有一个触发点来告诉 Rails 什么时候加载什么类：这个触发点就是 Ruby 的 const_missing 方法，而在什么时候会触发它呢？这就需要了解 Ruby 是怎么判断一个类（常量）是否已经在内存中定义了的原理了，即 &lt;a href="https://ruby-china.org/topics/26037" title=""&gt;Ruby 中的常量查找&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="ActiveSupport 对内核类加载的扩展"&gt;ActiveSupport 对内核类加载的扩展&lt;/h2&gt;&lt;h2 id="autoload 扩展"&gt;autoload 扩展&lt;/h2&gt;
&lt;p&gt;ActiveSupport 扩展内核的 autoload, 便于创建简洁的 library (Gem).&lt;/p&gt;

&lt;p&gt;比较大的项目中，通常会将通用模块封装成 Gem, 比如我们可以将 sso 相关的功能封装成 Gem, 目录结构如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lib/
  sso.rb
  sso
    form_auth.rb
    api_auth.rb
sso.gemspec
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;假设这里提供了 2 种认证方式，即基于 Form 的登陆方式，在 form_auth.rb 中实现; 此外还能通过 api 登陆，实现在 api_auth.rb 中。&lt;/p&gt;

&lt;p&gt;将整个 Gem 封装好之后，怎么来使用其中的实现呢？类加载的方式是第一个需要解决的问题。&lt;/p&gt;

&lt;p&gt;内核类加载中提供了这种能力，lib/sso.rb 中为整个 Gem 提供了单一的入口，可以承担加载类的职责，可以这样实现：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/sso.rb

require 'sso/form_auth'
require 'sso/api_auth.rb'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Rails App 代码中只需要 require 'sso' 就能将整个 sso 库提供的功能引入到项目里面来了。可见，这里的 lib/sso.rb 不会有具体的实现代码，只是一个类加载的入口，如果想把 sso 名字改成 account 怎么办？这里给一个完全基于内核类加载的解决方案：&lt;/p&gt;

&lt;p&gt;1  修改目录名和入口文件名：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lib/
  account.rb
  account
    form_auth.rb
    api_auth.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2  修改 account.rb 中文件路径&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/account.rb

require 'account/form_auth'
require 'account/api_auth.rb'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上的例子相对简单，如果要用这种方式管理很多复杂的 Gem 的时候就显得拙荆见肘了。考虑到很多 Ruby 相关的应用场景都和 Rails 有关，那么能不能更优雅一点呢？这里有一个信息没有充分利用，那就是 lib/sso.rb 的文件名。在 ActiveSupport::Dependencies::Autoload 中针对这样的使用场景做了一些扩展：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# active_support/dependencies/autoload

def autoload(const_name, path = @_at_path)
  unless path
    full = [name, @_under_path, const_name.to_s].compact.join("::")
    path = Inflector.underscore(full)
  end

  if @_eager_autoload
    @_autoloads[const_name] = path
  end

  super const_name, path
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基于扩展后的 autoload 就可以这样实现前面的 sso 类库的入口：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib/sso.rb

module Sso
  extend ActiveSupport::Autoload

  autoload :FormAuth
  autoload :ApiAuth 
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;入口文件中不在包含相对路径，只需要给出一个实现的类名即可。降低了和底层的耦合同时也提升了可读性。现在要改这个类库的名字只需要改 lib/sso.rb的文件名、module 的名字以及 lib/sso 目录的名字，是一个 O(1) 复杂度的算法，相比内核类加载那种方案的 O(n) 算法来说优化了很多。&lt;/p&gt;
&lt;h2 id="eager_autoload"&gt;eager_autoload&lt;/h2&gt;
&lt;p&gt;需要考虑线程安全的场景下，可以使用 require 来将类直接加载至内存中，但 require 也有和 autoload 一样的问题：需要指定文件路径。于是 ActiveSupport 给出了 eager_autoload, 可以选择性地加载类库中一些特定的类，避免了使用 require:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module MyLib
  extend ActiveSupport::Autoload

  autoload :Model

  eager_autoload do
    autoload :Cache
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样类库就提供了线程安全和非线程安全两种加载模式，线程安全的使用场景中可以直接在 App 启动的时候执行：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyLib.eager_load!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ActiveSupport 中的 eager_autoload 仅仅是记录了需要 eager_load 的文件路径，只有在执行了 &lt;strong&gt;eager_load!&lt;/strong&gt; 才会正真加载：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# active_support/dependencies/autoload

def eager_autoload
  old_eager, @_eager_autoload = @_eager_autoload, true
  yield
ensure
  @_eager_autoload = old_eager
end

def eager_load!
  @_autoloads.each_value { |file| require file }
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Rails 自动类加载"&gt;Rails 自动类加载&lt;/h2&gt;
&lt;p&gt;本章主要介绍 Rails 中给出的自动类加载解决方案，这是一个“启发式”类加载算法。&lt;/p&gt;

&lt;p&gt;值得强调的是：自动类加载是一种“启发式”的查找类定义文件位置的算法，真正找到之后加载用的还是 Ruby Kernel 提供的加载机制，具体方式和 RAILS_ENV 有关 (dev 下用 load, prod 下用 require).&lt;/p&gt;

&lt;p&gt;基本流程是，解释器会先用 Ruby 内核的常量查找算法尝试去找一个类定义，找不到就告诉 const_missing，然后 Rails 就跑去猜这个常量会定义在哪里，如果 Rails 还找不到，就抛异常。&lt;/p&gt;
&lt;h2 id="autoload_paths"&gt;autoload_paths&lt;/h2&gt;
&lt;p&gt;Rails 维护了类似于内核中 $LOAD_PATH 的变量 autoload_paths，Rails 3 中默认会将 app 下的子目录以及 lib 目录全部加入 autoload_paths, Rails 4 中去掉了 lib. &lt;/p&gt;

&lt;p&gt;以下是一个刚生成的 Rails 3 项目的 autoload 路径：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
.../app/assets
.../app/controllers
.../app/helpers
.../app/mailers
.../app/models
.../app/controllers/concerns
.../app/models/concerns
.../test/mailers/previews
.../lib
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Rails 中还可以添加自定义的 autoload 路径：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/application.rb
config.autoload_paths &amp;lt;&amp;lt; "#{Rails.root}/something"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;autoload_paths 实际上是在 ActiveSupport::Dependencies.autoload_paths 中定义的，这是一个 String Array, 可见 Rails 的 autoload 本质上是 ActiveSupport 的 autoload 机制。本文中刻意淡化 Rails 中的一些 autoload 相关的配置入口，转而从 ActiveSupport 的一些接口入手，介绍 Rails 中的类加载。脱离 Rails 调试 ActiveSupport 需要了解一些 &lt;a href="https://ruby-china.org/topics/26038" title=""&gt;Bundler 相关的知识&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;以下代码将当前目录加入 autoload_paths, 这样在 Ruby 找不到某个常量定义的时候，ActiveSupport 就会尝试找到常量定义文件并自动加载。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ActiveSupport::Dependencies.autoload_paths &amp;lt;&amp;lt; '.'
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ActiveSupport 中的常量查找算法"&gt;ActiveSupport 中的常量查找算法&lt;/h2&gt;
&lt;p&gt;新建文件 demo/user.rb，定义如下:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# demo/user.rb 
module Demo
  class User
    "class Demo::User loaded"
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将当前目录加入 autoload_paths:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.1.5 :008 &amp;gt;   ActiveSupport::Dependencies.autoload_paths &amp;lt;&amp;lt; "."
 =&amp;gt; ["."] 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新建 demo/role.rb，定义如下:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# demo/role.rb
module Demo
  class Role
    User
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开一个 irb console, 用 load 命令手动加载 demo/role.rb，发现即使没有为 demo/user.rb 声明任何的 load/require, 它居然这么顺利成章的自动加载了！&lt;/p&gt;

&lt;p&gt;Rails 中假设类常量名和文件名是有直接关系的，例如 app/models/auth/user.rb 中就应该是以下定义：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/models/auth/user.rb
module Auth
  class User
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails(ActiveSupport) 中的会根据触发 const_missing 的常量名称来猜测并尝试加载对应的文件，以加载前例中的 Auth::User 为例：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Demo::Role 找不到 User 常量，触发 const_missing(const_name), 此处 const_name == 'User'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ActiveSupport 中先拼接出来一个查询的起点 "#{Demo::Role.name}::#{const_name}", 即 Demo::Role::User&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;首先尝试查找 autoload_paths 下的 demo/role/user.rb, 没找到&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;然后往上走一层，尝试查找 autoload_paths 下的 demo/user.rb, 找到了&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果加载了 demo/user.rb 发现 Demo::User 还是未定义的状态，Rails 默认会抛 LoadError 异常：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LoadError: Unable to autoload constant Demo::User, expected ./demo/user.rb to define it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体算法的伪码如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if the class or module in which C is missing is Object
  let ns = ''
else
  let M = the class or module in which C is missing

  if M is anonymous
    let ns = ''
  else
    let ns = M.name
  end
end

loop do
  # Look for a regular file.
  for dir in autoload_paths
    if the file "#{dir}/#{ns.underscore}/c.rb" exists
      load/require "#{dir}/#{ns.underscore}/c.rb"

      if C is now defined
        return
      else
        raise LoadError
      end
    end
  end

  # Look for an automatic module.
  for dir in autoload_paths
    if the directory "#{dir}/#{ns.underscore}/c" exists
      if ns is an empty string
        let C = Module.new in Object and return
      else
        let C = Module.new in ns.constantize and return
      end
    end
  end

  if ns is empty
    # We reached the top-level without finding the constant.
    raise NameError
  else
    if C exists in any of the parent namespaces
      # Qualified constants heuristic.
      raise NameError
    else
      # Try again in the parent namespace.
      let ns = the parent namespace of ns and retry
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Rails 类加载中的常见误区"&gt;Rails 类加载中的常见误区&lt;/h2&gt;&lt;h3 id="autoload 目录下不要用 require"&gt;autoload 目录下不要用 require&lt;/h3&gt;
&lt;p&gt;没有 ActiveSupport 的情况下，我们需要用 Ruby 内核类加载机制来加载依赖：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'something'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样干的问题主要是 require 只加载一次 something 类，且只在第一次被加载，这样就永远不会触发 ActiveSupport 的 autoload，这就意味着，调试的时候如果修改了 something 需要重启整个 App.&lt;/p&gt;

&lt;p&gt;Rails 中推荐用 RAILS_ENV 来控制类加载方式，即 development 下用 load, production 下用 require.&lt;/p&gt;
&lt;h3 id="并发安全"&gt;并发安全&lt;/h3&gt;
&lt;p&gt;考虑为航天器建模，先定一个默认的飞行器的模型：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/models/flight_model.rb
class FlightModel
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后定义一种飞机模型，并生产一架飞机 (new 一个对象):&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/models/bell_x1/flight_model.rb
module BellX1
  class FlightModel &amp;lt; FlightModel
  end
end

# app/models/bell_x1/aircraft.rb
module BellX1
  class Aircraft
    def initialize
      @flight_model = FlightModel.new
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的 FlightModel.new 应该是想去新建一个 BellX1::FlightModel, 但如果 FlightModel 已经被加载过，解释器就能找到一个合法的类定义，不触发 Rails autoload, 则 BellX1::FlightModel 不会被加载，永远不可达。这种情况下代码的结果和具体的执行路径有关，存在并发安全问题。&lt;/p&gt;
&lt;h3 id="nesting 和 autoload 矛盾"&gt;nesting 和 autoload 矛盾&lt;/h3&gt;
&lt;p&gt;Rails 的 autoload 是基于 Ruby 内核常量查找机制的，其无法获取 nesting 内容，具体加载的类或常量和实际执行的时机有关，考虑下面的例子：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# qux.rb
Qux = "I'm at the root!"

# foo.rb
module Foo
end

# foo/qux.rb
module Foo
  Qux = "I'm in Foo!"
end

# foo/bar.rb
class Foo::Bar
  def self.print_qux
    puts Qux
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 console 中执行 2 次 Foo::Bar.print_qux, 解释器抛异常：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.1.5 :011 &amp;gt;   Foo::Bar.print_qux
I'm in Foo!
 =&amp;gt; nil 
2.1.5 :012 &amp;gt; Foo::Bar.print_qux
NameError: uninitialized constant Foo::Bar::Qux
  from /vagrant/demo1/foo/bar.rb:3:in print_qux'
  from (irb):12
  from /home/vagrant/.rvm/rubies/ruby-2.1.5/bin/irb:11:in &amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;出现这种问题的大致原理：&lt;/p&gt;

&lt;p&gt;执行第一次的时候 Ruby 尝试去取 Foo::Bar::Qux 发现找不到：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;然后找 Object::Qux, 还是找不到&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;触发 const_missing 交给  ActiveSupport 处理&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过 ActiveSupport 访问 Foo::Bar::Qux:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;加载顶层 module, foo.rb&lt;/li&gt;
&lt;li&gt;找 Foo::Bar::Qux 找不到&lt;/li&gt;
&lt;li&gt;加载 foo/qux.rb&lt;/li&gt;
&lt;li&gt;找 Foo::Qux&lt;/li&gt;
&lt;li&gt;很开心的加载了 Foo::Qux&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;执行第二次访问还是走一样的路径，区别在于找 Foo::Qux 的时候发现已经有定义了，就不往上找了&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;但已经存在的 Foo::Qux 并非是一个 missing 的常量，这里就出现了一个悖论：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby 内核告诉 ActiveSupport 我有一个常量找不到，可能是没加载文件，请帮我找到那个文件并加载这个常量&lt;/li&gt;
&lt;li&gt;然后 ActiveSupport 开始努力去寻找这个常量，结果找了半天只找到一个已经存在的常量&lt;/li&gt;
&lt;li&gt;ActiveSupport 毕竟只是苦力的干活，Ruby 内核才是掌柜的干活，苦力肯定不能擅作主张去加载一个 Ruby 没有加载的东西（已经存在的常量，如果 Ruby 没有加载就说明不是内核要的那个）&lt;/li&gt;
&lt;li&gt;无奈之下，ActiveSupport 也只好说自己没找到这个常量（实际上是找到了，但一切以 Ruby 的判断为准！N 个凡是！）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="不要在 App 启动的时候去 autoload 常量"&gt;不要在 App 启动的时候去 autoload 常量&lt;/h3&gt;
&lt;p&gt;考虑以下常量赋值：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/initializers/set_auth_service.rb:
AUTH_SERVICE = if Rails.env.production?
  RealAuthService
else
  MockedAuthService
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rails 初始化时候的常量是不会被修改的，之后就算我们修改了对应的 AuthService 实现，也必须重启才会生效。实现一个动态的 AccessPoint 可以解决这种问题：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/models/auth_service.rb
class AuthService
  if Rails.env.production?
    def self.instance
      RealAuthService
    end
  else
    def self.instance
      MockedAuthService
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://guides.rubyonrails.org/autoloading_and_reloading_constants.html" rel="nofollow" target="_blank" title=""&gt;Autoloading and Reloading Constants&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.plataformatec.com.br/2012/08/eager-loading-for-greater-good" rel="nofollow" target="_blank" title=""&gt;Eager loading for greater good&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell" rel="nofollow" target="_blank" title=""&gt;Rails autoloading — how it works, and when it doesn't&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://guides.rubyonrails.org/configuring.html" rel="nofollow" target="_blank" title=""&gt;Configuring Rails Applications&lt;/a&gt;&lt;/p&gt;</description>
      <author>yangyuqian</author>
      <pubDate>Mon, 15 Jun 2015 23:13:03 +0800</pubDate>
      <link>https://ruby-china.org/topics/26034</link>
      <guid>https://ruby-china.org/topics/26034</guid>
    </item>
  </channel>
</rss>
