forked from stylesRoR/stylesror.github.com
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
2303 lines (1643 loc) · 84.1 KB
/
index.html
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html><html lang="zhCN"><head><title>Ruby & Rails 风格指导 简体中文</title><meta charset="utf-8"><meta name="description" content="Ruby 及 Rails 风格指导"><link rel="stylesheet" href="style/garden.css" media="all"><link rel="stylesheet" href="style/print.css" media="print"><link rel="shortcut icon" href="image/ruby-china.png"><!--[if lt IE 9]>
<script src="javascript/html5.js"></script>
<![endif]-->
</head><body><!-- Navigation--><nav id="nav_main"><div><ul><li class="active"><a href="http://stylesRoR.github.com" title="Ruby & Rails 风格指导">zhCN</a></li><li><a href="http://stylesRoR.github.com/zhTW/index.html" title="Ruby & Rails 風格指南">zhTW</a></li></ul><a id="top" href="#intro" title="Back to top">#top</a><a id="hide_menu" class="tablet">Hide Menu</a></div><ul><li class="nav_intro"><h1><a href="#intro">简介</a></h1><ul><li><a href="#intro.authors">Ruby is Big in China <a href="http://ruby-china.org"><img src="image/ruby-china.png" alt="Ruby China" title="" /></a></a></li><li><a href="#intro.license">内容许可</a></li></ul></li><li class="nav_ruby"><h1><a href="#ruby">Ruby 风格指导</a></h1><ul><li><a href="#ruby.prelude">序幕</a></li><li><a href="#ruby.ruby-style-guide">Ruby 风格指南</a></li><li><a href="#ruby.source-formatting">源代码排版</a></li><li><a href="#ruby.syntax">语法</a></li><li><a href="#ruby.naming">命名</a></li><li><a href="#ruby.comments">注释 (Comments)</a></li><li><a href="#ruby.annotations">注解 (Annotations)</a></li><li><a href="#ruby.class">类别</a></li><li><a href="#ruby.exceptions">异常</a></li><li><a href="#ruby.collections">集合</a></li><li><a href="#ruby.string">字串</a></li><li><a href="#ruby.regexp">正則表示法</a></li><li><a href="#ruby.percent-literals">百分比字面</a></li><li><a href="#ruby.metaprog">元编程</a></li><li><a href="#ruby.misc">其它</a></li><li><a href="#ruby.contr">貢獻</a></li><li><a href="#ruby.stw">口耳相傳</a></li></ul></li><li class="nav_rails"><h1><a href="#rails">Rails 风格指导</a></h1><ul><li><a href="#rails.dev-rails-app">序幕</a></li><li><a href="#rails.config">配置</a></li><li><a href="#rails.routing">路由</a></li><li><a href="#rails.controllers">控制器</a></li><li><a href="#rails.models">模型</a></li><li><a href="#rails.migrations">迁移</a></li><li><a href="#rails.views">视图</a></li><li><a href="#rails.internalization">国际化</a></li><li><a href="#rails.assets">Assets</a></li><li><a href="#rails.mailers">Mailers</a></li><li><a href="#rails.bundler">Bundler</a></li><li><a href="#rails.manage-process">管理进程</a></li><li><a href="#rails.testing-rails-app">测试 Rails 应用</a></li><li><a href="#rails.further">延伸阅读</a></li></ul></li></ul></nav><!-- Mobile navigation--><nav id="nav_mobile"><a id="nav_prev_section" href="#">prev section<span class="nav_section_name">section name</span></a><a id="nav_next_section" href="#">next section<span class="nav_section_name">section name</span></a><a id="show_menu">show menu</a></nav><!-- Sections--><section id="intro"><!-- Introduction--><header id="intro.intro"><h1>简介</h1></header><!-- Articles--><article id="intro.authors"><h2>Ruby is Big in China <a href="http://ruby-china.org"><img src="image/ruby-china.png" alt="Ruby China" title="" /></a></h2><div><h2>关于作者</h2>
<p>这篇文章翻译自 bbatsov 所写的 <a href="https://github.com/bbatsov/ruby-style-guide">Ruby Style Guide</a> 及 <a href="https://github.com/bbatsov/rails-style-guide">Rails Style Guide</a>。</p>
<h2>关于译者</h2>
<p>Juanito Fatas,你可以在 twitter、ruby-china 找到我。</p>
<p>不好意思,我刚学习 Ruby 及 Rails,翻译的质量可能不太好,</p>
<p>有错误不好的地方,麻烦去 github 开任务给我。</p>
</div><div><h3>提交建议</h3>
<p><a href="https://github.com/JuanitoFatas/ruby-style-guide">Ruby Style Guide 建議</a></p>
<p><a href="https://github.com/JuanitoFatas/rails-style-guide">Rails Style Guide 建議</a></p>
<h2>为什么翻译此文</h2>
<p>几个礼拜前我开始学习 Ruby 及 Rails。我刚好看到了这两个指导,我觉得这可以让我有全观的理解及避免可能犯的错误,这样子再去学习会更有方向,这是我翻译的原因。</p>
<p>而关于风格,我看过一本书,有一段文字,诠释的很好:</p>
<p>Style is necessary only where understanding is missing. A corollary to this is that sometimes the only way to effectively use something you don’t understand is to copy styles observed elsewhere.
– <a href="http://letoverlambda.com/">From Let Over Lambda</a></p>
<p>我希望你读的愉快。</p></div></article><article id="intro.license"><h2>内容许可</h2><div><p><a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/">内容采用创用 CC 2.5 授权释出</a></p>
<p><img src="http://i.creativecommons.org/l/by-nc-sa/2.5/cn/88x31.png" alt="Creative Commons License" title="" /></p></div></article></section><section id="ruby"><!-- Introduction--><header id="ruby.intro"><h1>Ruby 风格指导</h1></header><!-- Articles--><article id="ruby.prelude"><h2>序幕</h2><div><aside>
<p>风格是从伟大事物中分离出的美好事物。 <br/>
-- Bozhidar Batsov</p>
</aside>
<p>作为 Ruby 开发者,有一件总是令我烦心的事 — Python 开发者有一份好的编程风格参考指南(<a href="http://www.python.org/dev/peps/pep-0008/">PEP-8</a>) 而我们永远没有一份官方指南,一份记录 Ruby 编程风格及最佳实践的指南。而我们确信风格很重要。我也相信这些好家伙们,像我们是 Ruby 开发者,应该可以自己产生一份这个梦寐以求的文档。</p>
<p>这份指南开始是作为我们公司内部 Ruby 编程指南(由我所写的)。进行到某个部分时,我决定要把我的成果贡献给广大的 Ruby 社群,而且这个世界需要从另一个公司内部的一点帮助。然而这个世界也可以从由社群制定及策动的一系列 Ruby 编程惯例、实践及风格中受益。</p>
<p>在开始写这份指南时,我收到世界上很多优秀 Ruby 社群用户们的反馈。感谢所有的建议及帮助!我们同心协力创造一个能够让每一个 Ruby 开发者受益的资源。</p>
<p>顺道一提,如果你对 Rails 感兴趣,你可以看看这份互补的 <a href="https://github.com/bbatsov/rails-style-guide">Ruby on Rails 3 风格指南</a>。</p></div></article><article id="ruby.ruby-style-guide"><h2>Ruby 风格指南</h2><div><p>这份 Ruby 风格指南向你推荐现实世界中,Ruby 程序员如何写出可被别的Ruby程序员维护的代码。一份风格指南,反映出现实世界中的用法,带有一个理想,帮助人们避免使用危险的代码 — 不管它看起来有对好。</p>
<p>本指南分成数个相关规则的小节。我试着在每个规则后说明理由(如果省略的话,我相信理由是显而易见的)。</p>
<p>我没有想到所有的规则 — 他们大致上是基于,我作为一个专业软体工程师的广泛生涯,从 Ruby 社群成员所得到的反馈及建议,和数个高度评价的 Ruby 程式设计资源,像是 <a href="http://pragprog.com/book/ruby3/programming-ruby-1-9">"Programming Ruby 1.9"</a> 以及 <a href="http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177">"The Ruby Programming Language"</a>。</p>
<p>本指南仍在完善中 — 某些规则缺乏实例,某些规则没有例子来清楚地演示它们。在最后交付时,这些议题将会被解决 — 就先把它们记在心理吧。</p>
<p>你可以使用 <a href="https://github.com/TechnoGate/transmuter">Transmuter</a> 来产生本指南的一份 PDF 或 HTML 复本。</p></div></article><article id="ruby.source-formatting"><h2>源代码排版</h2><div><aside>
<p>几乎每人都深信,每一个除了自己的风格都又丑又难读。把 "除了自己的" 拿掉,他们或许是对的...<br/>
-- Jerry Coffin (论缩排)</p>
</aside>
<p>使用 <code>UTF-8</code> 作为源文件的编码。</p>
<p>每个缩排层级使用两个<strong>空格</strong>。 </p>
<pre><code># 好
def some_method
do_something
end
# 差 - 四个空格
def some_method
do_something
end
</code></pre>
<p>使用 Unix 风格的行编码(缺省涵盖 BSD/Solaris/Linux/OSX 的用户,Windows 用户要格外小心。)
如果你使用 Git ,你也许会想加入下面这个配置,来保护你的项目被 Windows 的行编码侵入:</p>
<p><code>$ git config --global core.autocrlf true</code></p>
<p>使用空格来围绕操作符,逗号 <code>,</code> 、冒号 <code>:</code> 及分号 <code>;</code> 之后,围绕在 <code>{</code> 和 <code>}</code> 之前。空格可能对(大部分)Ruby 直译器来说是无关紧要的,但正确的使用是写出可读性高的代码的关键。</p>
<pre><code>sum = 1 + 2
a, b = 1, 2
1 > 2 ? true : false; puts 'Hi'
[1, 2, 3].each { |e| puts e }
</code></pre>
<p>唯一的例外是当使用指数操作符时:</p>
<pre><code># 差
e = M * c ** 2
# 好
e = M * c**2
</code></pre>
<p>不要有空格在 <code>(</code> 、 <code>[</code> 之后,或 <code>]</code> 、 <code>)</code> 之前。</p>
<pre><code>some(arg).other
[1, 2, 3].length
</code></pre>
<p>把 <code>when</code> 跟 <code>case</code> 缩排在同一层。我知道很多人不同意这一点,但这是“The Ruby Programming Language”及“Programming Ruby”所使用的风格。</p>
<pre><code>case
when song.name == 'Misty'
puts 'Not again!'
when song.duration > 120
puts 'Too long!'
when Time.now.hour > 21
puts "It's too late"
else
song.play
end
kind = case year
when 1850..1889 then 'Blues'
when 1890..1909 then 'Ragtime'
when 1910..1929 then 'New Orleans Jazz'
when 1930..1939 then 'Swing'
when 1940..1950 then 'Bebop'
else 'Jazz'
end
</code></pre>
<p>在 <code>def</code> 之间使用空行,并且把方法分成合乎逻辑的段落。</p>
<pre><code>def some_method
data = initialize(options)
data.manipulate!
data.result
end
def some_method
result
end
</code></pre>
<p>当一个方法呼叫的参数扩展至多行时,排列它们。</p>
<pre><code># 一开始(一行太长)
def send_mail(source)
Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
end
# 差(一般的缩排)
def send_mail(source)
Mailer.deliver(
to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
# 差(两倍缩排)
def send_mail(source)
Mailer.deliver(
to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
# 好
def send_mail(source)
Mailer.deliver(to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
</code></pre>
<p>使用 RDoc 以及它的惯例来撰写 API 文档。不要在注解区块及 <code>def</code> 之前放一个空行。
让每一行保持少于 80 个字符。
避免尾随的空白。</p></div></article><article id="ruby.syntax"><h2>语法</h2><div><p>使用 <code>def</code> 时,当有参数时使用括号。当方法不接受任何参数时,省略括号。</p>
<pre><code>def some_method
# body omitted
end
def some_method_with_arguments(arg1, arg2)
# body omitted
end
</code></pre>
<p>永远不要使用 <code>for</code> ,除非你很清楚为什么。大部分情况应该使用迭代器来取代。 <code>for</code> 是由 <code>each</code> 所实作的(所以你加入了一层的迂回),但出乎意料的是 — <code>for</code> 并没有包含一个新的视野(不像是 <code>each</code> )而在这个区块中定义的变量将会被外部所看到。</p>
<pre><code>arr = [1, 2, 3]
# 差
for elem in arr do
puts elem
end
# 好
arr.each { |elem| puts elem }
</code></pre>
<p>永远不要在多行的 <code>if/unless</code> 使用 <code>then</code></p>
<pre><code># 差
if some_condition then
# body omitted
end
# 好
if some_condition
# body omitted
end
</code></pre>
<p>偏爱三元操作符 <code>? :</code> 胜于 <code>if/then/else/end</code> 结构。它更为常见及更精准。</p>
<pre><code># 差
result = if some_condition then something else something_else end
# 好
result = some_condition ? something : something_else
</code></pre>
<p>使用一个表达式给一个三元操作符的分支。这也意味着三元操作符不要嵌套。嵌套情况使用 <code>if/else</code> 结构。</p>
<pre><code># 差
some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
# 好
if some_condition
nested_condition ? nested_something : nested_something_else
else
something_else
end
</code></pre>
<p>永远不要使用 <code>if x: ...</code> — 它已经在 Ruby 1.9 被移除了。使用三元操作符来取代。</p>
<pre><code># 差
result = if some_condition: something else something_else end
# 好
result = some_condition ? something : something_else
</code></pre>
<p>永远不要使用 <code>if x: ...</code> 使用三元操作符来取代。</p>
<p>一行的情况使用 <code>when x then ...</code>。替代方案的语法 <code>when x: ...</code> 在 Ruby 1.9 被移除了。</p>
<p>永远不要使用 <code>when x: ...</code>。参考前一个规则。</p>
<p>布尔表达式使用 <code>&&/||</code>,控制流程使用 <code>and/or</code>。
(经验法则:如果你需要使用外部括号,你正在使用错误的操作符。)</p>
<pre><code># 布尔表达式
if some_condition && some_other_condition
do_something
end
# 控制流程
document.saved? or document.save!
</code></pre>
<p>避免多行的 <code>? :</code>(三元操作符),使用 <code>if/unless</code> 来取代。</p>
<p>偏爱 <code>if/unless</code> 修饰符当你有单行的主体。</p>
<p>另一个好的方法是使用控制流程的 <code>and/or</code>。</p>
<pre><code># 差
if some_condition
do_something
end
# 好
do_something if some_condition
# 另一个好方法
some_condition and do_something
</code></pre>
<p>否定条件偏爱 <code>unless</code> 优于 <code>if</code>(或是控制流程 <code>or</code>)。</p>
<pre><code># 差
do_something if !some_condition
# 好
do_something unless some_condition
# 另一个好方法
some_condition or do_something
</code></pre>
<p>永远不要使用 <code>unless</code> 搭配 <code>else</code> 。将它们改写成肯定条件。</p>
<pre><code># 差
unless success?
puts 'failure'
else
puts 'success'
end
# 好
if success?
puts 'success'
else
puts 'failure'
end
</code></pre>
<p>不要使用括号围绕 <code>if/unless/while</code> 的条件式,除非这条件包含了一个赋值(见下面使用 <code>=</code> (一个赋值)的返回值)。</p>
<pre><code># 差
if (x > 10)
# body omitted
end
# 好
if x > 10
# body omitted
end
# 好
if (x = self.next_value)
# body omitted
end
</code></pre>
<p>忽略围绕方法参数的括号,如内部 DSL (如:Rake, Rails, RSpec),Ruby 中带有“关键字”状态的方法(如:<code>attr_reader</code>, <code>puts</code>)以及属性存取方法。</p>
<p>所有其他的方法呼叫使用括号围绕参数。</p>
<pre><code>class Person
attr_reader :name, :age
# 忽略
end
temperance = Person.new('Temperance', 30)
temperance.name
puts temperance.age
x = Math.sin(y)
array.delete(e)
</code></pre>
<p>单行区块喜好 <code>{...}</code> 胜于 <code>do..end</code>。多行区块避免使用 <code>{...}</code>(多行串连总是丑陋)。在 <code>do...end</code> 、“控制流程”及“方法定义” ,永远使用 <code>do...end</code> (如 Rakefile 及某些 DSL)。串连时避免使用 <code>do...end</code>。</p>
<pre><code>names = ["Bozhidar", "Steve", "Sarah"]
# 好
names.each { |name| puts name }
# 差
names.each do |name|
puts name
end
# 好
names.select { |name| name.start_with?("S") }.map { |name| name.upcase }
# 差
names.select do |name|
name.start_with?("S")
end.map { |name| name.upcase }
某些人会争论多行串连时,使用 {...} 看起来还可以,
但他们应该问问自己 — 这样代码真的可读吗
以及不能把区块内容取出来放到绝妙的方法中吗。
</code></pre>
<p>避免在不需要的场合时使用 <code>return</code> 。</p>
<pre><code># 差
def some_method(some_arr)
return some_arr.size
end
# 好
def some_method(some_arr)
some_arr.size
end
</code></pre>
<p>当赋予缺省值给方法参数时,使用空格围绕 <code>=</code> 操作符。</p>
<pre><code># 差
def some_method(arg1=:default, arg2=nil, arg3=[])
# 做些什么...
end
# 好
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
# 做些什么...
end
然而几本 Ruby 书建议第一个风格,第二个风格在实践中更为常见
(并可争议地可读性更高一点)。
</code></pre>
<p>避免在不需要的场合使用续行 <code>\</code> 。在实践中,尽量避免使用续行。</p>
<pre><code># 差
result = 1 - \
2
# 好 (但仍丑的跟地狱一样)
result = 1 \
- 2
</code></pre>
<p>使用 <code>=</code>(一个赋值)的返回值是好的,但用括号环绕赋值。</p>
<pre><code># 好 — 演示赋值的目标用途
if (v = array.grep(/foo/)) ...
# 差
if v = array.grep(/foo/) ...
# 也很好 — 演示赋值的目标用途及有正确的优先序
if (v = self.next_value) == "hello" ...
</code></pre>
<p>随意使用 <code>||=</code> 来初始化变量</p>
<pre><code># 仅在 name 为 nil 或 false 时,把名字设为 Bozhidar。
name ||= 'Bozhidar'
</code></pre>
<p>不要使用 <code>||=</code> 来初始化布尔变量。 </p>
<p>(想看看如果现在的值刚好是 <code>false</code> 时会发生什么。)</p>
<pre><code># 差 — 会把 enabled 设成真,即便它本来是假。
enabled ||= true
# 好
enabled = true if enabled.nil?
</code></pre>
<p>避免使用 Perl 风格的特别变量(像是 <code>$0-9</code>, <code>$`</code>, 等等)。它们看起来非常神秘以及不鼓励使用一行的脚本。</p>
<p>避免在方法名与左括号之间放一个空格。</p>
<pre><code># 差
f (3 + 2) + 1
# 好
f(3 + 2) + 1
</code></pre>
<p>如果方法的第一个参数由左括号开始,永远在这个方法呼叫里使用括号。</p>
<p>举个例子,写 <code>f((3+2) + 1)</code>。</p>
<p>总是使用 <code>-w</code> 来执行 Ruby 直译器,如果你忘了某个上述的规则,它就会警告你!</p>
<p>当你的哈希键是符号时,使用 Ruby 1.9 哈希字面语法。</p>
<pre><code># 差
hash = { :one => 1, :two => 2 }
# 好
hash = { one: 1, two: 2 }
</code></pre>
<p>使用新的 lambda 字面语法。</p>
<pre><code># 差
lambda = lambda { |a, b| a + b }
lambda.call(1, 2)
# 好
lambda = ->(a, b) { a + b }
lambda.(1, 2)
</code></pre>
<p>未使用的区块参数使用 <code>_</code> 。</p>
<pre><code># 差
result = hash.map { |k, v| v + 1 }
# 好
result = hash.map { |_, v| v + 1 }
</code></pre></div></article><article id="ruby.naming"><h2>命名</h2><div><aside>
<p>程式设计的真正难题是替事物命名及无效的缓存。 <br/>
-- Phil Karlton</p>
</aside>
<p>方法与变量使用蛇底式小写( <code>snake_case</code> )。</p>
<p>类别与模组使用驼峰式大小写( <code>CamelCase</code> )。(保留像是HTTP、RFC、XML 这种缩写为大写)</p>
<p>其他常数使用尖叫蛇底式大写( <code>SCREAMING_SNAKE_CASE</code> )。</p>
<p>判断式方法的名字(返回布尔值的方法)应以问号结尾。 (即 <code>Array#empty?</code> )</p>
<p>有潜在“危险性”的方法,若此<em>危险</em>方法有安全版本存在时,应以惊叹号结尾(即:改动 <code>self</code> 或参数、 <code>exit!</code> 等等方法)。</p>
<pre><code># 不好 - 没有对应的安全方法
class Person
def update!
end
end
# 好
class Person
def update
end
end
# 好
class Person
def update!
end
def update
end
end
</code></pre>
<p>如果可能的话,从危险方法(bang)的角度来定义对应的安全方法(non-bang)。</p>
<pre><code>class Array
def flatten_once!
res = []
each do |e|
[*e].each { |f| res << f }
end
replace(res)
end
def flatten_once
dup.flatten_once!
end
end
</code></pre>
<p>在短的区块使用 <code>reduce</code> 时,把参数命名为 <code>|a, e|</code> (累加器,元素)。</p>
<p>当定义二元操作符时,把参数命名为 <code>other</code> 。</p>
<pre><code>def +(other)
# body omitted
end
</code></pre>
<p>偏好 <code>map</code> 胜于 <code>collect</code> , <code>find</code> 胜于 <code>detect</code> , <code>select</code> 胜于 <code>find_all</code> , <code>reduce</code> 胜于 <code>inject</code> 以及 <code>size</code> 胜于 <code>length</code> 。这不是一个硬性要求;如果使用别名增加了可读性,使用它没关系。</p>
<p>这些有押韵的方法名是从 Smalltalk 继承而来,在别的语言不常见。鼓励使用 <code>select</code> 而不是 <code>find_all</code> 的理由是它跟 <code>reject</code> 搭配起来是一目了然的。</p></div></article><article id="ruby.comments"><h2>注释 (Comments)</h2><div><aside>
<p>良好的代码是最佳的文档。当你要加一个注释时,扪心自问,<br/>
"如何改善代码让它不需要注释?" 改善代码然后记录下来使它更简洁。 <br/>
-- Steve McConnell</p>
</aside>
<p>撰写自我记录的代码然后忽略之后的小节。我是认真的!</p>
<p>比一个单词长的注释要大写及使用标点符号。</p>
<p>句号后使用<a href="http://en.wikipedia.org/wiki/Sentence_spacing">一个空格</a>。</p>
<p>避免冗赘的注释</p>
<pre><code># 差
counter += 1 # 把计数器加一
</code></pre>
<p>保持现有的注释是最新的。过时的注解比没有注解还差。</p>
<p>避免替烂代码写注解。重构代码让它们看起来一目了然。 </p>
<p>(要嘛就做,要嘛不做― 不要只是试试看。)</p></div></article><article id="ruby.annotations"><h2>注解 (Annotations)</h2><div><p>注解应该直接写在相关代码那行之后。</p>
<p>注解关键字后方,伴随着一个冒号及空白,接着一个描述问题的记录。</p>
<p>如果需要用多行来描述问题,之后的行要放在 <code>#</code> 号后面并缩排两个空白。</p>
<p>def bar
# FIXME: 这在v3.2.1 版本之后会异常崩溃,或许与
# BarBazUtil 的版本更新有关
baz(:quux)
end</p>
<p>在问题是显而易见的情况下,任何的文档会是多余的,注解应该要留在可能有问题的那行。这个用法是例外而不是规则。</p>
<pre><code>def bar
sleep 100 # OPTIMIZE
end
</code></pre>
<p>使用 <code>TODO</code> 来标记之后应被加入的未实现功能或特色。</p>
<p>使用 <code>FIXME</code> 来标记一个需要修复的代码。</p>
<p>使用 <code>OPTIMIZE</code> 来标记可能影响效能的缓慢或效率低落的代码。</p>
<p>使用 <code>HACK</code> 来标记代码异味,其中包含了可疑的编码实践以及应该需要重构。</p>
<p>使用 <code>REVIEW</code> 来标记任何需要审视及确认正常动作的地方。</p>
<p>举例来说:</p>
<p><code>REVIEW: 我们确定用户现在是这么做的吗?</code></p>
<p>如果你觉得是适当的话,使用其他习惯的注解关键字,但记得把它们记录在项目的 <code>README</code> 或类似的地方。</p></div></article><article id="ruby.class"><h2>类别</h2><div><p>当设计类别阶层时,确认它们符合<a href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov 代换原则</a>。
尽可能让你的类别越<a href="http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">坚固</a>越好。
永远替类别提供一个适当的 <code>to_s</code> 方法给来表示领域模型。</p>
<pre><code>class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def to_s
"#@first_name #@last_name"
end
end
</code></pre>
<p>使用 <code>attr</code> 这类函数来定义琐碎的 accessor 或 mutators。</p>
<pre><code># 差
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def first_name
@first_name
end
def last_name
@last_name
end
end
# 好
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
</code></pre>
<p>考虑使用 <code>Struct.new</code>,它替你定义了那些琐碎的存取器(accessors),建构式(constructor)以及比较操作符(comparison operators)。</p>
<pre><code># 好
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
# 较佳
class Person < Struct.new (:first_name, :last_name)
end
</code></pre>
<p>考虑加入工厂方法来提供额外合理的方式,创造一个特定类别的实体。</p>
<pre><code>class Person
def self.create(options_hash)
# body omitted
end
end
</code></pre>
<p>偏好<a href="http://en.wikipedia.org/wiki/Duck_typing">鸭子类型</a>胜于继承。</p>
<pre><code># 差
class Animal
# 抽象方法
def speak
end
end
# 继承高层级的类别
class Duck < Animal
def speak
puts 'Quack! Quack'
end
end
# 继承高层级的类别
class Dog < Animal
def speak
puts 'Bau! Bau!'
end
end
# 好
class Duck
def speak
puts 'Quack! Quack'
end
end
class Dog
def speak
puts 'Bau! Bau!'
end
end
</code></pre>
<p>由于继承中“讨厌的”行为,避免使用类别变量( <code>@@</code> )。</p>
<pre><code>class Parent
@@class_var = 'parent'
def self.print_class_var
puts @@class_var
end
end
class Child < Parent
@@class_var = 'child'
end
Parent.print_class_var # => will print "child"
如同你所看到的,在类别阶级中的所有类别其实都共享一个类别变量。
应该通常偏好使用实体变量而不是类别变量。
</code></pre>
<p>依据方法的目的用途指定适当的可视层级(<code>private</code> , <code>protected</code> )。</p>
<p>别把所有方法都设为 <code>public</code> (方法的缺省值)。</p>
<p>我们现在是在写 <em>Ruby</em> ,不是 <em>Python</em> 。</p>
<p><code>public</code>, <code>protected</code>, <code>private</code> 和方法定义有一样的缩排。</p>
<p>在每一个上方留一个空行。</p>
<pre><code>class SomeClass
def public_method
# ...
end
private
def private_method
# ...
end
end
</code></pre>
<p>使用<code>def slef.method</code> 来定义singleton 方法。这让方法更能抵抗重构带来的变化。</p>
<pre><code>class TestClass
# 差
def TestClass.some_method
# body omitted
end
# 好
def self.some_other_method
# body omitted
end
# 也有可能及当你要定义多个
# singleton时的便利方法
class << self
def first_method
# body omitted
end
def second_method_etc
# body omitted
end
end
end
</code></pre></div></article><article id="ruby.exceptions"><h2>异常</h2><div><p>不要封锁异常。</p>
<pre><code>begin
# 这里发生了一个异常
rescue SomeError
# 拯救子句完全没有做事
end
</code></pre>
<p>不要为了控制流程而使用异常。</p>
<pre><code># 差
begin
n / d
rescue ZeroDivisionError
puts "Cannot divide by 0!"
end
# 好
if d.zero?
puts "Cannot divide by 0!"
else
n / d
end
</code></pre>
<p>避免救援 <code>Exception</code> 类别。这会把信号困住,并呼叫 <code>exit</code>,导致你需要 <code>kill -9</code> 进程。</p>
<pre><code># 不好
begin
# 呼叫 exit 及杀掉信号会被捕捉(除了 kill -9)
exit
rescue Exception
puts "you didn't really want to exit, right?"
# 异常处理
end
# 好
begin
# 从StandardError 中救援一个救援子句,
# 不是许多程式设计师所假定的异常。
rescue => e
# 异常处理
end
# 也很好
begin
# 这里发生一个异常
rescue StandardError => e
# 异常处理
end
</code></pre>
<p>把较具体的异常放在救援串连的较上层,不然它们永远不会被拯救。</p>
<pre><code># 差
begin
# 一些代码
rescue Exception => e
# 一些处理
rescue StandardError => e
# 一些处理
end
# 好
begin
# 一些代码
rescue StandardError => e
# 一些处理
rescue Exception => e
# 一些处理
end
</code></pre>
<p>在 ensure 区块中释放你的程式的外部资源。</p>
<pre><code>f = File.open("testfile")
begin
# .. 处理
rescue
# .. 错误处理
ensure
f.close unless f.nil?
end
</code></pre>
<p>偏爱使用标准函式库的异常处理胜于导入新的异常类别。</p></div></article><article id="ruby.collections"><h2>集合</h2><div><p>当你需要使用一个字串的数组时,偏好使用 <code>%w</code> 的字面数组语法。</p>
<pre><code># 差
STATES = ['draft', 'open', 'closed']
# 好
STATES = %w(draft open closed)
</code></pre>
<p>避免在数组中创造巨大的间隔。</p>
<pre><code>arr = []
arr[100] = 1 # 现在你有一个很多 nil 的数组
</code></pre>
<p>当处理独一无二的元素时,使用 <code>Set</code> 来替代 <code>Array</code> 。 <code>Set</code> 实现了不重复的无序数值集合。 <code>Set</code> 是数组直观的内部操作功能与哈希的快速存取的混合体。</p>
<p>使用符号取代字串作为哈希键。</p>
<pre><code># 差
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
# 好
hash = { one: 1, two: 2, three: 3 }
</code></pre>
<p>避免使用可变的对象作为键值。</p>
<p>优先使用新的 1.9 字面哈希语法而不是 => (hashrocket) 语法。</p>
<pre><code># 差
hash = { :one => 1, :two => 2, :three => 3 }
# 好
hash = { one: 1, two: 2, three: 3 }
</code></pre>
<p>信任这个事实, 1.9 的哈希是有序的。</p>
<p>在遍历一个集合时,不要改动它。</p></div></article><article id="ruby.string"><h2>字串</h2><div><p>偏好字串插值(interpolation),而不是字串串接(concatenation)。</p>
<pre><code># 差
email_with_name = user.name + ' <' + user.email + '>'
# 好
email_with_name = "#{user.name} <#{user.email}>"
</code></pre>
<p>当你不需要插入特殊符号如 <code>\t</code>, <code>\n</code>, <code>'</code>, 等等时,偏好单引号的字串。</p>
<pre><code># 差
name = "Bozhidar"
# 好
name = 'Bozhidar'
</code></pre>
<p>不要使用 <code>{}</code> 围绕要被插入字串的实体变量。</p>
<pre><code>class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
# 差
def to_s
"#{@first_name} #{@last_name}"
end
# 好
def to_s
"#@first_name #@last_name"
end
end
</code></pre>
<p>当你需要建构庞大的资料区块(chunk)时,避免使用 <code>String#+</code> 。使用 <code>String#<<</code> 来替代。字串串接在对的地方改变字串实体,并且永远比 <code>String#+</code> 来得快,<code>String#+</code> 创造了一堆新的字串对象。</p>
<pre><code># 好也比较快
html = ''
html << '<h1>Page title</h1>'
paragraphs.each do |paragraph|
html << "<p>#{paragraph}</p>"
end
</code></pre></div></article><article id="ruby.regexp"><h2>正則表示法</h2><div><p>如果你只需要在字串中简单的搜索文字,不要使用正則表示法:<code>string['text']</code> </p>
<p>针对简单的字串查询,你可以直接在字串索引中直接使用正則表示法。</p>
<pre><code>match = string[/regexp/] # 获得匹配正則表示法的内容
first_group = string[/text(grp)/, 1] # 或得分组的内容
string[/text (grp)/, 1] = 'replace' # string => 'text replace'
</code></pre>
<p>当你不需要替结果分组时,使用非分组的群组。</p>
<pre><code>/(first|second)/ # 差
/(?:first|second)/ # 好
</code></pre>
<p>避免使用 <code>$1-9</code>,因为它们很难追踪它们包含什么。可以使用命名群组来替代。</p>
<pre><code># 差
/(regexp)/ =~ string
...
process $1
# 好
/(?<meaningful_var>regexp)/ =~ string
...
process meaningful_var
</code></pre>
<p>字符类别只有几个你需要关心的特殊字元:<code>^</code>, <code>-</code>, <code>\</code>, <code>]</code>,所以你不用逃脱字元 <code>.</code> 或在 <code>[]</code> 的中括号。</p>
<p>小心使用 <code>^</code> 与 <code>$</code> ,它们匹配的是一行的开始与结束,不是字串的开始与结束。如果你想要匹配整个字串,使用 <code>\A</code> 与 <code>\z</code>。</p>
<p>(译注:<code>\Z</code> 实为 <code>/\n?\z/</code>,使用 <code>\z</code> 才能匹配到有含新行的字串的结束)</p>
<pre><code>string = "some injection\nusername"
string[/^username$/] # matches
string[/\Ausername\z/] # don't match
</code></pre>
<p>针对复杂的正則表示法,使用 <code>x</code> 修饰符。这让它们的可读性更高并且你可以加入有用的注释。只是要小心忽略的空白。</p>
<pre><code>regexp = %r{
start # 一些文字
\s # 空白字元