rubocop-hk 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +3732 -0
- data/STYLEGUIDE.md +1660 -0
- data/config/default.yml +59 -0
- data/config/rubocop-layout.yml +215 -0
- data/config/rubocop-lint.yml +43 -0
- data/config/rubocop-metrics.yml +14 -0
- data/config/rubocop-performance.yml +33 -0
- data/config/rubocop-rails.yml +374 -0
- data/config/rubocop-rspec.yml +29 -0
- data/config/rubocop-style.yml +197 -0
- data/lib/rubocop/hk/command.rb +27 -0
- data/lib/rubocop/hk/version.rb +7 -0
- data/lib/rubocop/hk.rb +9 -0
- metadata +126 -0
data/STYLEGUIDE.md
ADDED
@@ -0,0 +1,1660 @@
|
|
1
|
+
<div align="center">
|
2
|
+
|
3
|
+
# 🎨 Ruby Style Guide - RuboCop HK
|
4
|
+
|
5
|
+
> **The complete coding standards and conventions used by RuboCop HK**
|
6
|
+
|
7
|
+
[](https://github.com/rubocop/rubocop)
|
8
|
+
[](https://rubystyle.guide)
|
9
|
+
[](https://rubyonrails.org)
|
10
|
+
|
11
|
+
**[📚 Back to README](README.md) • [⚡ Quick Start](QUICK_START.md) • [📖 Usage Guide](USAGE.md) • [⚙️ Customization](CUSTOMIZATION.md)**
|
12
|
+
|
13
|
+
</div>
|
14
|
+
|
15
|
+
---
|
16
|
+
|
17
|
+
## 📋 Table of Contents
|
18
|
+
1. [Whitespace](#whitespace)
|
19
|
+
1. [Indentation](#indentation)
|
20
|
+
1. [Inline](#inline)
|
21
|
+
1. [Newlines](#newlines)
|
22
|
+
1. [Line Length](#line-length)
|
23
|
+
1. [Commenting](#commenting)
|
24
|
+
1. [Block and inline comments](#block-and-inline-comments)
|
25
|
+
1. [Punctuation, spelling, and grammar](#punctuation-spelling-and-grammar)
|
26
|
+
1. [TODO comments](#todo-comments)
|
27
|
+
1. [Commented-out code](#commented-out-code)
|
28
|
+
1. [Methods](#methods)
|
29
|
+
1. [Method definitions](#method-definitions)
|
30
|
+
1. [Method calls](#method-calls)
|
31
|
+
1. [Conditional Expressions](#conditional-expressions)
|
32
|
+
1. [Conditional keywords](#conditional-keywords)
|
33
|
+
1. [Ternary operator](#ternary-operator)
|
34
|
+
1. [Syntax](#syntax)
|
35
|
+
1. [Naming](#naming)
|
36
|
+
1. [Classes](#classes)
|
37
|
+
1. [Exceptions](#exceptions)
|
38
|
+
1. [Collections](#collections)
|
39
|
+
1. [Strings](#strings)
|
40
|
+
1. [Regular Expressions](#regular-expressions)
|
41
|
+
1. [Percent Literals](#percent-literals)
|
42
|
+
1. [Rails](#rails)
|
43
|
+
1. [Scopes](#scopes)
|
44
|
+
1. [Be Consistent](#be-consistent)
|
45
|
+
1. [Translation](#translation)
|
46
|
+
|
47
|
+
## Whitespace
|
48
|
+
|
49
|
+
### Indentation
|
50
|
+
|
51
|
+
* <a name="default-indentation"></a>Use soft-tabs with a
|
52
|
+
two-space indent.<sup>[[link](#default-indentation)]</sup>
|
53
|
+
|
54
|
+
* <a name="indent-when-as-case"></a>Indent `when` as deep as `case`.
|
55
|
+
<sup>[[link](#indent-when-as-case)]</sup>
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
case
|
59
|
+
when song.name == "Misty"
|
60
|
+
puts "Not again!"
|
61
|
+
when song.duration > 120
|
62
|
+
puts "Too long!"
|
63
|
+
when Time.now.hour > 21
|
64
|
+
puts "It's too late"
|
65
|
+
else
|
66
|
+
song.play
|
67
|
+
end
|
68
|
+
|
69
|
+
kind = case year
|
70
|
+
when 1850..1889 then "Blues"
|
71
|
+
when 1890..1909 then "Ragtime"
|
72
|
+
when 1910..1929 then "New Orleans Jazz"
|
73
|
+
when 1930..1939 then "Swing"
|
74
|
+
when 1940..1950 then "Bebop"
|
75
|
+
else "Jazz"
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
* <a name="align-function-params"></a>Align function parameters either all on
|
80
|
+
the same line or one per line.<sup>[[link](#align-function-params)]</sup>
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# bad
|
84
|
+
def self.create_translation(phrase_id, phrase_key, target_locale,
|
85
|
+
value, user_id, do_xss_check, allow_verification)
|
86
|
+
...
|
87
|
+
end
|
88
|
+
|
89
|
+
# good
|
90
|
+
def self.create_translation(phrase_id,
|
91
|
+
phrase_key,
|
92
|
+
target_locale,
|
93
|
+
value,
|
94
|
+
user_id,
|
95
|
+
do_xss_check,
|
96
|
+
allow_verification)
|
97
|
+
...
|
98
|
+
end
|
99
|
+
|
100
|
+
# good
|
101
|
+
def self.create_translation(
|
102
|
+
phrase_id,
|
103
|
+
phrase_key,
|
104
|
+
target_locale,
|
105
|
+
value,
|
106
|
+
user_id,
|
107
|
+
do_xss_check,
|
108
|
+
allow_verification
|
109
|
+
)
|
110
|
+
...
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
* <a name="indent-multi-line-bool"></a>Indent succeeding lines in multi-line
|
115
|
+
boolean expressions.<sup>[[link](#indent-multi-line-bool)]</sup>
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
# bad
|
119
|
+
def is_eligible?(user)
|
120
|
+
Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
|
121
|
+
is_in_program?(user) &&
|
122
|
+
program_not_expired
|
123
|
+
end
|
124
|
+
|
125
|
+
# good
|
126
|
+
def is_eligible?(user)
|
127
|
+
Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
|
128
|
+
is_in_program?(user) &&
|
129
|
+
program_not_expired
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### Inline
|
134
|
+
|
135
|
+
* <a name="trailing-whitespace"></a>Never leave trailing whitespace.
|
136
|
+
<sup>[[link](#trailing-whitespace)]</sup>
|
137
|
+
|
138
|
+
* <a name="space-before-comments"></a>When making inline comments, include a
|
139
|
+
space between the end of the code and the start of your comment.
|
140
|
+
<sup>[[link](#space-before-comments)]</sup>
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# bad
|
144
|
+
result = func(a, b)# we might want to change b to c
|
145
|
+
|
146
|
+
# good
|
147
|
+
result = func(a, b) # we might want to change b to c
|
148
|
+
```
|
149
|
+
|
150
|
+
* <a name="spaces-operators"></a>Use spaces around operators; after commas,
|
151
|
+
colons, and semicolons; and around `{` and before `}`.
|
152
|
+
<sup>[[link](#spaces-operators)]</sup>
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
sum = 1 + 2
|
156
|
+
a, b = 1, 2
|
157
|
+
1 > 2 ? true : false; puts "Hi"
|
158
|
+
[1, 2, 3].each { |e| puts e }
|
159
|
+
```
|
160
|
+
|
161
|
+
* <a name="no-space-before-commas"></a>Never include a space before a comma.
|
162
|
+
<sup>[[link](#no-space-before-commas)]</sup>
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
result = func(a, b)
|
166
|
+
```
|
167
|
+
|
168
|
+
* <a name="spaces-block-params"></a>Do not include space inside block
|
169
|
+
parameter pipes. Include one space between parameters in a block.
|
170
|
+
Include one space outside block parameter pipes.
|
171
|
+
<sup>[[link](#spaces-block-params)]</sup>
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# bad
|
175
|
+
{}.each { | x, y |puts x }
|
176
|
+
|
177
|
+
# good
|
178
|
+
{}.each { |x, y| puts x }
|
179
|
+
```
|
180
|
+
|
181
|
+
* <a name="no-space-after-!"></a>Do not leave space between `!` and its
|
182
|
+
argument.<sup>[[link](#no-space-after-!)]</sup>
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
!something
|
186
|
+
```
|
187
|
+
|
188
|
+
* <a name="no-spaces-braces"></a>No spaces after `(`, `[` or before `]`, `)`.
|
189
|
+
<sup>[[link](#no-spaces-braces)]</sup>
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
some(arg).other
|
193
|
+
[1, 2, 3].length
|
194
|
+
```
|
195
|
+
|
196
|
+
* <a name="no-spaces-string-interpolation"></a>Omit whitespace when doing
|
197
|
+
string interpolation.<sup>[[link](#no-spaces-string-interpolation)]</sup>
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
# bad
|
201
|
+
var = "This #{ foobar } is interpolated."
|
202
|
+
|
203
|
+
# good
|
204
|
+
var = "This #{foobar} is interpolated."
|
205
|
+
```
|
206
|
+
|
207
|
+
* <a name="no-spaces-range-literals"></a>Don't use extra whitespace in range
|
208
|
+
literals.<sup>[[link](#no-spaces-range-literals)]</sup>
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
# bad
|
212
|
+
(0 ... coll).each do |item|
|
213
|
+
|
214
|
+
# good
|
215
|
+
(0...coll).each do |item|
|
216
|
+
```
|
217
|
+
|
218
|
+
### Newlines
|
219
|
+
|
220
|
+
* <a name="multiline-if-newline"></a>Add a new line after `if` conditions spanning
|
221
|
+
multiple lines to help differentiate between the conditions and the body.
|
222
|
+
<sup>[[link](#multiline-if-newline)]</sup>
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
if @reservation_alteration.checkin == @reservation.start_date &&
|
226
|
+
@reservation_alteration.checkout == (@reservation.start_date + @reservation.nights)
|
227
|
+
|
228
|
+
redirect_to_alteration @reservation_alteration
|
229
|
+
end
|
230
|
+
```
|
231
|
+
|
232
|
+
* <a name="newline-after-conditional"></a>Add a new line after conditionals,
|
233
|
+
blocks, case statements, etc.<sup>[[link](#newline-after-conditional)]</sup>
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
if robot.is_awesome?
|
237
|
+
send_robot_present
|
238
|
+
end
|
239
|
+
|
240
|
+
robot.add_trait(:human_like_intelligence)
|
241
|
+
```
|
242
|
+
|
243
|
+
* <a name="newline-different-indent"></a>Don’t include newlines between areas
|
244
|
+
of different indentation (such as around class or module bodies).
|
245
|
+
<sup>[[link](#newline-different-indent)]</sup>
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
# bad
|
249
|
+
class Foo
|
250
|
+
|
251
|
+
def bar
|
252
|
+
# body omitted
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
# good
|
258
|
+
class Foo
|
259
|
+
def bar
|
260
|
+
# body omitted
|
261
|
+
end
|
262
|
+
end
|
263
|
+
```
|
264
|
+
|
265
|
+
* <a name="newline-between-methods"></a>Include one, but no more than one, new
|
266
|
+
line between methods.<sup>[[link](#newline-between-methods)]</sup>
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
def a
|
270
|
+
end
|
271
|
+
|
272
|
+
def b
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
* <a name="method-def-empty-lines"></a>Use a single empty line to break between
|
277
|
+
statements to break up methods into logical paragraphs internally.
|
278
|
+
<sup>[[link](#method-def-empty-lines)]</sup>
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
def transformorize_car
|
282
|
+
car = manufacture(options)
|
283
|
+
t = transformer(robot, disguise)
|
284
|
+
|
285
|
+
car.after_market_mod!
|
286
|
+
t.transform(car)
|
287
|
+
car.assign_cool_name!
|
288
|
+
|
289
|
+
fleet.add(car)
|
290
|
+
car
|
291
|
+
end
|
292
|
+
```
|
293
|
+
|
294
|
+
* <a name="trailing-newline"></a>End each file with a newline. Don't include
|
295
|
+
multiple newlines at the end of a file.
|
296
|
+
<sup>[[link](#trailing-newline)]</sup>
|
297
|
+
|
298
|
+
## Line Length
|
299
|
+
|
300
|
+
* Keep each line of code to a readable length. Unless
|
301
|
+
you have a reason to, keep lines to fewer than 100 characters.
|
302
|
+
([rationale](./rationales.md#line-length))<sup>
|
303
|
+
[[link](#line-length)]</sup>
|
304
|
+
|
305
|
+
## Commenting
|
306
|
+
|
307
|
+
> Though a pain to write, comments are absolutely vital to keeping our code
|
308
|
+
> readable. The following rules describe what you should comment and where. But
|
309
|
+
> remember: while comments are very important, the best code is
|
310
|
+
> self-documenting. Giving sensible names to types and variables is much better
|
311
|
+
> than using obscure names that you must then explain through comments.
|
312
|
+
|
313
|
+
> When writing your comments, write for your audience: the next contributor who
|
314
|
+
> will need to understand your code. Be generous — the next one may be you!
|
315
|
+
|
316
|
+
—[Google C++ Style Guide][google-c++]
|
317
|
+
|
318
|
+
Portions of this section borrow heavily from the Google
|
319
|
+
[C++][google-c++-comments] and [Python][google-python-comments] style guides.
|
320
|
+
|
321
|
+
### Block and inline comments
|
322
|
+
|
323
|
+
The final place to have comments is in tricky parts of the code. If you're
|
324
|
+
going to have to explain it at the next code review, you should comment it now.
|
325
|
+
Complicated operations get a few lines of comments before the operations
|
326
|
+
commence. Non-obvious ones get comments at the end of the line.
|
327
|
+
|
328
|
+
```ruby
|
329
|
+
def fallbacks_for(the_locale, opts = {})
|
330
|
+
# dup() to produce an array that we can mutate.
|
331
|
+
ret = @fallbacks[the_locale].dup
|
332
|
+
|
333
|
+
# We make two assumptions here:
|
334
|
+
# 1) There is only one default locale (that is, it has no less-specific
|
335
|
+
# children).
|
336
|
+
# 2) The default locale is just a language. (Like :en, and not :"en-US".)
|
337
|
+
if opts[:exclude_default] &&
|
338
|
+
ret.last == default_locale &&
|
339
|
+
ret.last != language_from_locale(the_locale)
|
340
|
+
ret.pop
|
341
|
+
end
|
342
|
+
|
343
|
+
ret
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
On the other hand, never describe the code. Assume the person reading the code
|
348
|
+
knows the language (though not what you're trying to do) better than you do.
|
349
|
+
|
350
|
+
<a name="no-block-comments"></a>Related: do not use block comments. They cannot
|
351
|
+
be preceded by whitespace and are not as easy to spot as regular comments.
|
352
|
+
<sup>[[link](#no-block-comments)]</sup>
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
# bad
|
356
|
+
=begin
|
357
|
+
comment line
|
358
|
+
another comment line
|
359
|
+
=end
|
360
|
+
|
361
|
+
# good
|
362
|
+
# comment line
|
363
|
+
# another comment line
|
364
|
+
```
|
365
|
+
|
366
|
+
### Punctuation, spelling and grammar
|
367
|
+
|
368
|
+
Pay attention to punctuation, spelling, and grammar; it is easier to read
|
369
|
+
well-written comments than badly written ones.
|
370
|
+
|
371
|
+
Comments should be as readable as narrative text, with proper capitalization
|
372
|
+
and punctuation. In many cases, complete sentences are more readable than
|
373
|
+
sentence fragments. Shorter comments, such as comments at the end of a line of
|
374
|
+
code, can sometimes be less formal, but you should be consistent with your
|
375
|
+
style.
|
376
|
+
|
377
|
+
Although it can be frustrating to have a code reviewer point out that you are
|
378
|
+
using a comma when you should be using a semicolon, it is very important that
|
379
|
+
source code maintain a high level of clarity and readability. Proper
|
380
|
+
punctuation, spelling, and grammar help with that goal.
|
381
|
+
|
382
|
+
### TODO comments
|
383
|
+
|
384
|
+
Use TODO comments for code that is temporary, a short-term solution, or
|
385
|
+
good-enough but not perfect.
|
386
|
+
|
387
|
+
TODOs should include the string TODO in all caps, followed by the full name
|
388
|
+
of the person who can best provide context about the problem referenced by the
|
389
|
+
TODO, in parentheses. A colon is optional. A comment explaining what there is
|
390
|
+
to do is required. The main purpose is to have a consistent TODO format that
|
391
|
+
can be searched to find the person who can provide more details upon request.
|
392
|
+
A TODO is not a commitment that the person referenced will fix the problem.
|
393
|
+
Thus when you create a TODO, it is almost always your name that is given.
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
# bad
|
397
|
+
# TODO(RS): Use proper namespacing for this constant.
|
398
|
+
|
399
|
+
# bad
|
400
|
+
# TODO(drumm3rz4lyfe): Use proper namespacing for this constant.
|
401
|
+
|
402
|
+
# good
|
403
|
+
# TODO(Ringo Starr): Use proper namespacing for this constant.
|
404
|
+
```
|
405
|
+
|
406
|
+
### Commented-out code
|
407
|
+
|
408
|
+
* <a name="commented-code"></a>Never leave commented-out code in our codebase.
|
409
|
+
<sup>[[link](#commented-code)]</sup>
|
410
|
+
|
411
|
+
## Methods
|
412
|
+
|
413
|
+
### Method definitions
|
414
|
+
|
415
|
+
* <a name="method-def-parens"></a>Use `def` with parentheses when there are
|
416
|
+
parameters. Omit the parentheses when the method doesn't accept any
|
417
|
+
parameters.<sup>[[link](#method-def-parens)]</sup>
|
418
|
+
|
419
|
+
```ruby
|
420
|
+
def some_method
|
421
|
+
# body omitted
|
422
|
+
end
|
423
|
+
|
424
|
+
def some_method_with_parameters(arg1, arg2)
|
425
|
+
# body omitted
|
426
|
+
end
|
427
|
+
```
|
428
|
+
|
429
|
+
* <a name="no-default-args"></a>Do not use default positional arguments.
|
430
|
+
Use keyword arguments (if available - in Ruby 2.0 or later) or an options
|
431
|
+
hash instead.<sup>[[link](#no-default-args)]</sup>
|
432
|
+
|
433
|
+
```ruby
|
434
|
+
# bad
|
435
|
+
def obliterate(things, gently = true, except = [], at = Time.now)
|
436
|
+
...
|
437
|
+
end
|
438
|
+
|
439
|
+
# good
|
440
|
+
def obliterate(things, gently: true, except: [], at: Time.now)
|
441
|
+
...
|
442
|
+
end
|
443
|
+
|
444
|
+
# good
|
445
|
+
def obliterate(things, options = {})
|
446
|
+
options = {
|
447
|
+
:gently => true, # obliterate with soft-delete
|
448
|
+
:except => [], # skip obliterating these things
|
449
|
+
:at => Time.now, # don't obliterate them until later
|
450
|
+
}.merge(options)
|
451
|
+
|
452
|
+
...
|
453
|
+
end
|
454
|
+
```
|
455
|
+
|
456
|
+
* <a name="no-single-line-methods"></a>Avoid single-line methods. Although
|
457
|
+
they are somewhat popular in the wild, there are a few peculiarities about
|
458
|
+
their definition syntax that make their use undesirable.
|
459
|
+
<sup>[[link](#no-single-line-methods)]</sup>
|
460
|
+
|
461
|
+
```ruby
|
462
|
+
# bad
|
463
|
+
def too_much; something; something_else; end
|
464
|
+
|
465
|
+
# good
|
466
|
+
def some_method
|
467
|
+
# body
|
468
|
+
end
|
469
|
+
```
|
470
|
+
|
471
|
+
### Method calls
|
472
|
+
|
473
|
+
**Use parentheses** for a method call:
|
474
|
+
|
475
|
+
* <a name="returns-val-parens"></a>If the method returns a value.
|
476
|
+
<sup>[[link](#returns-val-parens)]</sup>
|
477
|
+
|
478
|
+
```ruby
|
479
|
+
# bad
|
480
|
+
@current_user = User.find_by_id 1964192
|
481
|
+
|
482
|
+
# good
|
483
|
+
@current_user = User.find_by_id(1964192)
|
484
|
+
```
|
485
|
+
|
486
|
+
* <a name="first-arg-parens"></a>If the first argument to the method uses
|
487
|
+
parentheses.<sup>[[link](#first-arg-parens)]</sup>
|
488
|
+
|
489
|
+
```ruby
|
490
|
+
# bad
|
491
|
+
put! (x + y) % len, value
|
492
|
+
|
493
|
+
# good
|
494
|
+
put!((x + y) % len, value)
|
495
|
+
```
|
496
|
+
|
497
|
+
* <a name="space-method-call"></a>Never put a space between a method name and
|
498
|
+
the opening parenthesis.<sup>[[link](#space-method-call)]</sup>
|
499
|
+
|
500
|
+
```ruby
|
501
|
+
# bad
|
502
|
+
f (3 + 2) + 1
|
503
|
+
|
504
|
+
# good
|
505
|
+
f(3 + 2) + 1
|
506
|
+
```
|
507
|
+
|
508
|
+
* <a name="no-args-parens"></a>**Omit parentheses** for a method call if the
|
509
|
+
method accepts no arguments.<sup>[[link](#no-args-parens)]</sup>
|
510
|
+
|
511
|
+
```ruby
|
512
|
+
# bad
|
513
|
+
nil?()
|
514
|
+
|
515
|
+
# good
|
516
|
+
nil?
|
517
|
+
```
|
518
|
+
|
519
|
+
* <a name="no-return-parens"></a>If the method doesn't return a value (or we
|
520
|
+
don't care about the return), parentheses are optional. (Especially if the
|
521
|
+
arguments overflow to multiple lines, parentheses may add readability.)
|
522
|
+
<sup>[[link](#no-return-parens)]</sup>
|
523
|
+
|
524
|
+
```ruby
|
525
|
+
# okay
|
526
|
+
render(:partial => "foo")
|
527
|
+
|
528
|
+
# okay
|
529
|
+
render :partial => "foo"
|
530
|
+
```
|
531
|
+
|
532
|
+
In either case:
|
533
|
+
|
534
|
+
* <a name="options-no-braces"></a>If a method accepts an options hash as the
|
535
|
+
last argument, do not use `{` `}` during invocation.
|
536
|
+
<sup>[[link](#options-no-braces)]</sup>
|
537
|
+
|
538
|
+
```ruby
|
539
|
+
# bad
|
540
|
+
get "/v1/reservations", { :id => 54875 }
|
541
|
+
|
542
|
+
# good
|
543
|
+
get "/v1/reservations", :id => 54875
|
544
|
+
```
|
545
|
+
|
546
|
+
## Conditional Expressions
|
547
|
+
|
548
|
+
### Conditional keywords
|
549
|
+
|
550
|
+
* <a name="multiline-if-then"></a>Never use `then` for multi-line `if/unless`.
|
551
|
+
<sup>[[link](#multiline-if-then)]</sup>
|
552
|
+
|
553
|
+
```ruby
|
554
|
+
# bad
|
555
|
+
if some_condition then
|
556
|
+
...
|
557
|
+
end
|
558
|
+
|
559
|
+
# good
|
560
|
+
if some_condition
|
561
|
+
...
|
562
|
+
end
|
563
|
+
```
|
564
|
+
|
565
|
+
* <a name="multiline-while-until"></a>Never use `do` for multi-line `while` or
|
566
|
+
`until`.<sup>[[link](#multiline-while-until)]</sup>
|
567
|
+
|
568
|
+
```ruby
|
569
|
+
# bad
|
570
|
+
while x > 5 do
|
571
|
+
...
|
572
|
+
end
|
573
|
+
|
574
|
+
until x > 5 do
|
575
|
+
...
|
576
|
+
end
|
577
|
+
|
578
|
+
# good
|
579
|
+
while x > 5
|
580
|
+
...
|
581
|
+
end
|
582
|
+
|
583
|
+
until x > 5
|
584
|
+
...
|
585
|
+
end
|
586
|
+
```
|
587
|
+
|
588
|
+
* <a name="no-and-or"></a>The `and`, `or`, and `not` keywords are banned. It's
|
589
|
+
just not worth it. Always use `&&`, `||`, and `!` instead.
|
590
|
+
<sup>[[link](#no-and-or)]</sup>
|
591
|
+
|
592
|
+
* <a name="only-simple-if-unless"></a>Modifier `if/unless` usage is okay when
|
593
|
+
the body is simple, the condition is simple, and the whole thing fits on
|
594
|
+
one line. Otherwise, avoid modifier `if/unless`.
|
595
|
+
<sup>[[link](#only-simple-if-unless)]</sup>
|
596
|
+
|
597
|
+
```ruby
|
598
|
+
# bad - this doesn't fit on one line
|
599
|
+
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?
|
600
|
+
|
601
|
+
# okay
|
602
|
+
if request_opts[:trebuchet_experiments_on_page] &&
|
603
|
+
!request_opts[:trebuchet_experiments_on_page].empty?
|
604
|
+
|
605
|
+
add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])
|
606
|
+
end
|
607
|
+
|
608
|
+
# bad - this is complex and deserves multiple lines and a comment
|
609
|
+
parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i)
|
610
|
+
|
611
|
+
# okay
|
612
|
+
return if reconciled?
|
613
|
+
```
|
614
|
+
|
615
|
+
* <a name="no-unless-with-else"></a>Never use `unless` with `else`. Rewrite
|
616
|
+
these with the positive case first.<sup>[[link](#no-unless-with-else)]</sup>
|
617
|
+
|
618
|
+
```ruby
|
619
|
+
# bad
|
620
|
+
unless success?
|
621
|
+
puts "failure"
|
622
|
+
else
|
623
|
+
puts "success"
|
624
|
+
end
|
625
|
+
|
626
|
+
# good
|
627
|
+
if success?
|
628
|
+
puts "success"
|
629
|
+
else
|
630
|
+
puts "failure"
|
631
|
+
end
|
632
|
+
```
|
633
|
+
|
634
|
+
* <a name="unless-with-multiple-conditions"></a>Avoid `unless` with multiple
|
635
|
+
conditions.<sup>[[link](#unless-with-multiple-conditions)]</sup>
|
636
|
+
|
637
|
+
```ruby
|
638
|
+
# bad
|
639
|
+
unless foo? && bar?
|
640
|
+
...
|
641
|
+
end
|
642
|
+
|
643
|
+
# okay
|
644
|
+
if !(foo? && bar?)
|
645
|
+
...
|
646
|
+
end
|
647
|
+
```
|
648
|
+
|
649
|
+
* <a name="unless-with-comparison-operator"></a>Avoid `unless` with comparison operators if you can use `if` with an opposing comparison operator.<sup>[[link](#unless-with-comparison-operator)]</sup>
|
650
|
+
|
651
|
+
```ruby
|
652
|
+
# bad
|
653
|
+
unless x == 10
|
654
|
+
...
|
655
|
+
end
|
656
|
+
|
657
|
+
# good
|
658
|
+
if x != 10
|
659
|
+
...
|
660
|
+
end
|
661
|
+
|
662
|
+
# bad
|
663
|
+
unless x < 10
|
664
|
+
...
|
665
|
+
end
|
666
|
+
|
667
|
+
# good
|
668
|
+
if x >= 10
|
669
|
+
...
|
670
|
+
end
|
671
|
+
|
672
|
+
# ok
|
673
|
+
unless x === 10
|
674
|
+
...
|
675
|
+
end
|
676
|
+
```
|
677
|
+
|
678
|
+
* <a name="parens-around-conditions"></a>Don't use parentheses around the
|
679
|
+
condition of an `if/unless/while`.
|
680
|
+
<sup>[[link](#parens-around-conditions)]</sup>
|
681
|
+
|
682
|
+
```ruby
|
683
|
+
# bad
|
684
|
+
if (x > 10)
|
685
|
+
...
|
686
|
+
end
|
687
|
+
|
688
|
+
# good
|
689
|
+
if x > 10
|
690
|
+
...
|
691
|
+
end
|
692
|
+
|
693
|
+
```
|
694
|
+
|
695
|
+
### Ternary operator
|
696
|
+
|
697
|
+
* <a name="avoid-complex-ternary"></a>Avoid the ternary operator (`?:`) except
|
698
|
+
in cases where all expressions are extremely trivial. However, do use the
|
699
|
+
ternary operator(`?:`) over `if/then/else/end` constructs for single line
|
700
|
+
conditionals.<sup>[[link](#avoid-complex-ternary)]</sup>
|
701
|
+
|
702
|
+
```ruby
|
703
|
+
# bad
|
704
|
+
result = if some_condition then something else something_else end
|
705
|
+
|
706
|
+
# good
|
707
|
+
result = some_condition ? something : something_else
|
708
|
+
```
|
709
|
+
|
710
|
+
* <a name="no-nested-ternaries"></a>Use one expression per branch in a ternary
|
711
|
+
operator. This also means that ternary operators must not be nested. Prefer
|
712
|
+
`if/else` constructs in these cases.<sup>[[link](#no-nested-ternaries)]</sup>
|
713
|
+
|
714
|
+
```ruby
|
715
|
+
# bad
|
716
|
+
some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
|
717
|
+
|
718
|
+
# good
|
719
|
+
if some_condition
|
720
|
+
nested_condition ? nested_something : nested_something_else
|
721
|
+
else
|
722
|
+
something_else
|
723
|
+
end
|
724
|
+
```
|
725
|
+
|
726
|
+
* <a name="single-condition-ternary"></a>Avoid multiple conditions in ternaries.
|
727
|
+
Ternaries are best used with single conditions.
|
728
|
+
<sup>[[link](#single-condition-ternary)]</sup>
|
729
|
+
|
730
|
+
* <a name="no-multiline-ternaries"></a>Avoid multi-line `?:` (the ternary
|
731
|
+
operator), use `if/then/else/end` instead.
|
732
|
+
<sup>[[link](#no-multiline-ternaries)]</sup>
|
733
|
+
|
734
|
+
```ruby
|
735
|
+
# bad
|
736
|
+
some_really_long_condition_that_might_make_you_want_to_split_lines ?
|
737
|
+
something : something_else
|
738
|
+
|
739
|
+
# good
|
740
|
+
if some_really_long_condition_that_might_make_you_want_to_split_lines
|
741
|
+
something
|
742
|
+
else
|
743
|
+
something_else
|
744
|
+
end
|
745
|
+
```
|
746
|
+
|
747
|
+
### Nested conditionals
|
748
|
+
|
749
|
+
* <a name="no-nested-conditionals"></a>
|
750
|
+
Avoid the use of nested conditionals for flow of control.
|
751
|
+
([More on this][avoid-else-return-early].) <sup>[[link](#no-nested-conditionals)]</sup>
|
752
|
+
|
753
|
+
Prefer a guard clause when you can assert invalid data. A guard clause
|
754
|
+
is a conditional statement at the top of a function that returns as soon
|
755
|
+
as it can.
|
756
|
+
|
757
|
+
The general principles boil down to:
|
758
|
+
* Return immediately once you know your function cannot do anything more.
|
759
|
+
* Reduce nesting and indentation in the code by returning early. This makes
|
760
|
+
the code easier to read and requires less mental bookkeeping on the part
|
761
|
+
of the reader to keep track of `else` branches.
|
762
|
+
* The core or most important flows should be the least indented.
|
763
|
+
|
764
|
+
```ruby
|
765
|
+
# bad
|
766
|
+
def compute
|
767
|
+
server = find_server
|
768
|
+
if server
|
769
|
+
client = server.client
|
770
|
+
if client
|
771
|
+
request = client.make_request
|
772
|
+
if request
|
773
|
+
process_request(request)
|
774
|
+
end
|
775
|
+
end
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
# good
|
780
|
+
def compute
|
781
|
+
server = find_server
|
782
|
+
return unless server
|
783
|
+
client = server.client
|
784
|
+
return unless client
|
785
|
+
request = client.make_request
|
786
|
+
return unless request
|
787
|
+
process_request(request)
|
788
|
+
end
|
789
|
+
```
|
790
|
+
|
791
|
+
Prefer `next` in loops instead of conditional blocks.
|
792
|
+
|
793
|
+
```ruby
|
794
|
+
# bad
|
795
|
+
[0, 1, 2, 3].each do |item|
|
796
|
+
if item > 1
|
797
|
+
puts item
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
# good
|
802
|
+
[0, 1, 2, 3].each do |item|
|
803
|
+
next unless item > 1
|
804
|
+
puts item
|
805
|
+
end
|
806
|
+
```
|
807
|
+
|
808
|
+
See also the section "Guard Clause", p68-70 in Beck, Kent.
|
809
|
+
*Implementation Patterns*. Upper Saddle River: Addison-Wesley, 2008, which
|
810
|
+
has inspired some of the content above.
|
811
|
+
|
812
|
+
## Syntax
|
813
|
+
|
814
|
+
* <a name="no-for"></a>Never use `for`, unless you know exactly why. Most of the
|
815
|
+
time iterators should be used instead. `for` is implemented in terms of
|
816
|
+
`each` (so you're adding a level of indirection), but with a twist - `for`
|
817
|
+
doesn't introduce a new scope (unlike `each`) and variables defined in its
|
818
|
+
block will be visible outside it.<sup>[[link](#no-for)]</sup>
|
819
|
+
|
820
|
+
```ruby
|
821
|
+
arr = [1, 2, 3]
|
822
|
+
|
823
|
+
# bad
|
824
|
+
for elem in arr do
|
825
|
+
puts elem
|
826
|
+
end
|
827
|
+
|
828
|
+
# good
|
829
|
+
arr.each { |elem| puts elem }
|
830
|
+
```
|
831
|
+
|
832
|
+
* <a name="single-line-blocks"></a>Prefer `{...}` over `do...end` for
|
833
|
+
single-line blocks. Avoid using `{...}` for multi-line blocks (multiline
|
834
|
+
chaining is always ugly). Always use `do...end` for "control flow" and
|
835
|
+
"method definitions" (e.g. in Rakefiles and certain DSLs). Avoid `do...end`
|
836
|
+
when chaining.<sup>[[link](#single-line-blocks)]</sup>
|
837
|
+
|
838
|
+
```ruby
|
839
|
+
names = ["Bozhidar", "Steve", "Sarah"]
|
840
|
+
|
841
|
+
# good
|
842
|
+
names.each { |name| puts name }
|
843
|
+
|
844
|
+
# bad
|
845
|
+
names.each do |name| puts name end
|
846
|
+
|
847
|
+
# good
|
848
|
+
names.each do |name|
|
849
|
+
puts name
|
850
|
+
puts "yay!"
|
851
|
+
end
|
852
|
+
|
853
|
+
# bad
|
854
|
+
names.each { |name|
|
855
|
+
puts name
|
856
|
+
puts "yay!"
|
857
|
+
}
|
858
|
+
|
859
|
+
# good
|
860
|
+
names.select { |name| name.start_with?("S") }.map { |name| name.upcase }
|
861
|
+
|
862
|
+
# bad
|
863
|
+
names.select do |name|
|
864
|
+
name.start_with?("S")
|
865
|
+
end.map { |name| name.upcase }
|
866
|
+
```
|
867
|
+
|
868
|
+
Some will argue that multiline chaining would look okay with the use of
|
869
|
+
`{...}`, but they should ask themselves if this code is really readable and
|
870
|
+
whether the block's content can be extracted into nifty methods.
|
871
|
+
|
872
|
+
* <a name="self-assignment"></a>Use shorthand self assignment operators
|
873
|
+
whenever applicable.<sup>[[link](#self-assignment)]</sup>
|
874
|
+
|
875
|
+
```ruby
|
876
|
+
# bad
|
877
|
+
x = x + y
|
878
|
+
x = x * y
|
879
|
+
x = x**y
|
880
|
+
x = x / y
|
881
|
+
x = x || y
|
882
|
+
x = x && y
|
883
|
+
|
884
|
+
# good
|
885
|
+
x += y
|
886
|
+
x *= y
|
887
|
+
x **= y
|
888
|
+
x /= y
|
889
|
+
x ||= y
|
890
|
+
x &&= y
|
891
|
+
```
|
892
|
+
|
893
|
+
* <a name="semicolons"></a>Avoid semicolons except for in single line class
|
894
|
+
definitions. When it is appropriate to use a semicolon, it should be
|
895
|
+
directly adjacent to the statement it terminates: there should be no
|
896
|
+
space before the semicolon.<sup>[[link](#semicolons)]</sup>
|
897
|
+
|
898
|
+
```ruby
|
899
|
+
# bad
|
900
|
+
puts "foobar"; # superfluous semicolon
|
901
|
+
puts "foo"; puts "bar" # two expressions on the same line
|
902
|
+
|
903
|
+
# good
|
904
|
+
puts "foobar"
|
905
|
+
|
906
|
+
puts "foo"
|
907
|
+
puts "bar"
|
908
|
+
|
909
|
+
puts "foo", "bar" # this applies to puts in particular
|
910
|
+
```
|
911
|
+
|
912
|
+
* <a name="colon-use"></a>Use :: only to reference constants(this includes
|
913
|
+
classes and modules) and constructors (like Array() or Nokogiri::HTML()).
|
914
|
+
Do not use :: for regular method invocation.<sup>[[link](#colon-use)]</sup>
|
915
|
+
|
916
|
+
```ruby
|
917
|
+
# bad
|
918
|
+
SomeClass::some_method
|
919
|
+
some_object::some_method
|
920
|
+
|
921
|
+
# good
|
922
|
+
SomeClass.some_method
|
923
|
+
some_object.some_method
|
924
|
+
SomeModule::SomeClass::SOME_CONST
|
925
|
+
SomeModule::SomeClass()
|
926
|
+
```
|
927
|
+
|
928
|
+
* <a name="redundant-return"></a>Avoid `return` where not required.
|
929
|
+
<sup>[[link](#redundant-return)]</sup>
|
930
|
+
|
931
|
+
```ruby
|
932
|
+
# bad
|
933
|
+
def some_method(some_arr)
|
934
|
+
return some_arr.size
|
935
|
+
end
|
936
|
+
|
937
|
+
# good
|
938
|
+
def some_method(some_arr)
|
939
|
+
some_arr.size
|
940
|
+
end
|
941
|
+
```
|
942
|
+
|
943
|
+
* <a name="assignment-in-conditionals"></a>Don't use the return value of `=` in
|
944
|
+
conditionals<sup>[[link](#assignment-in-conditionals)]</sup>
|
945
|
+
|
946
|
+
```ruby
|
947
|
+
# bad - shows intended use of assignment
|
948
|
+
if (v = array.grep(/foo/))
|
949
|
+
...
|
950
|
+
end
|
951
|
+
|
952
|
+
# bad
|
953
|
+
if v = array.grep(/foo/)
|
954
|
+
...
|
955
|
+
end
|
956
|
+
|
957
|
+
# good
|
958
|
+
v = array.grep(/foo/)
|
959
|
+
if v
|
960
|
+
...
|
961
|
+
end
|
962
|
+
|
963
|
+
```
|
964
|
+
|
965
|
+
* <a name="double-pipe-for-uninit"></a>Use `||=` freely to initialize variables.
|
966
|
+
<sup>[[link](#double-pipe-for-uninit)]</sup>
|
967
|
+
|
968
|
+
```ruby
|
969
|
+
# set name to Bozhidar, only if it's nil or false
|
970
|
+
name ||= "Bozhidar"
|
971
|
+
```
|
972
|
+
|
973
|
+
* <a name="no-double-pipes-for-bools"></a>Don't use `||=` to initialize boolean
|
974
|
+
variables. (Consider what would happen if the current value happened to be
|
975
|
+
`false`.)<sup>[[link](#no-double-pipes-for-bools)]</sup>
|
976
|
+
|
977
|
+
```ruby
|
978
|
+
# bad - would set enabled to true even if it was false
|
979
|
+
enabled ||= true
|
980
|
+
|
981
|
+
# good
|
982
|
+
enabled = true if enabled.nil?
|
983
|
+
```
|
984
|
+
|
985
|
+
* <a name="lambda-calls"></a>Use `.call` explicitly when calling lambdas.
|
986
|
+
<sup>[[link](#lambda-calls)]</sup>
|
987
|
+
|
988
|
+
```ruby
|
989
|
+
# bad
|
990
|
+
lambda.(x, y)
|
991
|
+
|
992
|
+
# good
|
993
|
+
lambda.call(x, y)
|
994
|
+
```
|
995
|
+
|
996
|
+
* <a name="no-cryptic-perl"></a>Avoid using Perl-style special variables (like
|
997
|
+
`$0-9`, `$`, etc. ). They are quite cryptic and their use in anything but
|
998
|
+
one-liner scripts is discouraged. Prefer long form versions such as
|
999
|
+
`$PROGRAM_NAME`.<sup>[[link](#no-cryptic-perl)]</sup>
|
1000
|
+
|
1001
|
+
* <a name="single-action-blocks"></a>When a method block takes only one
|
1002
|
+
argument, and the body consists solely of reading an attribute or calling
|
1003
|
+
one method with no arguments, use the `&:` shorthand.
|
1004
|
+
<sup>[[link](#single-action-blocks)]</sup>
|
1005
|
+
|
1006
|
+
```ruby
|
1007
|
+
# bad
|
1008
|
+
bluths.map { |bluth| bluth.occupation }
|
1009
|
+
bluths.select { |bluth| bluth.blue_self? }
|
1010
|
+
|
1011
|
+
# good
|
1012
|
+
bluths.map(&:occupation)
|
1013
|
+
bluths.select(&:blue_self?)
|
1014
|
+
```
|
1015
|
+
|
1016
|
+
* <a name="redundant-self"></a>Prefer `some_method` over `self.some_method` when
|
1017
|
+
calling a method on the current instance.<sup>[[link](#redundant-self)]</sup>
|
1018
|
+
|
1019
|
+
```ruby
|
1020
|
+
# bad
|
1021
|
+
def end_date
|
1022
|
+
self.start_date + self.nights
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
# good
|
1026
|
+
def end_date
|
1027
|
+
start_date + nights
|
1028
|
+
end
|
1029
|
+
```
|
1030
|
+
|
1031
|
+
In the following three common cases, `self.` is required by the language
|
1032
|
+
and is good to use:
|
1033
|
+
|
1034
|
+
1. When defining a class method: `def self.some_method`.
|
1035
|
+
2. The *left hand side* when calling an assignment method, including assigning
|
1036
|
+
an attribute when `self` is an ActiveRecord model: `self.guest = user`.
|
1037
|
+
3. Referencing the current instance's class: `self.class`.
|
1038
|
+
|
1039
|
+
* <a name="freeze-constants"></a>When defining an object of any mutable
|
1040
|
+
type meant to be a constant, make sure to call `freeze` on it. Common
|
1041
|
+
examples are strings, arrays, and hashes.
|
1042
|
+
([More on this][ruby-freeze].)<sup>[[link](#freeze-constants)]</sup>
|
1043
|
+
|
1044
|
+
The reason is that Ruby constants are actually mutable. Calling `freeze`
|
1045
|
+
ensures they are not mutated and are therefore truly constant and
|
1046
|
+
attempting to modify them will raise an exception. For strings, this allows
|
1047
|
+
older versions of Ruby below 2.2 to intern them.
|
1048
|
+
|
1049
|
+
```ruby
|
1050
|
+
# bad
|
1051
|
+
class Color
|
1052
|
+
RED = "red"
|
1053
|
+
BLUE = "blue"
|
1054
|
+
GREEN = "green"
|
1055
|
+
|
1056
|
+
ALL_COLORS = [
|
1057
|
+
RED,
|
1058
|
+
BLUE,
|
1059
|
+
GREEN,
|
1060
|
+
]
|
1061
|
+
|
1062
|
+
COLOR_TO_RGB = {
|
1063
|
+
RED => 0xFF0000,
|
1064
|
+
BLUE => 0x0000FF,
|
1065
|
+
GREEN => 0x00FF00,
|
1066
|
+
}
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
# good
|
1070
|
+
class Color
|
1071
|
+
RED = "red".freeze
|
1072
|
+
BLUE = "blue".freeze
|
1073
|
+
GREEN = "green".freeze
|
1074
|
+
|
1075
|
+
ALL_COLORS = [
|
1076
|
+
RED,
|
1077
|
+
BLUE,
|
1078
|
+
GREEN,
|
1079
|
+
].freeze
|
1080
|
+
|
1081
|
+
COLOR_TO_RGB = {
|
1082
|
+
RED => 0xFF0000,
|
1083
|
+
BLUE => 0x0000FF,
|
1084
|
+
GREEN => 0x00FF00,
|
1085
|
+
}.freeze
|
1086
|
+
end
|
1087
|
+
```
|
1088
|
+
|
1089
|
+
## Naming
|
1090
|
+
|
1091
|
+
* <a name="snake-case"></a>Use `snake_case` for methods and variables.
|
1092
|
+
<sup>[[link](#snake-case)]</sup>
|
1093
|
+
|
1094
|
+
* <a name="camel-case"></a>Use `CamelCase` for classes and modules. (Keep
|
1095
|
+
acronyms like HTTP, RFC, XML uppercase.)
|
1096
|
+
<sup>[[link](#camel-case)]</sup>
|
1097
|
+
|
1098
|
+
* <a name="screaming-snake-case"></a>Use `SCREAMING_SNAKE_CASE` for other
|
1099
|
+
constants.<sup>[[link](#screaming-snake-case)]</sup>
|
1100
|
+
|
1101
|
+
* <a name="predicate-method-names"></a>The names of predicate methods (methods
|
1102
|
+
that return a boolean value) should end in a question mark.
|
1103
|
+
(i.e. `Array#empty?`).<sup>[[link](#predicate-method-names)]</sup>
|
1104
|
+
|
1105
|
+
* <a name="bang-methods"></a>The names of potentially "dangerous" methods
|
1106
|
+
(i.e. methods that modify `self` or the arguments, `exit!`, etc.) should
|
1107
|
+
end with an exclamation mark. Bang methods should only exist if a non-bang
|
1108
|
+
method exists. ([More on this][ruby-naming-bang].)
|
1109
|
+
<sup>[[link](#bang-methods)]</sup>
|
1110
|
+
|
1111
|
+
* <a name="throwaway-variables"></a>Name throwaway variables `_`.
|
1112
|
+
<sup>[[link](#throwaway-variables)]</sup>
|
1113
|
+
|
1114
|
+
```ruby
|
1115
|
+
version = "3.2.1"
|
1116
|
+
major_version, minor_version, _ = version.split(".")
|
1117
|
+
```
|
1118
|
+
|
1119
|
+
## Classes
|
1120
|
+
|
1121
|
+
* <a name="avoid-class-variables"></a>Avoid the usage of class (`@@`) variables
|
1122
|
+
due to their "nasty" behavior in inheritance.
|
1123
|
+
<sup>[[link](#avoid-class-variables)]</sup>
|
1124
|
+
|
1125
|
+
```ruby
|
1126
|
+
class Parent
|
1127
|
+
@@class_var = "parent"
|
1128
|
+
|
1129
|
+
def self.print_class_var
|
1130
|
+
puts @@class_var
|
1131
|
+
end
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
class Child < Parent
|
1135
|
+
@@class_var = "child"
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
Parent.print_class_var # => will print "child"
|
1139
|
+
```
|
1140
|
+
|
1141
|
+
As you can see all the classes in a class hierarchy actually share one
|
1142
|
+
class variable. Class instance variables should usually be preferred
|
1143
|
+
over class variables.
|
1144
|
+
|
1145
|
+
* <a name="singleton-methods"></a>Use `def self.method` to define singleton
|
1146
|
+
methods. This makes the methods more resistant to refactoring changes.
|
1147
|
+
<sup>[[link](#singleton-methods)]</sup>
|
1148
|
+
|
1149
|
+
```ruby
|
1150
|
+
class TestClass
|
1151
|
+
# bad
|
1152
|
+
def TestClass.some_method
|
1153
|
+
...
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
# good
|
1157
|
+
def self.some_other_method
|
1158
|
+
...
|
1159
|
+
end
|
1160
|
+
```
|
1161
|
+
* <a name="no-class-self"></a>Avoid `class << self` except when necessary,
|
1162
|
+
e.g. single accessors and aliased attributes.
|
1163
|
+
<sup>[[link](#no-class-self)]</sup>
|
1164
|
+
|
1165
|
+
```ruby
|
1166
|
+
class TestClass
|
1167
|
+
# bad
|
1168
|
+
class << self
|
1169
|
+
def first_method
|
1170
|
+
...
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
def second_method_etc
|
1174
|
+
...
|
1175
|
+
end
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
# good
|
1179
|
+
class << self
|
1180
|
+
attr_accessor :per_page
|
1181
|
+
alias_method :nwo, :find_by_name_with_owner
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
def self.first_method
|
1185
|
+
...
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
def self.second_method_etc
|
1189
|
+
...
|
1190
|
+
end
|
1191
|
+
end
|
1192
|
+
```
|
1193
|
+
|
1194
|
+
* <a name="access-modifiers"></a>Indent the `public`, `protected`, and
|
1195
|
+
`private` methods as much the method definitions they apply to. Leave one
|
1196
|
+
blank line above and below them.<sup>[[link](#access-modifiers)]</sup>
|
1197
|
+
|
1198
|
+
```ruby
|
1199
|
+
class SomeClass
|
1200
|
+
def public_method
|
1201
|
+
# ...
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
private
|
1205
|
+
|
1206
|
+
def private_method
|
1207
|
+
# ...
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
```
|
1211
|
+
|
1212
|
+
## Exceptions
|
1213
|
+
|
1214
|
+
* <a name="exception-flow-control"></a>Don't use exceptions for flow of control.
|
1215
|
+
<sup>[[link](#exception-flow-control)]</sup>
|
1216
|
+
|
1217
|
+
```ruby
|
1218
|
+
# bad
|
1219
|
+
begin
|
1220
|
+
n / d
|
1221
|
+
rescue ZeroDivisionError
|
1222
|
+
puts "Cannot divide by 0!"
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
# good
|
1226
|
+
if d.zero?
|
1227
|
+
puts "Cannot divide by 0!"
|
1228
|
+
else
|
1229
|
+
n / d
|
1230
|
+
end
|
1231
|
+
```
|
1232
|
+
|
1233
|
+
* <a name="dont-rescue-exception"></a>Avoid rescuing the `Exception` class.
|
1234
|
+
<sup>[[link](#dont-rescue-exception)]</sup>
|
1235
|
+
|
1236
|
+
```ruby
|
1237
|
+
# bad
|
1238
|
+
begin
|
1239
|
+
# an exception occurs here
|
1240
|
+
rescue Exception
|
1241
|
+
# exception handling
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
# good
|
1245
|
+
begin
|
1246
|
+
# an exception occurs here
|
1247
|
+
rescue StandardError
|
1248
|
+
# exception handling
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
# acceptable
|
1252
|
+
begin
|
1253
|
+
# an exception occurs here
|
1254
|
+
rescue
|
1255
|
+
# exception handling
|
1256
|
+
end
|
1257
|
+
```
|
1258
|
+
|
1259
|
+
* <a name="redundant-exception"></a>Don't specify `RuntimeError` explicitly in
|
1260
|
+
the two argument version of raise. Prefer error sub-classes for clarity and
|
1261
|
+
explicit error creation.<sup>[[link](#redundant-exception)]</sup>
|
1262
|
+
|
1263
|
+
```ruby
|
1264
|
+
# bad
|
1265
|
+
raise RuntimeError, "message"
|
1266
|
+
|
1267
|
+
# better - RuntimeError is implicit here
|
1268
|
+
raise "message"
|
1269
|
+
|
1270
|
+
# best
|
1271
|
+
class MyExplicitError < RuntimeError; end
|
1272
|
+
raise MyExplicitError
|
1273
|
+
```
|
1274
|
+
|
1275
|
+
|
1276
|
+
* <a name="exception-class-messages"></a>
|
1277
|
+
Prefer supplying an exception class and a message as two separate arguments
|
1278
|
+
to `raise`, instead of an exception instance.
|
1279
|
+
<sup>[[link](#exception-class-messages)]</sup>
|
1280
|
+
|
1281
|
+
```Ruby
|
1282
|
+
# bad
|
1283
|
+
raise SomeException.new("message")
|
1284
|
+
# Note that there is no way to do `raise SomeException.new("message"), backtrace`.
|
1285
|
+
|
1286
|
+
# good
|
1287
|
+
raise SomeException, "message"
|
1288
|
+
# Consistent with `raise SomeException, "message", backtrace`.
|
1289
|
+
```
|
1290
|
+
|
1291
|
+
|
1292
|
+
* <a name="rescue-as-modifier"></a>Avoid using rescue in its modifier form.
|
1293
|
+
<sup>[[link](#rescue-as-modifier)]</sup>
|
1294
|
+
|
1295
|
+
```ruby
|
1296
|
+
# bad
|
1297
|
+
read_file rescue handle_error($!)
|
1298
|
+
|
1299
|
+
# good
|
1300
|
+
begin
|
1301
|
+
read_file
|
1302
|
+
rescue Errno:ENOENT => ex
|
1303
|
+
handle_error(ex)
|
1304
|
+
end
|
1305
|
+
```
|
1306
|
+
|
1307
|
+
## Collections
|
1308
|
+
|
1309
|
+
* <a name="map-over-collect"></a>Prefer `map` over
|
1310
|
+
`collect`.<sup>[[link](#map-over-collect)]</sup>
|
1311
|
+
|
1312
|
+
* <a name="detect-over-find"></a>Prefer `detect` over `find`. The use of `find`
|
1313
|
+
is ambiguous with regard to ActiveRecord's `find` method - `detect` makes
|
1314
|
+
clear that you're working with a Ruby collection, not an AR object.
|
1315
|
+
<sup>[[link](#detect-over-find)]</sup>
|
1316
|
+
|
1317
|
+
* <a name="reduce-over-inject"></a>Prefer `reduce` over `inject`.
|
1318
|
+
<sup>[[link](#reduce-over-inject)]</sup>
|
1319
|
+
|
1320
|
+
* <a name="size-over-count"></a>Prefer `size` over either `length` or `count`
|
1321
|
+
for performance reasons.<sup>[[link](#size-over-count)]</sup>
|
1322
|
+
|
1323
|
+
* <a name="empty-collection-literals"></a>Prefer literal array and hash creation
|
1324
|
+
notation unless you need to pass parameters to their constructors.
|
1325
|
+
<sup>[[link](#empty-collection-literals)]</sup>
|
1326
|
+
|
1327
|
+
```ruby
|
1328
|
+
# bad
|
1329
|
+
arr = Array.new
|
1330
|
+
hash = Hash.new
|
1331
|
+
|
1332
|
+
# good
|
1333
|
+
arr = []
|
1334
|
+
hash = {}
|
1335
|
+
|
1336
|
+
# good because constructor requires parameters
|
1337
|
+
x = Hash.new { |h, k| h[k] = {} }
|
1338
|
+
```
|
1339
|
+
|
1340
|
+
* <a name="array-join"></a>Favor `Array#join` over `Array#*` for clarity.
|
1341
|
+
<sup>[[link](#array-join)]</sup>
|
1342
|
+
|
1343
|
+
```ruby
|
1344
|
+
# bad
|
1345
|
+
%w(one two three) * ", "
|
1346
|
+
# => "one, two, three"
|
1347
|
+
|
1348
|
+
# good
|
1349
|
+
%w(one two three).join(", ")
|
1350
|
+
# => "one, two, three"
|
1351
|
+
```
|
1352
|
+
|
1353
|
+
* <a name="symbol-keys"></a>Use symbols instead of strings as hash keys.
|
1354
|
+
<sup>[[link](#symbol-keys)]</sup>
|
1355
|
+
|
1356
|
+
```ruby
|
1357
|
+
# bad
|
1358
|
+
hash = { "one" => 1, "two" => 2, "three" => 3 }
|
1359
|
+
|
1360
|
+
# good
|
1361
|
+
hash = { :one => 1, :two => 2, :three => 3 }
|
1362
|
+
```
|
1363
|
+
|
1364
|
+
* <a name="symbol-literals"></a>Relatedly, use plain symbols instead of string
|
1365
|
+
symbols when possible.<sup>[[link](#symbol-literals)]</sup>
|
1366
|
+
|
1367
|
+
```ruby
|
1368
|
+
# bad
|
1369
|
+
:"symbol"
|
1370
|
+
|
1371
|
+
# good
|
1372
|
+
:symbol
|
1373
|
+
```
|
1374
|
+
|
1375
|
+
* <a name="deprecated-hash-methods"></a>Use `Hash#key?` instead of
|
1376
|
+
`Hash#has_key?` and `Hash#value?` instead of `Hash#has_value?`. According
|
1377
|
+
to Matz, the longer forms are considered deprecated.
|
1378
|
+
<sup>[[link](#deprecated-hash-methods)]</sup>
|
1379
|
+
|
1380
|
+
```ruby
|
1381
|
+
# bad
|
1382
|
+
hash.has_key?(:test)
|
1383
|
+
hash.has_value?(value)
|
1384
|
+
|
1385
|
+
# good
|
1386
|
+
hash.key?(:test)
|
1387
|
+
hash.value?(value)
|
1388
|
+
```
|
1389
|
+
|
1390
|
+
* <a name="multiline-hashes"></a>Use multi-line hashes when it makes the code
|
1391
|
+
more readable, and use trailing commas to ensure that parameter changes
|
1392
|
+
don't cause extraneous diff lines when the logic has not otherwise changed.
|
1393
|
+
<sup>[[link](#multiline-hashes)]</sup>
|
1394
|
+
|
1395
|
+
```ruby
|
1396
|
+
hash = {
|
1397
|
+
:protocol => "https",
|
1398
|
+
:only_path => false,
|
1399
|
+
:controller => :users,
|
1400
|
+
:action => :set_password,
|
1401
|
+
:redirect => @redirect_url,
|
1402
|
+
:secret => @secret,
|
1403
|
+
}
|
1404
|
+
```
|
1405
|
+
|
1406
|
+
* <a name="array-trailing-comma"></a>Use a trailing comma in an `Array` that
|
1407
|
+
spans more than 1 line<sup>[[link](#array-trailing-comma)]</sup>
|
1408
|
+
|
1409
|
+
```ruby
|
1410
|
+
# good
|
1411
|
+
array = [1, 2, 3]
|
1412
|
+
|
1413
|
+
# good
|
1414
|
+
array = [
|
1415
|
+
"car",
|
1416
|
+
"bear",
|
1417
|
+
"plane",
|
1418
|
+
"zoo",
|
1419
|
+
]
|
1420
|
+
```
|
1421
|
+
|
1422
|
+
## Strings
|
1423
|
+
|
1424
|
+
* <a name="string-interpolation"></a>Prefer string interpolation instead of
|
1425
|
+
string concatenation:<sup>[[link](#string-interpolation)]</sup>
|
1426
|
+
|
1427
|
+
```ruby
|
1428
|
+
# bad
|
1429
|
+
email_with_name = user.name + " <" + user.email + ">"
|
1430
|
+
|
1431
|
+
# good
|
1432
|
+
email_with_name = "#{user.name} <#{user.email}>"
|
1433
|
+
```
|
1434
|
+
|
1435
|
+
Furthermore, keep in mind Ruby 1.9-style interpolation. Let's say you are
|
1436
|
+
composing cache keys like this:
|
1437
|
+
|
1438
|
+
```ruby
|
1439
|
+
CACHE_KEY = "_store"
|
1440
|
+
|
1441
|
+
cache.write(@user.id + CACHE_KEY)
|
1442
|
+
```
|
1443
|
+
|
1444
|
+
Prefer string interpolation instead of string concatenation:
|
1445
|
+
|
1446
|
+
```ruby
|
1447
|
+
CACHE_KEY = "%d_store"
|
1448
|
+
|
1449
|
+
cache.write(CACHE_KEY % @user.id)
|
1450
|
+
```
|
1451
|
+
|
1452
|
+
* <a name="string-concatenation"></a>Avoid using `String#+` when you need to
|
1453
|
+
construct large data chunks. Instead, use `String#<<`. Concatenation mutates
|
1454
|
+
the string instance in-place and is always faster than `String#+`, which
|
1455
|
+
creates a bunch of new string objects.<sup>[[link](#string-concatenation)]</sup>
|
1456
|
+
|
1457
|
+
```ruby
|
1458
|
+
# good and also fast
|
1459
|
+
story = ""
|
1460
|
+
story << "The Ugly Duckling"
|
1461
|
+
|
1462
|
+
paragraphs.each do |paragraph|
|
1463
|
+
story << paragraph
|
1464
|
+
end
|
1465
|
+
```
|
1466
|
+
|
1467
|
+
* <a name="multi-line-strings"></a>Use `\` at the end of the line instead of `+`
|
1468
|
+
or `<<` to concatenate multi-line strings.
|
1469
|
+
<sup>[[link](#multi-line-strings)]</sup>
|
1470
|
+
|
1471
|
+
```ruby
|
1472
|
+
# bad
|
1473
|
+
"Some string is really long and " +
|
1474
|
+
"spans multiple lines."
|
1475
|
+
|
1476
|
+
"Some string is really long and " <<
|
1477
|
+
"spans multiple lines."
|
1478
|
+
|
1479
|
+
# good
|
1480
|
+
"Some string is really long and " \
|
1481
|
+
"spans multiple lines."
|
1482
|
+
```
|
1483
|
+
|
1484
|
+
## Regular Expressions
|
1485
|
+
|
1486
|
+
* <a name="regex-named-groups"></a>Avoid using `$1-9` as it can be hard to track
|
1487
|
+
what they contain. Named groups can be used instead.
|
1488
|
+
<sup>[[link](#regex-named-groups)]</sup>
|
1489
|
+
|
1490
|
+
```ruby
|
1491
|
+
# bad
|
1492
|
+
/(regexp)/ =~ string
|
1493
|
+
...
|
1494
|
+
process $1
|
1495
|
+
|
1496
|
+
# good
|
1497
|
+
/(?<meaningful_var>regexp)/ =~ string
|
1498
|
+
...
|
1499
|
+
process meaningful_var
|
1500
|
+
```
|
1501
|
+
|
1502
|
+
* <a name="caret-and-dollar-regexp"></a>Be careful with `^` and `$` as they
|
1503
|
+
match start/end of line, not string endings. If you want to match the whole
|
1504
|
+
string use: `\A` and `\z`.<sup>[[link](#caret-and-dollar-regexp)]</sup>
|
1505
|
+
|
1506
|
+
```ruby
|
1507
|
+
string = "some injection\nusername"
|
1508
|
+
string[/^username$/] # matches
|
1509
|
+
string[/\Ausername\z/] # don't match
|
1510
|
+
```
|
1511
|
+
|
1512
|
+
* <a name="comment-regexes"></a>Use `x` modifier for complex regexps. This makes
|
1513
|
+
them more readable and you can add some useful comments. Just be careful as
|
1514
|
+
spaces are ignored.<sup>[[link](#comment-regexes)]</sup>
|
1515
|
+
|
1516
|
+
```ruby
|
1517
|
+
regexp = %r{
|
1518
|
+
start # some text
|
1519
|
+
\s # white space char
|
1520
|
+
(group) # first group
|
1521
|
+
(?:alt1|alt2) # some alternation
|
1522
|
+
end
|
1523
|
+
}x
|
1524
|
+
```
|
1525
|
+
|
1526
|
+
## Percent Literals
|
1527
|
+
|
1528
|
+
* <a name="percent-literal-delimiters"></a>Prefer parentheses over curly
|
1529
|
+
braces, brackets, or pipes when using `%`-literal delimiters for
|
1530
|
+
consistency, and because the behavior of `%`-literals is closer to method
|
1531
|
+
calls than the alternatives.<sup>[[link](#percent-literal-delimiters)]</sup>
|
1532
|
+
|
1533
|
+
```ruby
|
1534
|
+
# bad
|
1535
|
+
%w[date locale]
|
1536
|
+
%w{date locale}
|
1537
|
+
%w|date locale|
|
1538
|
+
|
1539
|
+
# good
|
1540
|
+
%w(date locale)
|
1541
|
+
```
|
1542
|
+
|
1543
|
+
* <a name="percent-w"></a>Use `%w` freely.<sup>[[link](#percent-w)]</sup>
|
1544
|
+
|
1545
|
+
```ruby
|
1546
|
+
STATES = %w(draft open closed)
|
1547
|
+
```
|
1548
|
+
|
1549
|
+
* <a name="percent-parens"></a>Use `%()` for single-line strings which require
|
1550
|
+
both interpolation and embedded double-quotes. For multi-line strings,
|
1551
|
+
prefer heredocs.<sup>[[link](#percent-parens)]</sup>
|
1552
|
+
|
1553
|
+
```ruby
|
1554
|
+
# bad - no interpolation needed
|
1555
|
+
%(Welcome, Jane!)
|
1556
|
+
# should be "Welcome, Jane!"
|
1557
|
+
|
1558
|
+
# bad - no double-quotes
|
1559
|
+
%(This is #{quality} style)
|
1560
|
+
# should be "This is #{quality} style"
|
1561
|
+
|
1562
|
+
# bad - multiple lines
|
1563
|
+
%(Welcome, Jane!\nPlease enjoy your stay at #{location}\nCheers!)
|
1564
|
+
# should be a heredoc.
|
1565
|
+
|
1566
|
+
# good - requires interpolation, has quotes, single line
|
1567
|
+
%(Welcome, #{name}!)
|
1568
|
+
```
|
1569
|
+
|
1570
|
+
* <a name="percent-r"></a>Use `%r` only for regular expressions matching *more
|
1571
|
+
than* one "/" character.<sup>[[link](#percent-r)]</sup>
|
1572
|
+
|
1573
|
+
```ruby
|
1574
|
+
# bad
|
1575
|
+
%r(\s+)
|
1576
|
+
|
1577
|
+
# still bad
|
1578
|
+
%r(^/(.*)$)
|
1579
|
+
# should be /^\/(.*)$/
|
1580
|
+
|
1581
|
+
# good
|
1582
|
+
%r(^/blog/2011/(.*)$)
|
1583
|
+
```
|
1584
|
+
|
1585
|
+
* <a name="percent-x"></a>Avoid the use of %x unless you're going to invoke a
|
1586
|
+
command with backquotes in it (which is rather unlikely).
|
1587
|
+
<sup>[[link](#percent-x)]</sup>
|
1588
|
+
|
1589
|
+
```ruby
|
1590
|
+
# bad
|
1591
|
+
date = %x(date)
|
1592
|
+
|
1593
|
+
# good
|
1594
|
+
date = `date`
|
1595
|
+
echo = %x(echo `date`)
|
1596
|
+
```
|
1597
|
+
|
1598
|
+
## Rails
|
1599
|
+
|
1600
|
+
* <a name="next-line-return"></a>When immediately returning after calling
|
1601
|
+
`render` or `redirect_to`, put `return` on the next line, not the same line.
|
1602
|
+
<sup>[[link](#next-line-return)]</sup>
|
1603
|
+
|
1604
|
+
```ruby
|
1605
|
+
# bad
|
1606
|
+
render :text => "Howdy" and return
|
1607
|
+
|
1608
|
+
# good
|
1609
|
+
render :text => "Howdy"
|
1610
|
+
return
|
1611
|
+
|
1612
|
+
# still bad
|
1613
|
+
render :text => "Howdy" and return if foo.present?
|
1614
|
+
|
1615
|
+
# good
|
1616
|
+
if foo.present?
|
1617
|
+
render :text => "Howdy"
|
1618
|
+
return
|
1619
|
+
end
|
1620
|
+
```
|
1621
|
+
|
1622
|
+
### Scopes
|
1623
|
+
* <a name="scope-lambda"></a>When defining ActiveRecord model scopes, wrap the
|
1624
|
+
relation in a `lambda`. A naked relation forces a database connection to be
|
1625
|
+
established at class load time (instance startup).
|
1626
|
+
<sup>[[link](#scope-lambda)]</sup>
|
1627
|
+
|
1628
|
+
```ruby
|
1629
|
+
# bad
|
1630
|
+
scope :foo, where(:bar => 1)
|
1631
|
+
|
1632
|
+
# good
|
1633
|
+
scope :foo, -> { where(:bar => 1) }
|
1634
|
+
```
|
1635
|
+
|
1636
|
+
## Be Consistent
|
1637
|
+
|
1638
|
+
> If you're editing code, take a few minutes to look at the code around you and
|
1639
|
+
> determine its style. If they use spaces around all their arithmetic
|
1640
|
+
> operators, you should too. If their comments have little boxes of hash marks
|
1641
|
+
> around them, make your comments have little boxes of hash marks around them
|
1642
|
+
> too.
|
1643
|
+
|
1644
|
+
> The point of having style guidelines is to have a common vocabulary of coding
|
1645
|
+
> so people can concentrate on what you're saying rather than on how you're
|
1646
|
+
> saying it. We present global style rules here so people know the vocabulary,
|
1647
|
+
> but local style is also important. If code you add to a file looks
|
1648
|
+
> drastically different from the existing code around it, it throws readers out
|
1649
|
+
> of their rhythm when they go to read it. Avoid this.
|
1650
|
+
|
1651
|
+
— [Google C++ Style Guide][google-c++]
|
1652
|
+
|
1653
|
+
[rubocop-guide]: https://github.com/rubocop-hq/ruby-style-guide
|
1654
|
+
[github-ruby]: https://github.com/styleguide/ruby
|
1655
|
+
[google-c++]: https://google.github.io/styleguide/cppguide.html
|
1656
|
+
[google-c++-comments]: https://google.github.io/styleguide/cppguide.html#Comments
|
1657
|
+
[google-python-comments]: https://google.github.io/styleguide/pyguide.html#Comments
|
1658
|
+
[ruby-naming-bang]: http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist
|
1659
|
+
[ruby-freeze]: https://blog.honeybadger.io/when-to-use-freeze-and-frozen-in-ruby/
|
1660
|
+
[avoid-else-return-early]: http://blog.timoxley.com/post/47041269194/avoid-else-return-early
|