<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>charleszhang (Charles)</title>
    <link>https://ruby-china.org/charleszhang</link>
    <description>微软高级攻城狮，公众号「超哥的地盘」，可帮助内推微软，985、211院校或有大厂工作经验优先。</description>
    <language>en-us</language>
    <item>
      <title>在微软工作是怎样一种体验</title>
      <description>&lt;p&gt;该文首发于&lt;a href="https://mp.weixin.qq.com/s?__biz=MzU2NzczMDY1NA==&amp;amp;mid=2247483841&amp;amp;idx=1&amp;amp;sn=18173a28e56d62760067ae208089d17f&amp;amp;chksm=fc998970cbee0066eaec7c1b08263204fff06b1b16eecdcca7407aba7058f0cd098b2a399448&amp;amp;token=1374571472&amp;amp;lang=zh_CN#rd" rel="nofollow" target="_blank" title=""&gt;我的公众号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我最近从国内某大厂跳槽到了微软中国，聊聊在微软工作这段时间的体验。&lt;/p&gt;
&lt;h2 id="Work Life Balance"&gt;Work Life Balance&lt;/h2&gt;
&lt;p&gt;不卷，不加班。&lt;/p&gt;

&lt;p&gt;这大概是很多人想进外企的最重要原因之一，微软在这方面做的很好。公司鼓励工作生活平衡，实行不定时工作制，不打卡，上下班时间自由，工作地点也比较随意，支持在家办公，在家办公只要跟 leader 同步一下即可，也不需要审批。我去的时候组里有一个同事在三亚远程办公了一个月。平时经常有人在家办公，基本不会出现所有人都在公司的情况，这已经成为了一个不成文的惯例。&lt;/p&gt;

&lt;p&gt;服务端开发有时需要值班，保障服务正常运行，在国内公司通宵值班是家常便饭。在微软早 10 点 - 晚 10 点是中国这边值班，晚 10 点 - 早 10 点是美国同事值班，避免了通宵值班，这也是跨国公司的一个好处吧，大家都能睡个安稳觉。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/814c599b-cf83-4347-b53b-288d7e181534.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="办公装备："&gt;办公装备：&lt;/h2&gt;
&lt;p&gt;1、一个小型工作站，32G 内存，i9 处理器，运行起来相当丝滑。&lt;/p&gt;

&lt;p&gt;2、两个显示器，如果觉得不够可以去 IT 领取，不需要审批。&lt;/p&gt;

&lt;p&gt;3、两个笔记本电脑。&lt;/p&gt;

&lt;p&gt;4、随时可以申请可升降办公桌。&lt;/p&gt;

&lt;p&gt;5、网速超级快，整套装备使用体验相当好。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/305a89b9-120a-4400-9fd2-26e509e57ee0.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="工作氛围"&gt;工作氛围&lt;/h2&gt;
&lt;p&gt;同事都很 nice，非常有耐心的解答各种问题，氛围相当好。&lt;/p&gt;

&lt;p&gt;办公室一点都不拥挤，“地广人稀”，也从来不用担心找不到会议室用，资源非常充足。&lt;/p&gt;

&lt;p&gt;这是楼层休息区。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/73103426-c7e6-4752-b2db-bebb3c20d647.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;每层会有几个休息舱，方便午休的同事。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/d4d55c67-e8ab-4e84-9e43-0c22aed41150.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="福利"&gt;福利&lt;/h2&gt;
&lt;p&gt;1、全员持股，共同富裕的典范。另外还有股票内购福利，员工最多可以拿每个月工资的 15% 九折购买公司股票。&lt;/p&gt;

&lt;p&gt;2、入职就有 15 天年假，不论工作年限长短。长达 6 周的男性陪产假。&lt;/p&gt;

&lt;p&gt;3、覆盖配偶和子女的补充医疗保险。&lt;/p&gt;

&lt;p&gt;4、水果、饮料、零食、咖啡近乎无限供应。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/6ee05e97-7b61-4609-8c84-a7d8eaef3636.jpg!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/charleszhang/0234cd46-2f80-4489-a63d-698e553fcd17.jpg!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/charleszhang/669ac581-a537-4393-87f8-5a3bf4dea97b.jpg!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/charleszhang/a8441eb2-f4f7-443c-8c14-1df74a0f46f5.jpg!large" title="" alt=""&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;点击下方链接了解更多：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mp.weixin.qq.com/s?__biz=MzU2NzczMDY1NA==&amp;amp;mid=2247483791&amp;amp;idx=1&amp;amp;sn=10c8fe70fd3266f7cad19f94c66d18f8&amp;amp;chksm=fc99893ecbee00288f84cbbe3ccb5e988343064d30152eea33d0454a1f11b7106c41da6d542e&amp;amp;scene=21#wechat_redirect" rel="nofollow" target="_blank" title=""&gt;聊聊微软面试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mp.weixin.qq.com/s?__biz=MzU2NzczMDY1NA==&amp;amp;mid=2247483796&amp;amp;idx=1&amp;amp;sn=62897f04e9aa09088a0787014dfca976&amp;amp;chksm=fc998925cbee00335afc4781bc7e743419d0941643d6e152657173758419452087b5b0334411&amp;amp;scene=21#wechat_redirect" rel="nofollow" target="_blank" title=""&gt;那些不加班的公司名单&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mp.weixin.qq.com/s?__biz=MzU2NzczMDY1NA==&amp;amp;mid=2247483791&amp;amp;idx=3&amp;amp;sn=8e017272daebaa34adbf1b0655602f9a&amp;amp;chksm=fc99893ecbee002829e1a4604f8770dec8e6ae7f7e27f54be06f02a259357790f4d8ba2fa91b&amp;amp;scene=21#wechat_redirect" rel="nofollow" target="_blank" title=""&gt;微软所有在招职位列表&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;个人微信：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/a8efdfe4-a9fe-4274-b32a-74286a978f1f.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;公众号：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/691840e6-4597-400b-9086-50735b646091.jpg!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Sat, 26 Feb 2022 20:21:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/42163</link>
      <guid>https://ruby-china.org/topics/42163</guid>
    </item>
    <item>
      <title>聊聊微软面试</title>
      <description>&lt;p&gt;该文首发于&lt;a href="https://mp.weixin.qq.com/s?__biz=MzU2NzczMDY1NA==&amp;amp;mid=2247483773&amp;amp;idx=1&amp;amp;sn=28f50675abb183e1285ee9bbc09bde3b&amp;amp;chksm=fc9989cccbee00da5f1a49e519519c0369c1d6160c5921c23b28a0e609a8ad6d7eb659b04f3e#rd" rel="nofollow" target="_blank" title=""&gt;我的公众号&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;很多人问我微软面试主要考察什么以及如何准备面试，这里我结合自身面试经历和在准备面试过程中查阅的大量资料做一个全面的总结。但是这里不会给出任何具体面试题目，主要担心具体的题目会把人的思维限制住，光准备那几道题目，结果面试的时候一个都没用上。我尽可能提供准备面试的最详细指导，有任何问题可以留言或私信我。&lt;/p&gt;

