diff --git a/README.adoc b/README.adoc index 31f31578..408e20db 100644 --- a/README.adoc +++ b/README.adoc @@ -1421,12 +1421,58 @@ result = Use `when x then ...` for one-line cases. +[source,ruby] +---- +# bad +case year +when 1850..1889 + 'Blues' +when 1890..1909 + 'Ragtime' +when 1910..1929 + 'New Orleans Jazz' +when 1930..1939 + 'Swing' +when 1940..1950 + 'Bebop' +else + 'Jazz' +end + +# good +case year +when 1850..1889 then 'Blues' +when 1890..1909 then 'Ragtime' +when 1910..1929 then 'New Orleans Jazz' +when 1930..1939 then 'Swing' +when 1940..1950 then 'Bebop' +else 'Jazz' +end +---- + NOTE: The alternative syntax `when x: ...` has been removed as of Ruby 1.9. === Semicolon in `when` [[no-when-semicolons]] Do not use `when x; ...`. See the previous rule. +[source,ruby] +---- +# bad +case year +when 1850..1889; 'Blues' +when 1890..1909; 'Ragtime' +else; 'Jazz' +end + +# good +case year +when 1850..1889 then 'Blues' +when 1890..1909 then 'Ragtime' +else 'Jazz' +end +---- + === Semicolon in `in` [[no-in-pattern-semicolons]] Do not use `in pattern; ...`. Use `in pattern then ...` for one-line `in` pattern branches. @@ -1581,6 +1627,22 @@ false || true && false # => false (it's effectively false || (true && false)) Avoid multi-line `?:` (the ternary operator); use `if`/`unless` instead. +[source,ruby] +---- +# bad +result = some_condition ? + something : + something_else + +# good +result = + if some_condition + something + else + something_else + end +---- + === `if` as a Modifier [[if-as-a-modifier]] Prefer modifier `if`/`unless` usage when you have a single-line body. @@ -1892,6 +1954,15 @@ end Avoid the use of `BEGIN` blocks. +[source,ruby] +---- +# bad +BEGIN { puts 'Initializing...' } + +# good +puts 'Initializing...' +---- + === `END` Blocks [[no-END-blocks]] Do not use `END` blocks. Use `Kernel#at_exit` instead. @@ -3129,6 +3200,18 @@ IMPORTANT: When calling `super` without arguments, `super` and `super()` mean di Avoid parameter lists longer than three or four parameters. +[source,ruby] +---- +# bad +def create_user(first_name, last_name, email, phone, address, age) + # ... +end + +# good +def create_user(first_name, last_name, email:, phone: nil, address: nil, age: nil) + # ... +end +---- === Optional Arguments [[optional-arguments]] @@ -3300,6 +3383,23 @@ end If you really need "global" methods, add them to Kernel and make them private. +[source,ruby] +---- +# bad +def global_helper + # ... +end + +# good +module Kernel + private + + def global_helper + # ... + end +end +---- + == Classes & Modules === Consistent Classes [[consistent-classes]] @@ -4018,6 +4118,15 @@ Write comments in English. Use one space between the leading `#` character of the comment and the text of the comment. +[source,ruby] +---- +# bad +#some comment + +# good +# some comment +---- + === English Syntax [[english-syntax]] Comments longer than a word are capitalized and use punctuation. @@ -4338,6 +4447,18 @@ hash = { one: 1, two: 2, three: 3 } Avoid the use of mutable objects as hash keys. +[source,ruby] +---- +# bad +hash = { 'name' => 'John' } + +# good +hash = { name: 'John' } + +# good - frozen string key when symbols won't do +hash = { 'name'.freeze => 'John' } +---- + === No Mutable Defaults [[no-mutable-defaults]] Avoid the use of shared mutable objects as hash default values. @@ -4571,6 +4692,17 @@ Rely on the fact that as of Ruby 1.9 hashes are ordered. Do not modify a collection while traversing it. +[source,ruby] +---- +# bad +numbers = [1, 2, 3, 4, 5] +numbers.each { |n| numbers.delete(n) if n.even? } + +# good +numbers = [1, 2, 3, 4, 5] +odd_numbers = numbers.reject(&:even?) +---- + === Accessing Elements Directly [[accessing-elements-directly]] When accessing elements of a collection, avoid direct access via `[n]` by using an alternate form of the reader method if it is supplied. @@ -4613,6 +4745,25 @@ This is not a hard requirement; if the use of the alias enhances readability, it The rhyming methods are inherited from Smalltalk and are not common in other programming languages. The reason the use of `select` is encouraged over `find_all` is that it goes together nicely with `reject` and its name is pretty self-explanatory. +[source,ruby] +---- +# bad +items.collect { |item| item.name } +items.detect(&:active?) +items.find_all(&:valid?) +items.inject(0) { |sum, i| sum + i } +items.member?(value) +items.length + +# good +items.map { |item| item.name } +items.find(&:active?) +items.select(&:valid?) +items.reduce(0) { |sum, i| sum + i } +items.include?(value) +items.size +---- + === `count` vs `size` [[count-vs-size]] Don't use `count` as a substitute for `size`. @@ -5270,6 +5421,15 @@ SQL Prefer `Time.now` over `Time.new` when retrieving the current system time. +[source,ruby] +---- +# bad +Time.new + +# good +Time.now +---- + === No `DateTime` [[no-datetime]] Don't use `DateTime` unless you need to account for historical calendar reform - and if you do, explicitly specify the `start` argument to clearly state your intentions. @@ -5564,6 +5724,15 @@ echo = %x(echo `date`) Avoid the use of `%s`. It seems that the community has decided `:"some string"` is the preferred way to create a symbol with spaces in it. +[source,ruby] +---- +# bad +%s(some symbol) + +# good +:"some symbol" +---- + === Percent Literal Braces [[percent-literal-braces]] Use the braces that are the most appropriate for the various kinds of percent literals. @@ -5604,10 +5773,44 @@ Use the braces that are the most appropriate for the various kinds of percent li Avoid needless metaprogramming. +[source,ruby] +---- +# bad +class Person + %i[name age email].each do |attr| + define_method(attr) { instance_variable_get("@#{attr}") } + end +end + +# good +class Person + attr_reader :name, :age, :email +end +---- + === No Monkey Patching [[no-monkey-patching]] Do not mess around in core classes when writing libraries (do not monkey-patch them). +[source,ruby] +---- +# bad +class String + def to_hierarchical_date + split('-').map(&:to_i) + end +end + +# good - use refinements instead +module DateParsing + refine String do + def to_hierarchical_date + split('-').map(&:to_i) + end + end +end +---- + === Block `class_eval` [[block-class-eval]] The block form of `class_eval` is preferable to the string-interpolated form. @@ -6056,6 +6259,21 @@ Write `ruby -w` safe code. Avoid hashes as optional parameters. Does the method do too much? (Object initializers are exceptions for this rule). +[source,ruby] +---- +# bad +def send_email(subject, opts = {}) + to = opts[:to] + cc = opts[:cc] + # ... +end + +# good +def send_email(subject, to:, cc: nil) + # ... +end +---- + === Instance Vars [[instance-vars]] Use module instance variables instead of global variables. @@ -6083,10 +6301,51 @@ Use `OptionParser` for parsing complex command line options and `ruby -s` for tr Do not mutate parameters unless that is the purpose of the method. +[source,ruby] +---- +# bad +def clean(users) + users.reject!(&:disabled?) + users +end + +# good +def clean(users) + users.reject(&:disabled?) +end +---- + === Three is the Number Thou Shalt Count [[three-is-the-number-thou-shalt-count]] Avoid more than three levels of block nesting. +[source,ruby] +---- +# bad +def process(items) + items.each do |item| + item.widgets.each do |widget| + widget.parts.each do |part| + part.update! + end + end + end +end + +# good +def process(items) + items.each do |item| + process_widgets(item.widgets) + end +end + +def process_widgets(widgets) + widgets.each do |widget| + widget.parts.each(&:update!) + end +end +---- + === Functional Code [[functional-code]] Code in a functional way, avoiding mutation when that makes sense.