I. Avoid Allocating a Lot of Objects
Example:
DON’T
1_000_000.times { "some string or object" }
SHOULD BE
my_invariant = "some string or object"
1_000_000.times { my_invariant }
OR
1_000_000.times { "some string or object".freeze }
II. Do Not Use Exceptions for Control Flow
DON’T
def with_rescue
self.mythical_method
rescue NoMethodError
nil
end
SHOULD BE
def with_condition
respond_to?(:mythical_method) ? self.mythical_method : nil
end
III. Be Careful with Calculations within Iterators
DON’T
def func(array)
array.inject({}) { |h, e| h.merge(e => e) }
end
SHOULD BE
def func(array)
array.inject({}) { |h, e| h[e] = e; h }
end
IV. Avoid Object Creation if Possible
1. String Concatenation
Avoid using +=
to concatenate strings in favor of the <<
method
str1 = "first"
str2 = "second"
str1.object_id # => 16241320
DON’T
str1 += str2 # str1 = str1 + str2
str1.object_id # => 16241240, id is changed
SHOULD BE
str1 << str2
str1.object_id # => 16241240, id is the same
When you use +=
, Ruby creates a temporary object which is the result of str1 + str2
. Then it overrides the str1
variable with a reference to the newly built object. On the other hand, <<
modifies the existing one.
As a result of using +=
, you have the following disadvantages:
- More calculations to join strings.
- A redundant string object in memory (previous value of
str1
), which approximates the time when GC will trigger.
2. Use Bang! Methods
In many cases, bang methods do the same as their non-bang analogs but without duplicating an object.
array = ['a', 'b', 'c']
array.map(&:upcase)
array.map!(&:upcase)
Some methods are essentially an alias for bang methods
2.1 select vs. keep_if
a = %w{ a b c d e f }
a.keep_if { |v| v =~ /[aeiou]/ }
puts a
=> ["a", "e"]
a = %w{ a b c d e f }
a.select { |v| v =~ /[aeiou]/ }
puts a
=> ["a", "b", "c", "d", "e", "f"]
2.2 reject vs. delete_if
a = [1,2,3,4,5]
a.delete_if(&:even?)
puts a
=> [1,3,5]
3. Parallel Assignment is Slower
DON’T
a, b = 10, 20
SHOULD BE
a = 10
b = 20
V. Use Methods That Are Optimized by Ruby
DON’T | SHOULD BE |
---|---|
Enumerable#reverse.each | Enumerable#reverse_each |
Enumerable#select.first | Enumerable#detect |
VI. Be Careful When Using eval
and send
When you use them, please make sure you can handle what will be entered.
DON’T
type = params[:type] # type = "system('rm -rf ./')"
eval(type)
type = params[:type] # type = delete_all
User.send(type)
SHOULD BE
type = params[:type]
User.send(type) if ['abc', 'zyz'].include?(type)
Public comments are closed, but I love hearing from readers. Feel free to contact me with your thoughts.