&lt;p&gt;先介绍下个人背景，我是本科学历，非 985、211 院校，2009 年毕业后一直做软件开发。主要做 C++ 方面的开发，以 Windows 客户端和服务端开发为主，另外有三年的 Ruby On Rails 全栈开发经验。进入微软之前在国内某大厂做 Windows 客户端开发，在 2021 年 12 月份拿到了微软、腾讯的 Offer。微软的职位是服务端开发，title 是 Senior Software Engineer，工作地点在北京。&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;微软的社会招聘面试总共是 1 轮电话面试 +5 轮线上面试（疫情原因）。候选人以内推为主，微软暂时未与猎头公司合作。简历筛选通过后，先约电话面试时间，电话面试和每轮线上面试时间都在 1 小时左右。电话面试通过后，大约一周以后会约线上面试时间，线上面试用的是 Microsoft Teams，5 轮线上面试一天完成，效率杠杠滴~。线上面试大约一周后会告知结果，通过之后就是 offer、背调等流程。&lt;/p&gt;

&lt;p&gt;电话面试主要对候选人做初步筛选，问一些项目经历、基础知识、算法。千万不要大意，电话面试问的算法可能非常难，我当时被问到 3 个困难级别算法（后来才知道电话面试官是我未来的直接领导）。电话面试之前最好准备好纸和笔，算法题可以在纸上推演一下，有助于整理思路。&lt;/p&gt;
&lt;h2 id="关于编程语言"&gt;关于编程语言&lt;/h2&gt;
&lt;p&gt;微软的面试不太注重编程语言本身的考察，因为对一个优秀的程序员来说编程语言不算障碍，比如我主要做 C++ 方面的开发，但是面试的职位是用 C#。这点国外大厂都一样，我同学在亚马逊也是如此。面试写算法的时候用你自己熟悉的编程语言即可。编程语言问题仅在电话面试和其中一轮线上面试问到了几个，都是针对你熟悉的语言，对我来说就是 C++，难度不会太大。&lt;/p&gt;
&lt;h2 id="关于算法"&gt;关于算法&lt;/h2&gt;
&lt;p&gt;这是面试的重头戏，我是除了最后一轮外其余每轮都面了算法。方式是打开一个共享网址，手写代码，没有任何语法高亮和补全提示，所以平时一定要练好手写代码。难度大概是 leetcode 的中等和困难级别，也会有面试官自己想的题目，也会变换题目条件。leetcode 刷个 100 道以上应该算是门槛值吧。另外推荐阅读微软亚洲研究院的同事编写的《编程之美》，该书作者全是公司内部人士，有很大的参考价值。&lt;/p&gt;

&lt;p&gt;leetcode 题目太多了，目前总共有 2491 道题（还在不断增长中），不知道哪些是高频题，无从下手，除了买 plus 会员外，你还可以参考这个网站：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codetop.cc/home" rel="nofollow" target="_blank"&gt;https://codetop.cc/home&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;该网站也是微软的一个同事开发的，收录各大厂高频面试题。&lt;/p&gt;

&lt;p&gt;目前网上写 leetcode 题解的特别多，遇到问题的时候搜一下能找到很多精彩的题解。&lt;/p&gt;

&lt;p&gt;对于算法一定要特别重视，现在国内大厂也向国外大厂看齐，都考察算法，腾讯的 6 轮面试有 4 轮面了算法，其中第 3 轮几乎全是算法。leetcode 中国网站排名第一的哥们是前微软员工，他总共通过了 2456 道题！！&lt;/p&gt;

