├── CONTRIBUTING.md ├── README.md └── rationales.md /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | Interested in contributing to the Airbnb Ruby style guide? Awesome! Here are 4 | some guidelines to help you out. 5 | 6 | **Setting expectations**: Although we love hearing from others who are invested 7 | in Ruby style, ultimately this style guide is used to dictate the code style 8 | *within Airbnb*. It isn't a community style guide, and as such we may reject 9 | issues or PRs that simply don't fit our internal engineering needs. 10 | 11 | ## Pull Requests 12 | 13 | PRs to the style guide should include a description of the proposal, including 14 | examples of what "good" and "bad" code would look like under the new rule, as 15 | well as a rationale for the rule in question. The description of the rule and 16 | the examples belong in `README.md`. If the rationale is anything other than "for 17 | consistency", it should go into a corresponding section in `rationales.md`. 18 | Style rules that are purely about consistency do not need rationales. 19 | 20 | Individual rules should include, after the description of the rule, a link to 21 | that rule, and, if a rationale is being added to `rationales.md`, a link to the 22 | detailed rationale. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby 代码风格指南 2 | 3 | 这是 Airbnb 的 Ruby 代码风格指南 4 | 5 | 指南灵感来自于 [Github 的指南](https://web.archive.org/web/20160410033955/https://github.com/styleguide/ruby) 和 [Bozhidar Batsov 的指南][bbatsov-ruby] 6 | 7 | Airbnb 也在维护 [JavaScript 风格指南][airbnb-javascript] 8 | 9 | ## 内容表 (Table of Contents) 10 | 1. [空格 (Whitespace)](#空格-whitespace) 11 | 1. [缩进 (Indentation)](#缩进-indentation) 12 | 1. [行内 (Inline)](#行内-inline) 13 | 1. [换行 (Newlines)](#换行-newlines) 14 | 1. [行宽 (Line Length)](#行宽-line-length) 15 | 1. [注释 (Commenting)](#注释-commenting) 16 | 1. [文件级/类级 注释 (File/class-level comments)](#文件类-级别的注释-fileclass-level-comments) 17 | 1. [函数注释 (Function comments)](#函数注释-function-comments) 18 | 1. [块级和行内注释 (Block and inline comments)](#块级和行内注释-block-and-inline-comments) 19 | 1. [标点符号, 拼写和语法 (Punctuation, spelling, and grammar)](#标点符号-拼写和语法-punctuation-spelling-and-grammar) 20 | 1. [待办注释 (TODO comments)](#待办注释-todo-comments) 21 | 1. [注释掉的代码 (Commented-out code)](#注释掉的代码-commented-out-code) 22 | 1. [方法 (Methods)](#方法-methods) 23 | 1. [方法定义 (Method definitions)](#方法定义-method-definitions) 24 | 1. [方法调用 (Method calls)](#方法调用-method-calls) 25 | 1. [条件表达式 (Conditional Expressions)](#条件表达式-conditional-expressions) 26 | 1. [关键字 (Conditional keywords)](#关键字-conditional-keywords) 27 | 1. [三元操作符 (Ternary operator)](#三元操作符-ternary-operator) 28 | 1. [语法 (Syntax)](#语法-syntax) 29 | 1. [命名 (Naming)](#命名-naming) 30 | 1. [类 (Classes)](#类-classes) 31 | 1. [异常 (Exceptions)](#异常-exceptions) 32 | 1. [集合 (Collections)](#集合-collections) 33 | 1. [字符串 (Strings)](#字符串-strings) 34 | 1. [正则表达式 (Regular Expressions)](#正则表达式-regular-expressions) 35 | 1. [百分比字面量 (Percent Literals)](#百分比字面量-percent-literals) 36 | 1. [Rails](#rails) 37 | 1. [范围 (Scopes)](#范围-scopes) 38 | 1. [保持一致 (Be Consistent)](#保持一致-be-consistent) 39 | 40 | ## 空格 (Whitespace) 41 | 42 | ### 缩进 (Indentation) 43 | 44 | * 始终用 2 个空格做缩进。[[link](#default-indentation)] 45 | 46 | * `when` 的缩进和 `case` 一致。 47 | [[link](#indent-when-as-case)] 48 | 49 | ```ruby 50 | case 51 | when song.name == 'Misty' 52 | puts 'Not again!' 53 | when song.duration > 120 54 | puts 'Too long!' 55 | when Time.now.hour > 21 56 | puts "It's too late" 57 | else 58 | song.play 59 | end 60 | 61 | kind = case year 62 | when 1850..1889 then 'Blues' 63 | when 1890..1909 then 'Ragtime' 64 | when 1910..1929 then 'New Orleans Jazz' 65 | when 1930..1939 then 'Swing' 66 | when 1940..1950 then 'Bebop' 67 | else 'Jazz' 68 | end 69 | ``` 70 | 71 | * 函数的参数要么全部在同一行,如果参数要分成多行,则每行一个参数, 相同缩进。[[link](#align-function-params)] 72 | 73 | ```ruby 74 | # 错误 75 | def self.create_translation(phrase_id, phrase_key, target_locale, 76 | value, user_id, do_xss_check, allow_verification) 77 | ... 78 | end 79 | 80 | # 正确 81 | def self.create_translation(phrase_id, 82 | phrase_key, 83 | target_locale, 84 | value, 85 | user_id, 86 | do_xss_check, 87 | allow_verification) 88 | ... 89 | end 90 | 91 | # 正确 92 | def self.create_translation( 93 | phrase_id, 94 | phrase_key, 95 | target_locale, 96 | value, 97 | user_id, 98 | do_xss_check, 99 | allow_verification 100 | ) 101 | ... 102 | end 103 | ``` 104 | 105 | * 多行的布尔表达式,下一行缩进一下。[[link](#indent-multi-line-bool)] 106 | 107 | ```ruby 108 | # 错误 109 | def is_eligible?(user) 110 | Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) && 111 | is_in_program?(user) && 112 | program_not_expired 113 | end 114 | 115 | # 正确 116 | def is_eligible?(user) 117 | Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) && 118 | is_in_program?(user) && 119 | program_not_expired 120 | end 121 | ``` 122 | 123 | ### 行内 (Inline) 124 | 125 | * 行末不要留空格。 126 | [[link](#trailing-whitespace)] 127 | 128 | * 写行内注释的时候,在代码和注释之间放 1 个空格。 129 | [[link](#space-before-comments)] 130 | 131 | ```ruby 132 | # 错误 133 | result = func(a, b)# we might want to change b to c 134 | 135 | # 正确 136 | result = func(a, b) # we might want to change b to c 137 | ``` 138 | 139 | * 操作符两边放一个空格;逗号,冒号,分号后面都放一个空格;左大括号 `{` 两边放空格,右大括号 `}` 左边放空格。 140 | [[link](#spaces-operators)] 141 | 142 | ```ruby 143 | sum = 1 + 2 144 | a, b = 1, 2 145 | 1 > 2 ? true : false; puts 'Hi' 146 | [1, 2, 3].each { |e| puts e } 147 | ``` 148 | 149 | * 逗号前面永远不要放空格 150 | [[link](#no-space-before-commas)] 151 | 152 | ```ruby 153 | result = func(a, b) 154 | ``` 155 | 156 | * block 语法里,| | 内部的两边不应该带多余的空格,参数之间应该有1个空格,| | 后面应该有一个空格 157 | [[link](#spaces-block-params")] 158 | 159 | ```ruby 160 | # 错误 161 | {}.each { | x, y |puts x } 162 | 163 | # 正确 164 | {}.each { |x, y| puts x } 165 | ``` 166 | 167 | * 感叹号和参数间不要留空格,下面是个正确的例子。[[link](#no-space-after-!)] 168 | 169 | ```ruby 170 | !something 171 | ``` 172 | 173 | * `(`, `[` 后面不要有空格 174 | `]`, `)` 前面不要有空格 175 | [[link](#no-spaces-braces)] 176 | 177 | ```ruby 178 | some(arg).other 179 | [1, 2, 3].length 180 | ``` 181 | 182 | * 183 | 字符串插值时候忽略空格。[[link](#no-spaces-string-interpolation)] 184 | 185 | ```ruby 186 | # 错误 187 | var = "This #{ foobar } is interpolated." 188 | 189 | # 正确 190 | var = "This #{foobar} is interpolated." 191 | ``` 192 | 193 | * 当表达范围时,不要写额外的空格。[[link](#no-spaces-range-literals)] 194 | 195 | ```ruby 196 | # 错误 197 | (0 ... coll).each do |item| 198 | 199 | # 正确 200 | (0...coll).each do |item| 201 | ``` 202 | 203 | ### 换行 (Newlines) 204 | 205 | * if 条件保持相同缩进,方便识别哪些是条件,哪些是内容。 206 | [[link](#multiline-if-newline)] 207 | 208 | ```ruby 209 | if @reservation_alteration.checkin == @reservation.start_date && 210 | @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights) 211 | 212 | redirect_to_alteration @reservation_alteration 213 | end 214 | ``` 215 | 216 | * 条件语句,块,case 语句,等等东西后面换一行, 例子如下。[[link](#newline-after-conditional)] 217 | 218 | ```ruby 219 | if robot.is_awesome? 220 | send_robot_present 221 | end 222 | 223 | robot.add_trait(:human_like_intelligence) 224 | ``` 225 | 226 | * 不同缩进的代码之间无需空行 (比如 class 或 module 和内容之间)。 227 | [[link](#newline-different-indent)] 228 | 229 | ```ruby 230 | # 错误 231 | class Foo 232 | 233 | def bar 234 | # body omitted 235 | end 236 | 237 | end 238 | 239 | # 正确 240 | class Foo 241 | def bar 242 | # body omitted 243 | end 244 | end 245 | ``` 246 | 247 | * 方法之间 1 个空行就好。[[link](#newline-between-methods)] 248 | 249 | ```ruby 250 | def a 251 | end 252 | 253 | def b 254 | end 255 | ``` 256 | 257 | * 1 个空行隔开类似的逻辑。 258 | [[link](#method-def-empty-lines)] 259 | 260 | ```ruby 261 | def transformorize_car 262 | car = manufacture(options) 263 | t = transformer(robot, disguise) 264 | 265 | car.after_market_mod! 266 | t.transform(car) 267 | car.assign_cool_name! 268 | 269 | fleet.add(car) 270 | car 271 | end 272 | ``` 273 | 274 | * 文件末尾只放一个空行。 275 | [[link](#trailing-newline)] 276 | 277 | ## 行宽 (Line Length) 278 | 279 | * 把每一行控制在可读宽度内, 280 | 除非有特别理由, 每一行应小于 100 个字符 281 | ([rationale](./rationales.md#line-length)) 282 | [[link](#line-length)] 283 | 284 | ## 注释 (Commenting) 285 | 286 | > 虽然写注释很痛苦, 但是对于保持代码可读非常重要 287 | > 下面的规则描述了你应该怎么写注释, 以及写在哪里。 288 | > 但是记住:注释虽然非常重要, 但是最好的注释是代码本身。 289 | > 直观的变量名本身就是注释, 比起起一个奇怪的名字,然后用注释解释要好得多 290 | 291 | > 当写注释时,为你的观众去写:下一个贡献者需要理解你的代码。 292 | > 请慷慨一些 - 下一个可能就是你自己! 293 | 294 | —[Google C++ 风格指南][google-c++] 295 | 296 | 这一部分的指南从 Google 297 | [C++][google-c++-comments] 和 [Python][google-python-comments] 风格指南那边借鉴了很多。 298 | 299 | ### 文件/类 级别的注释 (File/class-level comments) 300 | 301 | 每个类的定义都应该带些注释, 说明它是干什么的, 以及怎么用它。 302 | 303 | 如果文件里没有任何 class, 或者多于一个 class, 顶部应该有注释说明里面是什么。 304 | 305 | ```ruby 306 | # Automatic conversion of one locale to another where it is possible, like 307 | # American to British English. 308 | module Translation 309 | # Class for converting between text between similar locales. 310 | # Right now only conversion between American English -> British, Canadian, 311 | # Australian, New Zealand variations is provided. 312 | class PrimAndProper 313 | def initialize 314 | @converters = { :en => { :"en-AU" => AmericanToAustralian.new, 315 | :"en-CA" => AmericanToCanadian.new, 316 | :"en-GB" => AmericanToBritish.new, 317 | :"en-NZ" => AmericanToKiwi.new, 318 | } } 319 | end 320 | 321 | ... 322 | 323 | # Applies transforms to American English that are common to 324 | # variants of all other English colonies. 325 | class AmericanToColonial 326 | ... 327 | end 328 | 329 | # Converts American to British English. 330 | # In addition to general Colonial English variations, changes "apartment" 331 | # to "flat". 332 | class AmericanToBritish < AmericanToColonial 333 | ... 334 | end 335 | ``` 336 | 337 | 所有文件,包括数据和配置文件,都应该有文件级别(file-level)的注释。 338 | 339 | ```ruby 340 | # List of American-to-British spelling variants. 341 | # 342 | # This list is made with 343 | # lib/tasks/list_american_to_british_spelling_variants.rake. 344 | # 345 | # It contains words with general spelling variation patterns: 346 | # [trave]led/lled, [real]ize/ise, [flav]or/our, [cent]er/re, plus 347 | # and these extras: 348 | # learned/learnt, practices/practises, airplane/aeroplane, ... 349 | 350 | sectarianizes: sectarianises 351 | neutralization: neutralisation 352 | ... 353 | ``` 354 | 355 | ### 函数注释 (Function comments) 356 | 357 | 函数声明的前面都应该有注释,描述函数是做什么的,以及怎么用。 358 | 这些注释应该是 描述性的(descriptive) 比如 ("Opens the file") 359 | 而不是 命令式的(imperative) 比如 ("Open the file"); 360 | 注释描述这个函数,而不是告诉这个函数应该做什么。 361 | 总的来说,函数前面的注释并不负责解释函数是怎么做到它提供的功能的。 362 | 解释功能怎么实现的注释,应该零散的分布在函数内部的注释里。 363 | 364 | 每个函数都应该提到输入和输出是什么,除非碰到以下情况: 365 | 366 | * 不是外部可见的函数 (not externally visible) 367 | * 非常短 (very short) 368 | * 很明显 (obvious) 369 | 370 | 你喜欢什么格式都行。在 Ruby 里两种常见的格式是 [TomDoc](http://tomdoc.org/) 和 [YARD](http://rubydoc.info/docs/yard/file/docs/GettingStarted.md)。 371 | 你也可以直接简洁明了的写出来,比如这样: 372 | 373 | ```ruby 374 | # Returns the fallback locales for the_locale. 375 | # If opts[:exclude_default] is set, the default locale, which is otherwise 376 | # always the last one in the returned list, will be excluded. 377 | # 378 | # For example: 379 | # fallbacks_for(:"pt-BR") 380 | # => [:"pt-BR", :pt, :en] 381 | # fallbacks_for(:"pt-BR", :exclude_default => true) 382 | # => [:"pt-BR", :pt] 383 | def fallbacks_for(the_locale, opts = {}) 384 | ... 385 | end 386 | ``` 387 | 388 | ### 块级和行内注释 (Block and inline comments) 389 | 390 | 这个部分有点小麻烦. 如果你下次 code review 需要解释这些代码的话, 你现在就应该写注释。 391 | 复杂的操作应该把注释写在前面。 392 | 单行的不太容易理解的代码, 注释应该写在代码后面。 393 | 394 | ```ruby 395 | def fallbacks_for(the_locale, opts = {}) 396 | # dup() to produce an array that we can mutate. 397 | ret = @fallbacks[the_locale].dup 398 | 399 | # We make two assumptions here: 400 | # 1) There is only one default locale (that is, it has no less-specific 401 | # children). 402 | # 2) The default locale is just a language. (Like :en, and not :"en-US".) 403 | if opts[:exclude_default] && 404 | ret.last == default_locale && 405 | ret.last != language_from_locale(the_locale) 406 | ret.pop 407 | end 408 | 409 | ret 410 | end 411 | ``` 412 | 在另一方面,永远不要在注释里描述代码。你要假设读代码的人对这门语言的了解比你知道的多。 413 | 414 | 相关的一个事儿:不要用块级注释,他们前面没有空格,不方便一眼认出来是注释。 415 | [[link](#no-block-comments)] 416 | 417 | ```ruby 418 | # 错误 419 | =begin 420 | comment line 421 | another comment line 422 | =end 423 | 424 | # 正确 425 | # comment line 426 | # another comment line 427 | ``` 428 | 429 | ### 标点符号, 拼写和语法 (Punctuation, spelling, and grammar) 430 | 431 | 要注意注释里的标点符号, 拼写和语法; 432 | 注释写得好读起来也容易。 433 | 434 | 注释应该和叙述文 (narrative text) 一样易读 (readable), 435 | 有着正确的大小写和标点符号。 在很多情况下。 完整的句子比句子碎片容易读得多。 436 | 短的注释, 比如跟在代码后面的, 可以不那么正式, 但风格应该一致。 437 | 438 | 虽然在提交代码时, 别人指出你在应该用分号的地方用了逗号, 这种小事蛮折磨人的. 439 | 但重要的是源代码应该保持高度的清晰和可读. 正确的标点符号, 拼写, 和语法非常重要. 440 | 441 | 442 | ### 待办注释 (TODO comments) 443 | 444 | 当代码是临时解决方案,够用但是并不完美时,用 TODO 注释。 445 | 446 | TODO 应该全大写, 然后是写这个注释的人名, 用圆括号括起来, 冒号可写可不写。 447 | 然后后面是解释需要做什么,统一 TODO 注释样式的目的是方便搜索。 448 | 括号里的人名不代表这个人负责解决这个问题, 只是说这个人知道这里要解决什么问题. 449 | 每次你写 TODO 注释的时候加上你的名字。 450 | 451 | ```ruby 452 | # 错误 453 | # TODO(RS): Use proper namespacing for this constant. 454 | 455 | # 错误 456 | # TODO(drumm3rz4lyfe): Use proper namespacing for this constant. 457 | 458 | # 正确 459 | # TODO(Ringo Starr): Use proper namespacing for this constant. 460 | ``` 461 | 462 | ### 注释掉的代码 (Commented-out code) 463 | 464 | * 注释掉的代码就不要留在代码库里了。 465 | [[link](#commented-code)] 466 | 467 | ## 方法 (Methods) 468 | 469 | ### 方法定义 (Method definitions) 470 | 471 | * 函数要接参数的时候就用括号, 不用参数时就不用写括号了。 下面是正确的例子.[[link](#method-def-parens)] 472 | 473 | ```ruby 474 | def some_method 475 | # body omitted 476 | end 477 | 478 | def some_method_with_parameters(arg1, arg2) 479 | # body omitted 480 | end 481 | ``` 482 | 483 | * 不要用默认参数,用一个选项 hash 来做这个事。[[link](#no-default-args)] 484 | 485 | ```ruby 486 | # 错误 487 | def obliterate(things, gently = true, except = [], at = Time.now) 488 | ... 489 | end 490 | 491 | # 正确 492 | def obliterate(things, options = {}) 493 | default_options = { 494 | :gently => true, # obliterate with soft-delete 495 | :except => [], # skip obliterating these things 496 | :at => Time.now, # don't obliterate them until later 497 | } 498 | options.reverse_merge!(default_options) 499 | 500 | ... 501 | end 502 | ``` 503 | 504 | * 避免定义单行函数,虽然有些人喜欢这样写,但是这样容易引起一些奇怪的问题 505 | [[link](#no-single-line-methods)] 506 | 507 | ```ruby 508 | # 错误 509 | def too_much; something; something_else; end 510 | 511 | # 正确 512 | def some_method 513 | # body 514 | end 515 | ``` 516 | 517 | ### 方法调用 (Method calls) 518 | 519 | 应该用 **圆括号** 的情况: 520 | 521 | * 如果方法会返回值。 522 | [[link](#returns-val-parens)] 523 | 524 | ```ruby 525 | # 错误 526 | @current_user = User.find_by_id 1964192 527 | 528 | # 正确 529 | @current_user = User.find_by_id(1964192) 530 | ``` 531 | 532 | * 如果第一个参数需要圆括号。[[link](#first-arg-parens)] 533 | 534 | ```ruby 535 | # 错误 536 | put! (x + y) % len, value 537 | 538 | # 正确 539 | put!((x + y) % len, value) 540 | ``` 541 | 542 | * 方法名和左括号之间永远不要放空格。[[link](#space-method-call)] 543 | 544 | ```ruby 545 | # 错误 546 | f (3 + 2) + 1 547 | 548 | # 正确 549 | f(3 + 2) + 1 550 | ``` 551 | 552 | * 对于不用接收参数的方法, **忽略圆括号** 。[[link](#no-args-parens)] 553 | 554 | ```ruby 555 | # 错误 556 | nil?() 557 | 558 | # 正确 559 | nil? 560 | ``` 561 | 562 | * 如果方法不返回值 (或者我们不关心返回值),那么带不带括号都行。 563 | (如果参数会导致代码多于一行, 建议加个括号比较有可读性) 564 | [[link](#no-return-parens)] 565 | 566 | ```ruby 567 | # okay 568 | render(:partial => 'foo') 569 | 570 | # okay 571 | render :partial => 'foo' 572 | ``` 573 | 574 | 不论什么情况: 575 | 576 | * 如果一个方法的最后一个参数接收 hash, 那么不需要 `{` `}` 。 577 | [[link](#options-no-braces)] 578 | 579 | ```ruby 580 | # 错误 581 | get '/v1/reservations', { :id => 54875 } 582 | 583 | # 正确 584 | get '/v1/reservations', :id => 54875 585 | ``` 586 | 587 | ## 条件表达式 (Conditional Expressions) 588 | 589 | ### 关键字 (Conditional keywords) 590 | 591 | * 永远不要把 `then` 和多行的 `if/unless` 搭配使用。 592 | [[link](#multiline-if-then)] 593 | 594 | ```ruby 595 | # 错误 596 | if some_condition then 597 | ... 598 | end 599 | 600 | # 正确 601 | if some_condition 602 | ... 603 | end 604 | ``` 605 | 606 | * `do` 不要和多行的 `while` 或 `until`搭配使用。[[link](#multiline-while-until)] 607 | 608 | ```ruby 609 | # 错误 610 | while x > 5 do 611 | ... 612 | end 613 | 614 | until x > 5 do 615 | ... 616 | end 617 | 618 | # 正确 619 | while x > 5 620 | ... 621 | end 622 | 623 | until x > 5 624 | ... 625 | end 626 | ``` 627 | 628 | * `and`, `or`, 和`not` 关键词禁用。 因为不值得。 总是用 `&&`, `||`, 和 `!` 来代替。 629 | [[link](#no-and-or)] 630 | 631 | * 适合用 `if/unless` 的情况: 内容简单, 条件简单, 整个东西能塞进一行。 632 | 不然的话, 不要用 `if/unless`。 633 | [[link](#only-simple-if-unless)] 634 | 635 | ```ruby 636 | # 错误 - 一行塞不下 637 | add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty? 638 | 639 | # 还行 640 | if request_opts[:trebuchet_experiments_on_page] && 641 | !request_opts[:trebuchet_experiments_on_page].empty? 642 | 643 | add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) 644 | end 645 | 646 | # 错误 - 这个很复杂,需要写成多行,而且需要注释 647 | parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i) 648 | 649 | # 还行 650 | return if reconciled? 651 | ``` 652 | 653 | * 不要把 `unless` 和 `else` 搭配使用。[[link](#no-unless-with-else)] 654 | 655 | ```ruby 656 | # 错误 657 | unless success? 658 | puts 'failure' 659 | else 660 | puts 'success' 661 | end 662 | 663 | # 正确 664 | if success? 665 | puts 'success' 666 | else 667 | puts 'failure' 668 | end 669 | ``` 670 | 671 | * 避免用多个条件的 `unless` 672 | 。[[link](#unless-with-multiple-conditions)] 673 | 674 | ```ruby 675 | # 错误 676 | unless foo? && bar? 677 | ... 678 | end 679 | 680 | # 还行 681 | if !(foo? && bar?) 682 | ... 683 | end 684 | ``` 685 | 686 | * 条件语句 `if/unless/while` 不需要圆括号。 687 | [[link](#parens-around-conditions)] 688 | 689 | ```ruby 690 | # 错误 691 | if (x > 10) 692 | ... 693 | end 694 | 695 | # 正确 696 | if x > 10 697 | ... 698 | end 699 | 700 | ``` 701 | 702 | ### 三元操作符 (Ternary operator) 703 | 704 | * 避免使用三元操作符 (`?:`),如果不用三元操作符会变得很啰嗦才用。 705 | 对于单行的条件, 用三元操作符(`?:`) 而不是 `if/then/else/end`.[[link](#avoid-complex-ternary)] 706 | 707 | ```ruby 708 | # 错误 709 | result = if some_condition then something else something_else end 710 | 711 | # 正确 712 | result = some_condition ? something : something_else 713 | ``` 714 | 715 | * 不要嵌套使用三元操作符,换成 716 | `if/else`.[[link](#no-nested-ternaries)] 717 | 718 | ```ruby 719 | # 错误 720 | some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else 721 | 722 | # 正确 723 | if some_condition 724 | nested_condition ? nested_something : nested_something_else 725 | else 726 | something_else 727 | end 728 | ``` 729 | 730 | * 避免多条件三元操作符。最好判断一个条件。 731 | [[link](#single-condition-ternary)] 732 | 733 | * 避免拆成多行的 `?:` (三元操作符), 用 `if/then/else/end` 就好了。 734 | [[link](#no-multiline-ternaries)] 735 | 736 | ```ruby 737 | # 错误 738 | some_really_long_condition_that_might_make_you_want_to_split_lines ? 739 | something : something_else 740 | 741 | # 正确 742 | if some_really_long_condition_that_might_make_you_want_to_split_lines 743 | something 744 | else 745 | something_else 746 | end 747 | ``` 748 | 749 | ## 语法 (Syntax) 750 | 751 | * 永远不要用 `for`,除非有非常特殊的理由。 752 | 绝大部分情况都应该用 `each`。 `for` 是用 `each` 实现的(所以你间接加了一层), 753 | 但区别是 - `for` 不会有新 scope (不像 `each`) 里面定义的变量外面可见。[[link](#no-for)] 754 | 755 | ```ruby 756 | arr = [1, 2, 3] 757 | 758 | # 错误 759 | for elem in arr do 760 | puts elem 761 | end 762 | 763 | # 正确 764 | arr.each { |elem| puts elem } 765 | ``` 766 | 767 | * 768 | 单行的情况下, 尽量用 `{...}` 而不是 `do...end`。 769 | 多行的情况下避免用 `{...}`. 770 | 对于 "control flow" 和 "方法定义"(举例: 在 Rakefiles 和某些 DSLs 里) 总是用 `do...end`。 771 | 方法连用(chaining)时 避免使用 `do...end` 。[[link](#single-line-blocks)] 772 | 773 | ```ruby 774 | names = ["Bozhidar", "Steve", "Sarah"] 775 | 776 | # 正确 777 | names.each { |name| puts name } 778 | 779 | # 错误 780 | names.each do |name| puts name end 781 | 782 | # 正确 783 | names.each do |name| 784 | puts name 785 | puts 'yay!' 786 | end 787 | 788 | # 错误 789 | names.each { |name| 790 | puts name 791 | puts 'yay!' 792 | } 793 | 794 | # 正确 795 | names.select { |name| name.start_with?("S") }.map { |name| name.upcase } 796 | 797 | # 错误 798 | names.select do |name| 799 | name.start_with?("S") 800 | end.map { |name| name.upcase } 801 | ``` 802 | 803 | 有些人会说多行连用(chaining) 用 804 | `{...}` 符号 其实也没那么难看, 但他们应该问问自己这代码真的可读吗, 而且 block 里的内容是否可以抽出来弄好看些. 805 | 806 | * 尽可能用短的自赋值操作符 (self assignment operators)。[[link](#self-assignment)] 807 | 808 | ```ruby 809 | # 错误 810 | x = x + y 811 | x = x * y 812 | x = x**y 813 | x = x / y 814 | x = x || y 815 | x = x && y 816 | 817 | # 正确 818 | x += y 819 | x *= y 820 | x **= y 821 | x /= y 822 | x ||= y 823 | x &&= y 824 | ``` 825 | 826 | * 避免用分号。 除非是单行 class 定义的情况下。 而且当使用分号时, 分号前面不应该有空格。[[link](#semicolons)] 827 | 828 | ```ruby 829 | # 错误 830 | puts 'foobar'; # 多余的分号 831 | puts 'foo'; puts 'bar' # 两个表达式放到一行 832 | 833 | # 正确 834 | puts 'foobar' 835 | 836 | puts 'foo' 837 | puts 'bar' 838 | 839 | puts 'foo', 'bar' # this applies to puts in particular 840 | ``` 841 | 842 | * :: 的使用场景是引用常量,(比如 843 | classes 和 modules 里的常量) 以及调用构造函数 (比如 Array() 或者 Nokogiri::HTML())。 844 | 普通方法调用就不要使用 :: 了。[[link](#colon-use)] 845 | 846 | ```ruby 847 | # 错误 848 | SomeClass::some_method 849 | some_object::some_method 850 | 851 | # 正确 852 | SomeClass.some_method 853 | some_object.some_method 854 | SomeModule::SomeClass::SOME_CONST 855 | SomeModule::SomeClass() 856 | ``` 857 | 858 | * 尽量避免用 `return`。 859 | [[link](#redundant-return)] 860 | 861 | ```ruby 862 | # 错误 863 | def some_method(some_arr) 864 | return some_arr.size 865 | end 866 | 867 | # 正确 868 | def some_method(some_arr) 869 | some_arr.size 870 | end 871 | ``` 872 | 873 | * 条件语句里不要用返回值[[link](#assignment-in-conditionals)] 874 | 875 | ```ruby 876 | # 错误 - shows intended use of assignment 877 | if (v = array.grep(/foo/)) 878 | ... 879 | end 880 | 881 | # 错误 882 | if v = array.grep(/foo/) 883 | ... 884 | end 885 | 886 | # 正确 887 | v = array.grep(/foo/) 888 | if v 889 | ... 890 | end 891 | 892 | ``` 893 | 894 | * 请随意用 `||=` 来初始化变量。 895 | [[link](#double-pipe-for-uninit)] 896 | 897 | ```ruby 898 | # 把 name 赋值为 Bozhidar, 仅当 name 是 nil 或者 false 时 899 | name ||= 'Bozhidar' 900 | ``` 901 | 902 | * 不要用 `||=` 来初始化布尔变量。 903 | (想象下如果值刚好是 `false` 会咋样.)[[link](#no-double-pipes-for-bools)] 904 | 905 | ```ruby 906 | # 错误 - would set enabled to true even if it was false 907 | enabled ||= true 908 | 909 | # 正确 910 | enabled = true if enabled.nil? 911 | ``` 912 | 913 | * 当调用 lambdas 时,明确使用 `.call`。 914 | [[link](#lambda-calls)] 915 | 916 | ```ruby 917 | # 错误 918 | lambda.(x, y) 919 | 920 | # 正确 921 | lambda.call(x, y) 922 | ``` 923 | 924 | * 避免使用 Perl 风格的特殊变量名 925 | ( 比如 `$0-9`, `$`, 等等. ). 因为看起来蛮神秘的. 建议只在单行里使用。建议用长名字, 比如 `$PROGRAM_NAME`.[[link](#no-cryptic-perl)] 926 | 927 | * 当一个方法块只需要 1 个参数,而且方法体也只是读一个属性, 或者无参数的调用一样方法,这种情况下用 `&:` . 928 | [[link](#single-action-blocks)] 929 | 930 | ```ruby 931 | # 错误 932 | bluths.map { |bluth| bluth.occupation } 933 | bluths.select { |bluth| bluth.blue_self? } 934 | 935 | # 正确 936 | bluths.map(&:occupation) 937 | bluths.select(&:blue_self?) 938 | ``` 939 | 940 | * 当调用当前实例的某个方法时, 尽量用 `some_method` 而不是 `self.some_method`。[[link](#redundant-self)] 941 | 942 | ```ruby 943 | # 错误 944 | def end_date 945 | self.start_date + self.nights 946 | end 947 | 948 | # 正确 949 | def end_date 950 | start_date + nights 951 | end 952 | ``` 953 | 954 | 在下面 3 种常见情况里, 需要用, 而且应该用`self.`: 955 | 956 | 1. 当定义一个类方法时: `def self.some_method`. 957 | 2. 当调用一个赋值方法 (assignment method) 时,*左手边* 应该用 self, 比如当 self 是 ActiveRecord 模型然后你需要赋值一个属性: `self.guest = user`. 958 | 3. 指向 (Referencing) 当前实例的类: `self.class`. 959 | 960 | ## 命名 (Naming) 961 | 962 | * 用 蛇命名法 (`snake_case`) 来命名 methods 和 variables。 963 | [[link](#snake-case)] 964 | 965 | * 用 驼峰命名法(`CamelCase`) 命名 class 和 module。 (缩写词如 HTTP, RFC, XML 全部大写) 966 | [[link](#camel-case)] 967 | 968 | * 用尖叫蛇命名法 (`SCREAMING_SNAKE_CASE`) 来命名常量。[[link](#screaming-snake-case)] 969 | 970 | * 断定方法的名字 (predicate methods) (意思是那些返回布尔值的方法) 应该以问号结尾。 971 | (比如 `Array#empty?`)。[[link](#predicate-method-names)] 972 | 973 | * 有一定 "潜在危险" 的方法 974 | (意思就是那些. 会修改 `self` 的方法, 或者原地修改参数的方法, 或者带有 `exit!` 的方法, 等等) 应该以感叹号结尾. 这种危险方法应该仅当同名的不危险方法存在之后, 才存在. ([More on this][ruby-naming-bang].) 975 | [[link](#bang-methods)] 976 | 977 | * 把不用的变量名命名为 `_`. 978 | [[link](#throwaway-variables)] 979 | 980 | ```ruby 981 | payment, _ = Payment.complete_paypal_payment!(params[:token], 982 | native_currency, 983 | created_at) 984 | ``` 985 | 986 | ## 类 (Classes) 987 | 988 | * 避免使用类变量(`@@`), 989 | 因为在继承的时候它们会有 "淘气" 的行为。 990 | [[link](#avoid-class-variables)] 991 | 992 | ```ruby 993 | class Parent 994 | @@class_var = 'parent' 995 | 996 | def self.print_class_var 997 | puts @@class_var 998 | end 999 | end 1000 | 1001 | class Child < Parent 1002 | @@class_var = 'child' 1003 | end 1004 | 1005 | Parent.print_class_var # => 会输出"child" 1006 | ``` 1007 | 1008 | 你可以看到在这个类的继承层级了,所有的类都共享一个类变量。 尽量使用实例变量而不是类变量。 1009 | 1010 | * 用 `def self.method` 来定义单例方法(singleton 1011 | methods). 这样在需要改类名的时候更方便. 1012 | [[link](#singleton-methods)] 1013 | 1014 | ```ruby 1015 | class TestClass 1016 | # 错误 1017 | def TestClass.some_method 1018 | ... 1019 | end 1020 | 1021 | # 正确 1022 | def self.some_other_method 1023 | ... 1024 | end 1025 | ``` 1026 | * 除非必要,避免写 `class << self`, 1027 | 必要的情况比如 single accessors 和 aliased attributes。 1028 | [[link](#no-class-self)] 1029 | 1030 | ```ruby 1031 | class TestClass 1032 | # 错误 1033 | class << self 1034 | def first_method 1035 | ... 1036 | end 1037 | 1038 | def second_method_etc 1039 | ... 1040 | end 1041 | end 1042 | 1043 | # 正确 1044 | class << self 1045 | attr_accessor :per_page 1046 | alias_method :nwo, :find_by_name_with_owner 1047 | end 1048 | 1049 | def self.first_method 1050 | ... 1051 | end 1052 | 1053 | def self.second_method_etc 1054 | ... 1055 | end 1056 | end 1057 | ``` 1058 | 1059 | * `public`, `protected`, `private` 它们和方法定义保持相同缩进。 并且上下各留一个空行。[[link](#access-modifiers)] 1060 | 1061 | ```ruby 1062 | class SomeClass 1063 | def public_method 1064 | # ... 1065 | end 1066 | 1067 | private 1068 | 1069 | def private_method 1070 | # ... 1071 | end 1072 | end 1073 | ``` 1074 | 1075 | ## 异常 (Exceptions) 1076 | 1077 | * 不要把异常用于控制流里 (flow of control) 1078 | [[link](#exception-flow-control)] 1079 | 1080 | ```ruby 1081 | # 错误 1082 | begin 1083 | n / d 1084 | rescue ZeroDivisionError 1085 | puts "Cannot divide by 0!" 1086 | end 1087 | 1088 | # 正确 1089 | if d.zero? 1090 | puts "Cannot divide by 0!" 1091 | else 1092 | n / d 1093 | end 1094 | ``` 1095 | 1096 | * 避免捕捉 `Exception` 这个大类的异常 1097 | [[link](#dont-rescue-exception)] 1098 | 1099 | ```ruby 1100 | # 错误 1101 | begin 1102 | # an exception occurs here 1103 | rescue Exception 1104 | # exception handling 1105 | end 1106 | 1107 | # 正确 1108 | begin 1109 | # an exception occurs here 1110 | rescue StandardError 1111 | # exception handling 1112 | end 1113 | 1114 | # 可以接受 1115 | begin 1116 | # an exception occurs here 1117 | rescue 1118 | # exception handling 1119 | end 1120 | ``` 1121 | 1122 | * 传 2 个参数调 raise 异常时不要明确指明`RuntimeError`。尽量用 error 子类这样比较清晰和明确。[[link](#redundant-exception)] 1123 | 1124 | ```ruby 1125 | # 错误 1126 | raise RuntimeError, 'message' 1127 | 1128 | # 正确一点 - RuntimeError 是默认的 1129 | raise 'message' 1130 | 1131 | # 最好 1132 | class MyExplicitError < RuntimeError; end 1133 | raise MyExplicitError 1134 | ``` 1135 | 1136 | * 尽量将异常的类和讯息两个分开作为 `raise` 的参数,而不是提供异常的实例。[[link](#exception-class-messages)] 1137 | 1138 | ```ruby 1139 | # 错误 1140 | raise SomeException.new('message') 1141 | # 注意,提供异常的实例没办法做到 `raise SomeException.new('message'), backtrace`. 1142 | 1143 | # 正确 1144 | raise SomeException, 'message' 1145 | # 可以达到 `raise SomeException, 'message', backtrace`. 1146 | ``` 1147 | 1148 | * 避免使用 rescue 的变异形式。 1149 | [[link](#rescue-as-modifier)] 1150 | 1151 | ```ruby 1152 | # 错误 1153 | read_file rescue handle_error($!) 1154 | 1155 | # 正确 1156 | begin 1157 | read_file 1158 | rescue Errno:ENOENT => ex 1159 | handle_error(ex) 1160 | end 1161 | ``` 1162 | 1163 | ## 集合 (Collections) 1164 | 1165 | * 尽量用 `map` 而不是 `collect`。[[link](#map-over-collect)] 1166 | 1167 | * 尽量用 `detect` 而不是 `find`。 `find` 1168 | 容易和 ActiveRecord 的 `find` 搞混 - `detect` 则是明确的说明了 1169 | 是要操作 Ruby 的集合, 而不是 ActiveRecord 对象。 1170 | [[link](#detect-over-find)] 1171 | 1172 | * 尽量用 `reduce` 而不是 `inject`。 1173 | [[link](#reduce-over-inject)] 1174 | 1175 | * 尽量用 `size`, 而不是 `length` 或者 `count`, 出于性能理由。[[link](#size-over-count)] 1176 | 1177 | * 尽量用数组和 hash 字面量来创建, 1178 | 而不是用 new。 除非你需要传参数。 1179 | [[link](#empty-collection-literals)] 1180 | 1181 | ```ruby 1182 | # 错误 1183 | arr = Array.new 1184 | hash = Hash.new 1185 | 1186 | # 正确 1187 | arr = [] 1188 | hash = {} 1189 | 1190 | # 正确, 因为构造函数需要参数 1191 | x = Hash.new { |h, k| h[k] = {} } 1192 | ``` 1193 | 1194 | * 为了可读性倾向于用 `Array#join` 而不是 `Array#*`。 1195 | [[link](#array-join)] 1196 | 1197 | ```ruby 1198 | # 错误 1199 | %w(one two three) * ', ' 1200 | # => 'one, two, three' 1201 | 1202 | # 正确 1203 | %w(one two three).join(', ') 1204 | # => 'one, two, three' 1205 | ``` 1206 | 1207 | * 用 符号(symbols) 而不是 字符串(strings) 作为 hash keys。 1208 | [[link](#symbol-keys)] 1209 | 1210 | ```ruby 1211 | # 错误 1212 | hash = { 'one' => 1, 'two' => 2, 'three' => 3 } 1213 | 1214 | # 正确 1215 | hash = { :one => 1, :two => 2, :three => 3 } 1216 | ``` 1217 | 1218 | * 如果可以的话, 用普通的 symbol 而不是字符串 symbol。[[link](#symbol-literals)] 1219 | 1220 | ```ruby 1221 | # 错误 1222 | :"symbol" 1223 | 1224 | # 正确 1225 | :symbol 1226 | ``` 1227 | 1228 | * 用 `Hash#key?` 而不是 `Hash#has_key?` 1229 | 用 `Hash#value?` 而不是 `Hash#has_value?`. 1230 | 根据 Matz 的说法, 长一点的那种写法在考虑要废弃掉。 1231 | [[link](#deprecated-hash-methods") 1232 | 1233 | ```ruby 1234 | # 错误 1235 | hash.has_key?(:test) 1236 | hash.has_value?(value) 1237 | 1238 | # 正确 1239 | hash.key?(:test) 1240 | hash.value?(value) 1241 | ``` 1242 | 1243 | * 用多行 hashes 使得代码更可读, 逗号放末尾。 1244 | [[link](#multiline-hashes)] 1245 | 1246 | ```ruby 1247 | hash = { 1248 | :protocol => 'https', 1249 | :only_path => false, 1250 | :controller => :users, 1251 | :action => :set_password, 1252 | :redirect => @redirect_url, 1253 | :secret => @secret, 1254 | } 1255 | ``` 1256 | 1257 | * 如果是多行数组,用逗号结尾。[[link](#array-trailing-comma)] 1258 | 1259 | ```ruby 1260 | # 正确 1261 | array = [1, 2, 3] 1262 | 1263 | # 正确 1264 | array = [ 1265 | "car", 1266 | "bear", 1267 | "plane", 1268 | "zoo", 1269 | ] 1270 | ``` 1271 | 1272 | ## 字符串 (Strings) 1273 | 1274 | * 尽量使用字符串插值,而不是字符串拼接[[link](#string-interpolation)] 1275 | 1276 | ```ruby 1277 | # 错误 1278 | email_with_name = user.name + ' <' + user.email + '>' 1279 | 1280 | # 正确 1281 | email_with_name = "#{user.name} <#{user.email}>" 1282 | ``` 1283 | 1284 | 另外,记住 Ruby 1.9 风格的字符串插值。比如说你要构造出缓存的 key 名: 1285 | 1286 | ```ruby 1287 | CACHE_KEY = '_store' 1288 | 1289 | cache.write(@user.id + CACHE_KEY) 1290 | ``` 1291 | 1292 | 那么建议用字符串插值而不是字符串拼接: 1293 | 1294 | ```ruby 1295 | CACHE_KEY = '%d_store' 1296 | 1297 | cache.write(CACHE_KEY % @user.id) 1298 | ``` 1299 | 1300 | * 在需要构建大数据块时,避免使用 `String#+`。 1301 | 而是用 `String#<<`. 它可以原位拼接字符串而且它总是快于 `String#+`, 1302 | 这种用加号的语法会创建一堆新的字符串对象.[[link](#string-concatenation)] 1303 | 1304 | ```ruby 1305 | # 正确而且快 1306 | html = '' 1307 | html << '