&lt;p&gt;还有一部分编程题不是纯算法，算法与实际问题结合，难度较大，一般不会让当场写代码，但是会让说思路，然后面试结束后把完整代码发给面试官。解决这种题目，平时的技术积累和思维方式很重要。&lt;/p&gt;
&lt;h2 id="关于系统设计"&gt;关于系统设计&lt;/h2&gt;
&lt;p&gt;这是面试的另一个重头戏，除了最后一轮外其余每轮都有系统设计题。系统设计的问题比较开放，主要是口述，有的也会让写伪代码。平时需要多积累，知识要有广度，对一些好的协议、系统设计都要了解，达到融会贯通、举一反三，这样才能在遇到未见过的问题时能从平时的知识积累里提炼出解决方案。Github 有一个仓库是专门针对系统设计面试题的，可以作为参考，但是仅依靠这个仓库不足以通过系统设计面试，平时还是要多思考、学习各种系统的实现方式。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/donnemartin/system-design-primer" rel="nofollow" target="_blank"&gt;https://github.com/donnemartin/system-design-primer&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="关于英语"&gt;关于英语&lt;/h2&gt;
&lt;p&gt;不同的部门面试时对英语的考察不一样，有的要求英文自我介绍，有的英文面试具体问题，准备个英文版的自我介绍应该是基本的要求。英语是微软的工作语言，后续的 offer、公司的资料、邮件全是英文，英语不好的话会比较吃力。英语对程序员也是非常重要，学好英语很有必要。我本人是英语 6 级，建议没有过 4 级的（以前我以为所有本科生都过了 4 级）努力补一下这块短板。&lt;/p&gt;
&lt;h2 id="项目经历"&gt;项目经历&lt;/h2&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;最后，有任何问题欢迎给我留言或私信，想要内推的可以加我微信，备注内推。祝大家都能找到理想的工作。&lt;/p&gt;

&lt;p&gt;点击查看&lt;a href="https://careers.microsoft.com/us/en/search-results" rel="nofollow" target="_blank" title=""&gt;微软所有在招职位列表&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/charleszhang/a8efdfe4-a9fe-4274-b32a-74286a978f1f.jpg!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Mon, 07 Feb 2022 20:30:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/42117</link>
      <guid>https://ruby-china.org/topics/42117</guid>
    </item>
    <item>
      <title>Crystal 程序结构</title>
      <description>&lt;p&gt;有木有想一起翻译 Crystal 官方文档的小伙伴&lt;img title=":stuck_out_tongue_closed_eyes:" alt="😝" src="https://twemoji.ruby-china.com/2/svg/1f61d.svg" class="twemoji"&gt; ，试着翻译了两篇，一个人动力不足&lt;img title=":joy:" alt="😂" src="https://twemoji.ruby-china.com/2/svg/1f602.svg" class="twemoji"&gt;，期待小伙伴们参与：&lt;a href="https://github.com/chinazhangchao/crystal-book-zh_CN" rel="nofollow" target="_blank"&gt;https://github.com/chinazhangchao/crystal-book-zh_CN&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Crystal 程序"&gt;Crystal 程序&lt;/h2&gt;
&lt;p&gt;Crystal 程序是一个全局对象，在里面可以定义类型、方法和文件局部变量。&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 在程序定义方法&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 在程序调用add方法&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;方法的返回值是最后一条表达式的值；无需显式使用 &lt;code&gt;return&lt;/code&gt; 表达式。当然也可以使用 &lt;code&gt;return&lt;/code&gt; ：&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;even?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当调用一个方法时，假如没有指定接收者，例如 &lt;code&gt;add(1, 2)&lt;/code&gt;，如果在当前类型或祖先链中没有找该方法，就会在全局程序中继续查找。&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;
    &lt;span class="c1"&gt;# 调用全局程序的 add 方法&lt;/span&gt;
    &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 调用 Foo 的 baz 方法&lt;/span&gt;
    &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果要调用全局程序的方法，不管当前类型是否定义了一个同名方法，都可以使用前缀 &lt;code&gt;::&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;
    &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 2&lt;/span&gt;
    &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 6&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全局声明的变量在方法内不可见：&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="c1"&gt;# error: undefined local variable or method 'x'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;方法调用时圆括号可以省略：&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# 等价于 add(1, 2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="主代码"&gt;主代码&lt;/h2&gt;
&lt;p&gt;主代码是编译运行程序时运行的代码，可以直接在源文件里写，无需放入 "main" 函数：&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 这是一个输出 "Hello Crystal!" 的程序&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello Crystal!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主代码也可以在类型声明里：&lt;/p&gt;
&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 这是一个输出 "Hello" 的程序&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt;
  &lt;span class="c1"&gt;# 这里的 'self' 是 Hello 类&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>charleszhang</author>
      <pubDate>Sat, 14 Jul 2018 10:49:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/37155</link>
      <guid>https://ruby-china.org/topics/37155</guid>
    </item>
    <item>
      <title>Rails 中的 MIME 类型解析规则</title>
      <description>&lt;p&gt;本文缘于在项目中遇到的一个问题，查阅了网上的资料和 Rails 源码后有一点收获，简单做个总结，有些地方不够全面，欢迎大家补充指正。&lt;/p&gt;
&lt;h2 id="相关背景"&gt;相关背景&lt;/h2&gt;
&lt;p&gt;Rails 项目中经常可以看到类似如下代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@users&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xml&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;xml: &lt;/span&gt;&lt;span class="vi"&gt;@users&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果想获取 xml 格式的数据，就在请求路径后面增加&lt;code&gt;.xml&lt;/code&gt;扩展名，比如&lt;code&gt;localhost:3000/users.xml&lt;/code&gt;，这样就可以拿到 xml 格式的返回。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;路径扩展名是 Rails 中 MIME 类型解析的一个影响因素，另一个影响因素是 HTTP 头字段 Accept。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="HTTP头字段Accept"&gt;HTTP 头字段 Accept&lt;/h2&gt;
&lt;p&gt;当浏览器发送请求的时候，它也会通知服务器自己能处理的内容类型。访问网站的时候可以通过浏览器的开发者工具查看它们发送的 Accept 头。&lt;/p&gt;

&lt;p&gt;下面是我的机器上不同浏览器发送的 Accept 头：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chrome: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Firefox: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Safari: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们看看 Chrome 的 Accept 头：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chrome: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Chrome 的头表明它可以处理 html 文档（text/html）、xhtml 文档（application/xhtml+xml）、xml 文档（application/xml）、webp 和 apng 格式的图片，以及其它别的格式。&lt;/p&gt;

&lt;p&gt;Accept 头里面有一个 q 值，它表示优先级。HTTP 标准里面是这么描述的（&lt;a href="https://tools.ietf.org/html/rfc7231#section-5.3.1" rel="nofollow" target="_blank" title=""&gt;https://tools.ietf.org/html/rfc7231#section-5.3.1&lt;/a&gt;）：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;5.3.1.  Quality Values&lt;/p&gt;

&lt;p&gt;Many of the request header fields for proactive negotiation use a common parameter, named "q" (case-insensitive), to assign a relative "weight" to the preference for that associated kind of content.  This weight is referred to as a "quality value" (or "qvalue") because the same parameter name is often used within server configurations to assign a weight to the relative quality of the various representations that can be selected for a resource.&lt;/p&gt;

&lt;p&gt;The weight is normalized to a real number in the range 0 through 1, where 0.001 is the least preferred and 1 is the most preferred; a value of 0 means "not acceptable".  If no "q" parameter is present, the default weight is 1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;简单说就是为了表示 Accept 中内容类型的优先级，使用一个参数 q 来作为权重，q 是一个 0 到 1 之间的实数，最小是 0.001，最大是 1。如果是 0 表示不接受这种内容类型。如果没有指定，q 默认值为 1。&lt;/p&gt;

&lt;p&gt;Accept 内容类型的优先级除了与 q 值和顺序有关，还与类型具体化程度有关，越具体的类型优先级越高（&lt;a href="https://tools.ietf.org/html/rfc7231#section-5.3.2" rel="nofollow" target="_blank" title=""&gt;https://tools.ietf.org/html/rfc7231#section-5.3.2&lt;/a&gt;）：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Media ranges can be overridden by more specific media ranges or
   specific media types.  If more than one media range applies to a
   given type, the most specific reference has precedence. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;举 RFC 里的例子：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accept: text/*, text/plain, text/plain;format=flowed, */*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 Accept 头里，类型优先级如下：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;text/plain;format=flowed&lt;/li&gt;