Page title

' 1308 | 1309 | paragraphs.each do |paragraph| 1310 | html << "

#{paragraph}

" 1311 | end 1312 | ``` 1313 | 1314 | * 对于多行字符串, 在行末使用 `\` ,而不是 `+` 1315 | 或者 `<<` 1316 | [[link](#multi-line-strings)] 1317 | 1318 | ```ruby 1319 | # 错误 1320 | "Some string is really long and " + 1321 | "spans multiple lines." 1322 | 1323 | "Some string is really long and " << 1324 | "spans multiple lines." 1325 | 1326 | # 正确 1327 | "Some string is really long and " \ 1328 | "spans multiple lines." 1329 | ``` 1330 | 1331 | ## 正则表达式 (Regular Expressions) 1332 | 1333 | * 避免使用 `$1-9` 因为可能难以辨认出是哪一个,取个名。 1334 | [[link](#regex-named-groups)] 1335 | 1336 | ```ruby 1337 | # 错误 1338 | /(regexp)/ =~ string 1339 | ... 1340 | process $1 1341 | 1342 | # 正确 1343 | /(?regexp)/ =~ string 1344 | ... 1345 | process meaningful_var 1346 | ``` 1347 | 1348 | * 小心使用 `^` 和 `$` 因为它们匹配的是 1349 | 行头/行末,而不是某个字符串的结尾. 如果你想匹配整个字符串, 用: `\A` 和 `\z`.[[link](#caret-and-dollar-regexp)] 1350 | 1351 | ```ruby 1352 | string = "some injection\nusername" 1353 | string[/^username$/] # matches 1354 | string[/\Ausername\z/] # don't match 1355 | ``` 1356 | 1357 | * 使用 `x` 修饰符在复杂的正则表达式上。 1358 | 这使得它更可读, 并且你可以加有用的注释。只是要注意空格会被忽略。[[link](#comment-regexes)] 1359 | 1360 | ```ruby 1361 | regexp = %r{ 1362 | start # some text 1363 | \s # white space char 1364 | (group) # first group 1365 | (?:alt1|alt2) # some alternation 1366 | end 1367 | }x 1368 | ``` 1369 | 1370 | ## 百分比字面量 (Percent Literals) 1371 | 1372 | * 统一用圆括号,不要用其他括号, 1373 | 因为它的行为更接近于一个函数调用。[[link](#percent-literal-delimiters)] 1374 | 1375 | ```ruby 1376 | # 错误 1377 | %w[date locale] 1378 | %w{date locale} 1379 | %w|date locale| 1380 | 1381 | # 正确 1382 | %w(date locale) 1383 | ``` 1384 | 1385 | * 随意用 `%w` [[link](#percent-w)] 1386 | 1387 | ```ruby 1388 | STATES = %w(draft open closed) 1389 | ``` 1390 | 1391 | * 在一个单行字符串里需要 插值(interpolation) 和内嵌双引号时使用 `%()`。 1392 | 对于多行字符串,建议用 heredocs 语法。[[link](#percent-parens)] 1393 | 1394 | ```ruby 1395 | # 错误 - 不需要字符串插值 1396 | %(
Some text
) 1397 | # 直接 '
Some text
' 就行了 1398 | 1399 | # 错误 - 无双引号 1400 | %(This is #{quality} style) 1401 | # 直接 "This is #{quality} style" 就行了 1402 | 1403 | # 错误 - 多行了 1404 | %(
\n#{exclamation}\n
) 1405 | # 应该用 heredoc. 1406 | 1407 | # 正确 - 需要字符串插值, 有双引号, 单行. 1408 | %(#{name}) 1409 | ``` 1410 | 1411 | * 仅在需要匹配 *多于一个* '/'符号的时候使用 `%r`。[[link](#percent-r)] 1412 | 1413 | ```ruby 1414 | # 错误 1415 | %r(\s+) 1416 | 1417 | # 依然不好 1418 | %r(^/(.*)$) 1419 | # should be /^\/(.*)$/ 1420 | 1421 | # 正确 1422 | %r(^/blog/2011/(.*)$) 1423 | ``` 1424 | 1425 | * 避免使用 %x ,除非你要调用一个带引号的命令(非常少见的情况)。 1426 | [[link](#percent-x)] 1427 | 1428 | ```ruby 1429 | # 错误 1430 | date = %x(date) 1431 | 1432 | # 正确 1433 | date = `date` 1434 | echo = %x(echo `date`) 1435 | ``` 1436 | 1437 | ## Rails 1438 | 1439 | * 当调用 `render` 或 `redirect_to` 后需要马上"返回"时,把 `return` 放到下一行, 不要放到同一行。 1440 | [[link](#next-line-return)] 1441 | 1442 | ```ruby 1443 | # 错误 1444 | render :text => 'Howdy' and return 1445 | 1446 | # 正确 1447 | render :text => 'Howdy' 1448 | return 1449 | 1450 | # still bad 1451 | render :text => 'Howdy' and return if foo.present? 1452 | 1453 | # 正确 1454 | if foo.present? 1455 | render :text => 'Howdy' 1456 | return 1457 | end 1458 | ``` 1459 | 1460 | ### 范围 (Scopes) 1461 | * 当定义 ActiveRecord 的模型 scopes 时, 把内容用大括号包起来。 1462 | 如果不包的话, 在载入这个 class 时就会被强迫连接数据库。 1463 | [[link](#scope-lambda)] 1464 | 1465 | ```ruby 1466 | # 错误 1467 | scope :foo, where(:bar => 1) 1468 | 1469 | # 正确 1470 | scope :foo, -> { where(:bar => 1) } 1471 | ``` 1472 | 1473 | ## 保持一致 (Be Consistent) 1474 | 1475 | > 在你编辑某块代码时,看看周围的代码是什么风格。 1476 | > 如果它们在数学操作符两边都放了空格,那么你也应该这样做。 1477 | > 如果它们的代码注释用 # 井号包成了一个盒子, 那么你也应该这样做。 1478 | 1479 | > 风格指南存在的意义是 让看代码时能关注 "代码说的是什么"。 1480 | > 而不是 "代码是怎么说这件事的"。这份整体风格指南就是帮助你做这件事的。 1481 | > 注意局部的风格同样重要。如果一个部分的代码和周围的代码很不一样。 1482 | > 别人读的时候思路可能会被打断。尽量避免这一点。 1483 | 1484 | —[Google C++ Style Guide][google-c++] 1485 | 1486 | [airbnb-javascript]: https://github.com/airbnb/javascript 1487 | [bbatsov-ruby]: https://github.com/bbatsov/ruby-style-guide 1488 | [github-ruby]: https://github.com/styleguide/ruby 1489 | [google-c++]: https://google.github.io/styleguide/cppguide.html 1490 | [google-c++-comments]: https://google.github.io/styleguide/cppguide.html#Comments 1491 | [google-python-comments]: https://google.github.io/styleguide/pyguide.html#Comments 1492 | [ruby-naming-bang]: http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist 1493 | -------------------------------------------------------------------------------- /rationales.md: -------------------------------------------------------------------------------- 1 | # Rationales 2 | 3 | This document contains what are at times lengthy rationales and justifications 4 | for the decisions made in the main [Style guide](./README.md). 5 | 6 | ## Table of Contents 7 | 1. [Line Length](#line-length) 8 | 9 | ### Line Length 10 | 11 | Keeping code visually grouped together (as a 100-character line limit enforces) 12 | makes it easier to understand. For example, you don't have to scroll back and 13 | forth on one line to see what's going on -- you can view it all together. 14 | 15 | Here are examples from our codebase showing several techniques for 16 | breaking complex statements into multiple lines that are all < 100 17 | characters. Notice techniques like: 18 | 19 | * liberal use of linebreaks inside unclosed `(` `{` `[` 20 | * chaining methods, ending unfinished chains with a `.` 21 | * composing long strings by putting strings next to each other, separated 22 | by a backslash-then-newline. 23 | * breaking long logical statements with linebreaks after operators like 24 | `&&` and `||` 25 | 26 | ```ruby 27 | scope = Translation::Phrase.includes(:phrase_translations). 28 | joins(:phrase_screenshots). 29 | where(:phrase_screenshots => { 30 | :controller => controller_name, 31 | :action => JAROMIR_JAGR_SALUTE, 32 | }) 33 | ``` 34 | 35 | ```ruby 36 | translation = FactoryGirl.create( 37 | :phrase_translation, 38 | :locale => :is, 39 | :phrase => phrase, 40 | :key => 'phone_number_not_revealed_time_zone', 41 | :value => 'Símanúmerið þitt verður ekki birt. Það er aðeins hægt að hringja á '\ 42 | 'milli 9:00 og 21:00 %{time_zone}.' 43 | ) 44 | ``` 45 | 46 | ```ruby 47 | if @reservation_alteration.checkin == @reservation.start_date && 48 | @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights) 49 | 50 | redirect_to_alteration @reservation_alteration 51 | end 52 | ``` 53 | 54 | ```erb 55 | <% if @presenter.guest_visa_russia? %> 56 | <%= icon_tile_for(I18n.t("email.reservation_confirmed_guest.visa.details_header", 57 | :default => "Visa for foreign Travelers"), 58 | :beveled_big_icon => "stamp") do %> 59 | <%= I18n.t("email.reservation_confirmed_guest.visa.russia.details_copy", 60 | :default => "Foreign guests travelling to Russia may need to obtain a visa...") %> 61 | <% end %> 62 | <% end %> 63 | ``` 64 | 65 | These code snippets are very much more readable than the alternative: 66 | 67 | ```ruby 68 | scope = Translation::Phrase.includes(:phrase_translations).joins(:phrase_screenshots).where(:phrase_screenshots => { :controller => controller_name, :action => JAROMIR_JAGR_SALUTE }) 69 | 70 | translation = FactoryGirl.create(:phrase_translation, :locale => :is, :phrase => phrase, :key => 'phone_number_not_revealed_time_zone', :value => 'Símanúmerið þitt verður ekki birt. Það er aðeins hægt að hringja á milli 9:00 og 21:00 %{time_zone}.') 71 | 72 | if @reservation_alteration.checkin == @reservation.start_date && @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights) 73 | redirect_to_alteration @reservation_alteration 74 | end 75 | ``` 76 | 77 | ```erb 78 | <% if @presenter.guest_visa_russia? %> 79 | <%= icon_tile_for(I18n.t("email.reservation_confirmed_guest.visa.details_header", :default => "Visa for foreign Travelers"), :beveled_big_icon => "stamp") do %> 80 | <%= I18n.t("email.reservation_confirmed_guest.visa.russia.details_copy", :default => "Foreign guests travelling to Russia may need to obtain a visa prior to...") %> 81 | <% end %> 82 | <% end %> 83 | ``` 84 | --------------------------------------------------------------------------------