&lt;li&gt;text/plain&lt;/li&gt;
&lt;li&gt;text/*&lt;/li&gt;
&lt;li&gt;&lt;em&gt;/&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于 Accept 优先级到此为止，不继续深究，有兴趣的同学可以阅读 RFC 原文。&lt;/p&gt;
&lt;h2 id="遇到的问题"&gt;遇到的问题&lt;/h2&gt;
&lt;p&gt;我们的项目采用前后端分离的写法，后端 controller 中有如下代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:alert&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'用户密码被重置，请重新登录'&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;new_user_session_path&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="n"&gt;new_user_session_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;msg: &lt;/span&gt;&lt;span class="s1"&gt;'用户密码被重置，请重新登录'&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前端发送的请求头中 Accept 字段如下：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Accept: application/json, text/plain, */*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;按我的理解，接口应该返回 json 数据，结果返回的是 html 重定向。
经过后来几次测试、跟踪源码，现象如下：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;Accept&lt;/th&gt;
&lt;th&gt;返回&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;application/json, text/plain, */*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;html 重定向&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;application/json, text/plain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;json 数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;与 respond_to 代码块中声明格式的顺序有关，即&lt;code&gt;format.html&lt;/code&gt;、&lt;code&gt;format.json&lt;/code&gt;哪个在前，返回哪个&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;除了第一条，后面两条基本还是符合直觉的。接下来看看 Rails 判断返回格式的规则到底是什么样的？&lt;/p&gt;
&lt;h2 id="Rails对MIME的解析"&gt;Rails 对 MIME 的解析&lt;/h2&gt;
&lt;p&gt;答案当然要从 Rails 源码中找（Read the fucking source code ^_^），涉及的函数如下（在文件&lt;code&gt;rails/actionpack/lib/action_dispatch/http/mime_negotiation.rb&lt;/code&gt;中，代码细节先不用深究，后文还有分析）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# order是在respond_to代码块中声明的返回格式数组，formats函数返回前端需要的格式数组，见后面的定义&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;negotiate_mime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;formats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ALL&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;priority&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;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# formats函数解析前端需要的格式，返回解析后的格式数组&lt;/span&gt;
&lt;span class="c1"&gt;# 其中第二个条件分支中的accepts函数会解析Accept字段，注意前提是有Accept头并且校验合法&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formats&lt;/span&gt;
  &lt;span class="n"&gt;fetch_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"action_dispatch.request.formats"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;params_readable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
                        &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:format&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BadRequest&lt;/span&gt;
                        &lt;span class="kp"&gt;false&lt;/span&gt;
                      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params_readable&lt;/span&gt;
      &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:format&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;use_accept_header&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;valid_accept_header&lt;/span&gt;
      &lt;span class="n"&gt;accepts&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;extension_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;format_from_path_extension&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;extension_format&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;xhr?&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:js&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:html&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;set_header&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# accepts函数调用Mime::Type.parse完成真正的解析工作&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;accepts&lt;/span&gt;
  &lt;span class="n"&gt;fetch_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"action_dispatch.request.accepts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"HTTP_ACCEPT"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;

    &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;content_mime_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;set_header&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="c1"&gt;# 这个正则表达式是用来判断是否是浏览器发出的请求，不是浏览器发出的请求才返回true&lt;/span&gt;
  &lt;span class="no"&gt;BROWSER_LIKE_ACCEPTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/,\s*\*\/\*|\*\/\*\s*,/&lt;/span&gt;

  &lt;span class="c1"&gt;# 校验函数，在formats中被调用，用来检查Accept是否正确&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_accept_header&lt;/span&gt; &lt;span class="c1"&gt;# :doc:&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xhr?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;content_mime_type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="o"&gt;!~&lt;/span&gt; &lt;span class="no"&gt;BROWSER_LIKE_ACCEPTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# 此处是问题的关键，如果accept头里包含*/*和其他类型（如：Accept: application/json, text/plain, */*）则此函数返回false，&lt;/span&gt;
      &lt;span class="c1"&gt;# 因此formats函数里不会解析Accept，Accept不起作用，formats函数继续往下走，走到了else分支返回html。&lt;/span&gt;
      &lt;span class="c1"&gt;# 如果只有*/*（Accept: */*），此函数返回true，negotiate_mime函数会选择respond代码块里的第一个格式类型返回（详细分析见下文）&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过以上代码，可以大体看出 Rails 对返回格式的判断流程：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;判断请求是否带 format 后缀，如果带，则返回相应格式。如：&lt;a href="http://localhost:3000/users.xml" rel="nofollow" target="_blank"&gt;http://localhost:3000/users.xml&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;判断请求是否有 Accept 头并且是合法头，如果是，则解析 Accept，返回 Accept 中的格式。&lt;/li&gt;
&lt;li&gt;判断请求是否有&lt;code&gt;format_from_path_extension&lt;/code&gt;的格式，不好意思，这个暂时没来得及研究是啥，欢迎大神们补充^_^。&lt;/li&gt;
&lt;li&gt;判断请求是否是 Ajax 调用，如果是，返回 javascript 格式数据。&lt;/li&gt;
&lt;li&gt;以上都不满足，返回 html 格式。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="对浏览器的特殊处理"&gt;对浏览器的特殊处理&lt;/h2&gt;
&lt;p&gt;从上面的代码里看到，Rails 对浏览器发出的请求做了特殊处理，如果是浏览器发出的，则不解析 Accept 头。为什么要对浏览器的请求做特殊处理？查阅的资料显示是因为早期浏览器设计不规范，大部分浏览器的请求头 Accept 字段第一个值是&lt;code&gt;application/xml&lt;/code&gt;，如果按照 Accept 解析就会给浏览器用户返回 xml 格式的数据，而这通常不是浏览器用户想要的。因此判断如果是浏览器就直接忽略 Accept 头。&lt;/p&gt;

&lt;p&gt;最后总结一下 Accept 的三种情形。&lt;/p&gt;
&lt;h2 id="Accept三种情形"&gt;Accept 三种情形&lt;/h2&gt;&lt;h3 id="1. Accept不包含*/*"&gt;1. Accept 不包含*/*&lt;/h3&gt;
&lt;p&gt;假设接口中有如下代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;html: &lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;p&amp;gt;this is html&amp;lt;/p&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html_safe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="s1"&gt;'this is json'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Accept 的值为：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application/json, text/plain
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时&lt;code&gt;formats&lt;/code&gt;函数中&lt;code&gt;valid_accept_header&lt;/code&gt;返回 true，Rails 会解析 Accept，&lt;code&gt;formats&lt;/code&gt;函数最终返回如下格式顺序的数组：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json
plain
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;negotiate_mime&lt;/code&gt;函数中的&lt;code&gt;order&lt;/code&gt;数组中包含的格式是：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html
json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种情况下，代码遍历&lt;code&gt;formats&lt;/code&gt;，如果&lt;code&gt;order&lt;/code&gt;中有匹配的格式就返回。&lt;code&gt;format&lt;/code&gt;的第一个格式是 json，&lt;code&gt;order&lt;/code&gt;中有匹配，此时返回 json 数据。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结论：Accept 不包含*/*时，按照 Accept 的优先级返回。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="2. Accept头是*/*"&gt;2. Accept 头是*/*&lt;/h3&gt;
&lt;p&gt;Accept 的值为：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接口代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;html: &lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;p&amp;gt;this is html&amp;lt;/p&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html_safe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="s1"&gt;'this is json'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时返回 html 格式。&lt;/p&gt;

&lt;p&gt;把&lt;code&gt;respond_to&lt;/code&gt;代码块调整一下顺序：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;respond_to do |format|
  format.json { render json: { data: 'this is json' } }
  format.html { render html: '&amp;lt;p&amp;gt;this is html&amp;lt;/p&amp;gt;'.html_safe }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时返回 json 格式。&lt;/p&gt;

&lt;p&gt;Accept 头是*/*时，解析 Accept 的结果为&lt;code&gt;Mime::ALL&lt;/code&gt;类型，从&lt;code&gt;negotiate_mime&lt;/code&gt;函数代码中可以明显看出，当请求类型为&lt;code&gt;Mime::ALL&lt;/code&gt;时，选择&lt;code&gt;order&lt;/code&gt;中的第一个格式返回，此时会返回&lt;code&gt;respond_to&lt;/code&gt;代码块中的第一个格式。&lt;/p&gt;

&lt;p&gt;如果没有&lt;code&gt;respond_to&lt;/code&gt;代码块呢？如果没有&lt;code&gt;respond_to&lt;/code&gt;代码块，但是在 view 目录下有&lt;code&gt;test.html.erb&lt;/code&gt;、&lt;code&gt;test.json.jbuilder&lt;/code&gt;两个文件，Rails 返回什么格式？这种情况下，Rails 按顺序遍历所有注册的 Mime 格式，找对应的匹配文件，一旦找到文件就返回该格式。这种情况下的返回格式依赖 Mime 格式的注册顺序，下面是 Mime 格式注册的代码（&lt;code&gt;rails/actionpack/lib/action_dispatch/http/mime_types.rb&lt;/code&gt;）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( application/xhtml+xml )&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( xhtml )&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/plain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(txt)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/javascript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:js&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( application/javascript application/x-javascript )&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:css&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/calendar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ics&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/csv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:csv&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/vcard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:vcf&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"text/vtt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:vtt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w(vtt)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:png&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(png)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"image/jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:jpeg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(jpg jpeg jpe pjpeg)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"image/gif"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:gif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(gif)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"image/bmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(bmp)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"image/tiff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tiff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(tif tiff)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"image/svg+xml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:svg&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"video/mpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:mpeg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(mpg mpeg mpe)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"audio/mpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:mp3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(mp1 mp2 mp3)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"audio/ogg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ogg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(oga ogg spx opus)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"audio/aac"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:m4a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( audio/mp4 )&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w(m4a mpg4 aac)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"video/webm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:webm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(webm)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"video/mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:mp4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(mp4 m4v)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"font/otf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:otf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(otf)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"font/ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ttf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(ttf)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"font/woff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:woff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(woff)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"font/woff2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:woff2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(woff2)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/xml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:xml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( text/xml application/x-xml )&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/rss+xml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rss&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/atom+xml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:atom&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/x-yaml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:yaml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( text/yaml )&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w(yml yaml)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:multipart_form&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/x-www-form-urlencoded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:url_encoded_form&lt;/span&gt;

&lt;span class="c1"&gt;# https://www.ietf.org/rfc/rfc4627.txt&lt;/span&gt;
&lt;span class="c1"&gt;# http://www.json.org/JSONRequest.html&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w( text/x-json application/jsonrequest )&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(pdf)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:zip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sx"&gt;%w(zip)&lt;/span&gt;
&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s2"&gt;"application/gzip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:gzip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w(application/x-gzip)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w(gz)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很明显，&lt;code&gt;text/html&lt;/code&gt;是第一个格式，因此在本例中返回&lt;code&gt;test.html.erb&lt;/code&gt;文件内容。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结论：Accept 头是*/*时，返回&lt;code&gt;respond_to&lt;/code&gt;代码块中的第一个格式。没有&lt;code&gt;respond_to&lt;/code&gt;代码块时，按 Mime 格式注册顺序寻找对应文件返回。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="3. Accept头包含*/*和其他内容"&gt;3. Accept 头包含*/*和其他内容&lt;/h3&gt;
&lt;p&gt;Accept 的值为：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application/json, text/plain, */*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接口代码如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;respond_to do |format|
  format.json { render json: { data: 'this is json' } }
  format.html { render html: '&amp;lt;p&amp;gt;this is html&amp;lt;/p&amp;gt;'.html_safe }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;暂时不考虑流程中的 3、4，此时返回 html，不会解析 Accept 头（原因在于&lt;code&gt;valid_accept_header&lt;/code&gt;函数返回 false，对浏览器的特殊处理），也不会受&lt;code&gt;respond_to&lt;/code&gt;代码块顺序影响。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结论：Accept 头包含*/*和其他内容，返回 html。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;文笔不好，内容又多，写的有点乱，大家见谅。&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.bigbinary.com/2010/11/23/mime-type-resolution-in-rails.html" rel="nofollow" target="_blank" title=""&gt;https://blog.bigbinary.com/2010/11/23/mime-type-resolution-in-rails.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/issues/9940" rel="nofollow" target="_blank" title=""&gt;https://github.com/rails/rails/issues/9940&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tools.ietf.org/html/rfc7231#section-5.3.1" rel="nofollow" target="_blank" title=""&gt;https://tools.ietf.org/html/rfc7231#section-5.3.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tools.ietf.org/html/rfc7231#section-5.3.2" rel="nofollow" target="_blank" title=""&gt;https://tools.ietf.org/html/rfc7231#section-5.3.2&lt;/a&gt;&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Tue, 20 Mar 2018 18:02:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/35274</link>
      <guid>https://ruby-china.org/topics/35274</guid>
    </item>
    <item>
      <title>Ruby 2.3 中的魔法注释 # frozen_string_literal: true</title>
      <description>&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;Ruby 中冻结的对象只会创建一次，以后遇到相同的对象会复用之前创建的对象，这样可以减少对象创建次数和垃圾回收次数。Ruby 中的符号、整数、浮点数默认都是冻结的，字符串字面量目前还不是。&lt;/p&gt;

&lt;p&gt;为了提高程序性能，&lt;strong&gt;在 Ruby 3 中，字符串字面量在所有文件中默认被冻结。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;为了过渡，Ruby2.3 增加了一个魔法注释：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="作用"&gt;作用&lt;/h2&gt;
&lt;p&gt;它告诉 Ruby，文件中的所有字符串字面量都被隐式冻结，不可修改，就像每一个字符串都调用了&lt;code&gt;freeze&lt;/code&gt;方法一样。 &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&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;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"12"&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="n"&gt;frozen&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="使用方式"&gt;使用方式&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;把该注释加在文件的第一行。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;另外，在 Ruby 2.3 中使用&lt;code&gt;--enable=frozen-string-literal&lt;/code&gt;标志运行 ruby，也会默认冻结所有文件中的字符串字面量。在单个文件中可以通过&lt;code&gt;# frozen_string_literal: false&lt;/code&gt;覆盖全局设置（Ruby 3 中也可以用此方式覆盖全局设置）。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ruby &lt;span class="nt"&gt;--enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frozen-string-literal t.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="如何修改"&gt;如何修改&lt;/h2&gt;
&lt;p&gt;如果想要修改字符串字面量怎么办？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;无论全局或每个文件如何设置，可以使用一元 + 运算符（注意优先级）来产生非冻结字符串或调用&lt;code&gt;dup&lt;/code&gt;方法来对其进行复制&lt;/strong&gt;：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&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="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还可以使用一元 - 运算符冻结可变（未冻结）字符串。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>charleszhang</author>
      <pubDate>Mon, 12 Mar 2018 16:01:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/35215</link>
      <guid>https://ruby-china.org/topics/35215</guid>
    </item>
    <item>
      <title>Ruby 安全调用运算符 (&amp;.)</title>
      <description>&lt;p&gt;今天在阅读 discourse 源码时，看到其中有很多地方使用&lt;code&gt;&amp;amp;.&lt;/code&gt;符号，如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:whisper&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;viewed_by&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;staff?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不明白干啥的，搜索了一下，发现国外一篇文章总结的很好，在此记录一下。&lt;/p&gt;

&lt;p&gt;安全调用运算符（&amp;amp;.）是 Ruby 2.3.0中最吸引人的新增特性之一。类似的运算符在C#和Groovy语言中早已存在，用的语法略有不同，是符号（?.）。那么这个运算符的作用是什么呢？&lt;/p&gt;
&lt;h2 id="使用场景"&gt;使用场景&lt;/h2&gt;
&lt;p&gt;假设有一个 account 对象，它有一个关联的 owner 对象，现在想要获取 owner 的 address 属性。稳妥的不引发 nil 异常的写法如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种写法很啰嗦、很不爽。ActiveSupport 提供的 try 方法有类似的写法（两者稍微有一点区别，我们稍后讨论）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:owner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码完成同样的功能——它要么返回 address 字段值要么返回 nil（当调用链中有 nil 值时）。但第一个例子的写法也有可能返回 false，比如当 owner 值为 false 的时候。&lt;/p&gt;
&lt;h2 id="使用&amp;amp;."&gt;使用&amp;amp;.&lt;/h2&gt;
&lt;p&gt;我们可以使用安全调用运算符重写前面的例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽然语法看起来有点奇怪，但我猜你会很快爱上它，因为它的确让代码更简洁。&lt;/p&gt;
&lt;h2 id="更多例子"&gt;更多例子&lt;/h2&gt;
&lt;p&gt;接下来让我们详细比较一下这三种方式：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# account without an owner&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; NoMethodError: undefined method `address' for nil:NilClass&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:owner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个例子没有什么特别的。但如果 owner 的值为 false 呢（虽然可能性不大，但在垃圾代码的狂躁世界里并非不可能）？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; NoMethodError: undefined method `address' for false:FalseClass `&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:owner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; undefined method `address' for false:FalseClass`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;第一个亮点出现了——&amp;amp;.运算符只跳过 nil 但可以识别 false！它与&lt;code&gt;s1 &amp;amp;&amp;amp; s1.s2 &amp;amp;&amp;amp; s1.s2.s3&lt;/code&gt;这种写法并不完全一样。&lt;/strong&gt;
如果 owner 不为空但是并没有 address 属性（方法）呢？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; NoMethodError: undefined method `address' for #&amp;lt;Object:0x00559996b5bde8&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; NoMethodError: undefined method `address' for #&amp;lt;Object:0x00559996b5bde8&amp;gt;`&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:owner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; NoMethodError: undefined method `address' for #&amp;lt;Object:0x00559996b5bde8&amp;gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;很遗憾，try 方法没有检查接收者是否响应给定的方法。这就是为什么应该尽量使用 try 的严格版本——try! 的原因：&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;account.try!(:owner).try!(:address)
# =&amp;gt; NoMethodError: undefined method `address' for #&amp;lt;Object:0x00559996b5bde8&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="注意事项"&gt;注意事项&lt;/h2&gt;
&lt;p&gt;这段作者也有疑惑，我就自己发挥一下了。第一个例子不用解释。第二个我理解为先调用 nil?，此时返回 false（主对象 main 调用 nil？的结果）。然后再调用 nil?，还是返回 false（相当于 false.nil?）。&lt;strong&gt;第三个例子表明&amp;amp;.符号在对象为 nil 时不会再去检查是否响应对应的方法&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nil.nil?
# =&amp;gt; true

nil?.nil?
# =&amp;gt; false

nil&amp;amp;.nil?
# =&amp;gt; nil
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Array#dig and Hash#dig"&gt;Array#dig and Hash#dig&lt;/h2&gt;
&lt;p&gt;在我看来，dig 方法是这个版本中最有用的特性。我们再也不用写下面这样恶心的代码：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address = params[:account].try(:[], :owner).try(:[], :address)

# or

address = params[:account].fetch(:owner) .fetch(:address)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只需简单的使用Hash#dig来达到同样的目的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address = params.dig(:account, :owner, :address)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="写在后面"&gt;写在后面&lt;/h2&gt;
&lt;p&gt;我非常不喜欢处理动态语言中的空值，安全调用运算符和 dig 的出现使得代码可以写得非常简洁。（最后一句不翻了，Ruby 2.3.0 早已发布，祝大家编码愉快&lt;code&gt;^_^&lt;/code&gt;）&lt;/p&gt;

&lt;p&gt;英文原文地址：&lt;a href="http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/" rel="nofollow" target="_blank"&gt;http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/&lt;/a&gt;&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Thu, 08 Mar 2018 11:50:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/35195</link>
      <guid>https://ruby-china.org/topics/35195</guid>
    </item>
    <item>
      <title>Rails 5 需要用 throw (:abort) 终止回调执行，官方文档未更新，坑……</title>
      <description>&lt;p&gt;通过 false 终止回调执行不起作用，google 一番未果，怒跟源码，发现如下：&lt;/p&gt;

&lt;p&gt;activesupport-5.0.0.1/lib/active_support/callbacks.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deprecated_false_terminator&lt;/span&gt; &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
  &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_lambda&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;terminate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="kp"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:abort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;halt_and_display_warning_on_return_false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;display_deprecation_warning_for_false_terminator&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;terminate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&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;terminate&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要用 throw(:abort) 终止回调执行，官方文档还未更新&lt;/p&gt;

&lt;p&gt;&lt;a href="http://guides.rubyonrails.org/active_record_callbacks.html#halting-execution" rel="nofollow" target="_blank"&gt;http://guides.rubyonrails.org/active_record_callbacks.html#halting-execution&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;国外一哥们的总结：&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.bigbinary.com/2016/02/13/rails-5-does-not-halt-callback-chain-when-false-is-returned.html" rel="nofollow" target="_blank"&gt;http://blog.bigbinary.com/2016/02/13/rails-5-does-not-halt-callback-chain-when-false-is-returned.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Tue, 29 Nov 2016 16:11:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/31733</link>
      <guid>https://ruby-china.org/topics/31733</guid>
    </item>
    <item>
      <title>写了一个简单易用的定义枚举库</title>
      <description>&lt;p&gt;源码：&lt;a href="https://github.com/chinazhangchao/def_enum_helper" rel="nofollow" target="_blank"&gt;https://github.com/chinazhangchao/def_enum_helper&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="DefEnumHelper"&gt;DefEnumHelper&lt;/h2&gt;
&lt;p&gt;Define very powerful enum class.&lt;/p&gt;
&lt;h2 id="Installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;Add this line to your application's Gemfile:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'def_enum_helper'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then execute:&lt;/p&gt;

&lt;p&gt;$ bundle&lt;/p&gt;

&lt;p&gt;Or install it yourself as:&lt;/p&gt;

&lt;p&gt;$ gem install def_enum_helper&lt;/p&gt;
&lt;h2 id="Usage"&gt;Usage&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 默认枚举从1开始递增&lt;/span&gt;

&lt;span class="n"&gt;def_enum&lt;/span&gt; &lt;span class="ss"&gt;:YinYang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;YANG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"阳"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;YIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"阴"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;YANG&lt;/span&gt;      &lt;span class="c1"&gt;# 1&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;YIN&lt;/span&gt;       &lt;span class="c1"&gt;# 2&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;            &lt;span class="c1"&gt;# 1&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;          &lt;span class="c1"&gt;# 阳&lt;/span&gt;
&lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:YANG&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="c1"&gt;# same as YinYang[1]&lt;/span&gt;
&lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"阳"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="c1"&gt;# same as YinYang[1]&lt;/span&gt;
&lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;             &lt;span class="c1"&gt;# 所有枚举的struct array [#&amp;lt;struct index=1, display="阳"&amp;gt;, # &amp;lt;struct index=2, display="阴"&amp;gt;]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;      &lt;span class="c1"&gt;# 2&lt;/span&gt;
&lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;struct index=1, display="阳"&amp;gt; #&amp;lt;struct index=2, display="阴"&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;YinYang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_hash_array&lt;/span&gt; &lt;span class="c1"&gt;# [{:index=&amp;gt;1, :display=&amp;gt;"阳"}, {:index=&amp;gt;2, :display=&amp;gt;"阴"}]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;YingYang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json_array&lt;/span&gt;  &lt;span class="c1"&gt;# [{"index":1,"display":"阳"},{"index":2,"display":"阴"}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 自定义枚举整数值&lt;/span&gt;

&lt;span class="n"&gt;def_enum_with_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:DataOperationType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'插入'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:UPDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'修改'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DELETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'删除'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;DataOperationType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;INSERT&lt;/span&gt;  &lt;span class="c1"&gt;# 3&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 自定义枚举对象&lt;/span&gt;
&lt;span class="no"&gt;CustomStruct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:display&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:short_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def_enum_struct_with_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:Country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="p"&gt;{&lt;/span&gt;
                             &lt;span class="no"&gt;CHINA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;CustomStruct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'中华人民共和国'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'中国'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                             &lt;span class="no"&gt;USA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;CustomStruct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'美利坚合众国'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'美国'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                         &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="no"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:CHINA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;         &lt;span class="c1"&gt;# &amp;lt;struct CustomStruct index=1, display="中华人民共和国", short_name="中国"&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="License"&gt;License&lt;/h3&gt;
&lt;p&gt;(MIT License) - Copyright (c) 2016 Charles Zhang&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Wed, 23 Nov 2016 18:20:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/31673</link>
      <guid>https://ruby-china.org/topics/31673</guid>
    </item>
    <item>
      <title>Rails 5 如何自定义处理 ActionDispatch::ParamsParser::ParseError 异常</title>
      <description>&lt;p&gt;参考这个方式处理 &lt;a href="https://robots.thoughtbot.com/catching-json-parse-errors-with-custom-middleware" rel="nofollow" target="_blank"&gt;https://robots.thoughtbot.com/catching-json-parse-errors-with-custom-middleware&lt;/a&gt; ，提示“No such middleware to insert before: ActionDispatch::ParamsParser”。&lt;/p&gt;

&lt;p&gt;运行 rake middleware 结果如下：（确实没有 ActionDispatch::ParamsParser）&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Sendfile&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Static&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Executor&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Strategy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LocalCache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Middleware&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Runtime&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MethodOverride&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestId&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Sprockets&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;QuietAssets&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logger&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ShowExceptions&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;WebConsole&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Middleware&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DebugExceptions&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RemoteIp&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Reloader&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cookies&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CookieStore&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Flash&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Head&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ConditionalGet&lt;/span&gt;
&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ETag&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;RailsChecheDataaudit&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;改为&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_before&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'CatchJsonParseErrors'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;仍然无法捕获异常。应该如何处理？&lt;/p&gt;</description>
      <author>charleszhang</author>
      <pubDate>Wed, 19 Oct 2016 14:51:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/31378</link>
      <guid>https://ruby-china.org/topics/31378</guid>
    </item>
  </channel>
</rss>
