- 除nil和false外所有值都是真值,0也是true;用nil?区分false和nil。
- 常量是可变的,需总是将常量、数组常量、模块定义的常量freeze冻结。
module Defaults
NETWORKS = ["192.168.1", "192.168.2"].freeze
end
def host_addresses(host, networks=Defaults::NETWORKS)
networks.map {|net| net << ".#{host}"}
end
p host_addresses(1)
["192.168.1.1", "192.168.2.1"]
p Defaults::NETWORKS
["192.168.1.1", "192.168.2.1"]
["192.168.1", "192.168.2"].map!(&:freeze).freeze
setter方法在调用时需要显式的接收者,没有接收者时,会被解析为变量赋值。
class Counter
attr_accessor(:counter)
def initialize
counter = 0
end
end
- 实例方法中调用
setter 时,需要使用self作为接收者。
- 在调用非
setter 方法时,不要总是使用self,会弄乱代码。
class Counter
attr_accessor(:counter)
def initialize(counter)
self.counter = counter
end
def full
self.counter + "hello"
end
- 使用Struct而非Hash存储结构化数据。
class Weather
Temperature = [
{
date: '2018-11-1',
high: '30',
low: '20'
},
{
date: '2018-11-2',
high: '31',
low: '21'
},
{
date: '2018-11-3',
high: '32',
low: '22'
}
]
Reading = Struct.new(:date, :high, :low)
def initialize
@readings = []
Temperature.each do |temp|
@readings << Reading.new(temp[:date], temp[:high], temp[:low])
end
end
def diff
sum = 0
@readings.each do |reading|
sum += reading.high.to_f - reading.low.to_f
end
sum
end
end
- 通过在模块中嵌入代码来创建命名空间,让命名空间结构和目录结构相同。
module Notebooks
class Binding
...
end
end
- 如果使用时可能出现歧义,使用“::”来限定顶级常量。
module Cluster
class Array
def initialize(n)
@disk = Array.new(n){|i| "disk#{i}"}
@disk = ::Array.new(n){|i| "disk#{i}"}
end
end
end
- 使用
Array方法将nil及标量对象转换成数组,不要传递Hash。
[7] pry(main)> Array(nil)
=> []
[12] pry(main)> Array(['a','b','c'])
=> ["a", "b", "c"]
[13] pry(main)> Array({a: 1, b: 2})
=> [[:a, 1], [:b, 2]]
- 通过
protected方法共享私有方法。
reduce 总是要给累计器一个初值。
def sum(enum)
enum.reduce(0) do |accumulator, element|
accumulator + element
end
end
def sum(enum)
enum.reduce(0, :+)
enum.reduce(&:+)
end
users.reduce([]) do |names, user|
names << user.name if user.age >= 21
names
end
array.reduce({}) do |hash, element|
hash.update(element => true)
end
- 考虑使用默认哈希值,推荐使用
fetch 更安全,第一个参数为key,第二个参数为找不到key时的返回值。
h = {}
h[:weekdays] = h.fetch(:weekdays, []) << "Monday"
- 使用定制的异常而不是抛出字符串。
- 新的异常类必须继承标准异常类。任何其他的尝试都会导致一个TypeError。
- 标准异常类形成了一套以Exception为基类的继承体系。但它以及它的许多子类被认为是低级别错误。多数标准异常应继承自StandardError。
- 通常异常类名为
Error 作为后缀。
class TemperatureError < StandardError
attr_reader(:temperature)
def initialize(temperature)
@temperature = temperature
super("invalid temperature: #{@temperature}")
end
end
raise(TemperatureError.new(180))
- 捕获可能的最具体的异常。只捕获那些你知道如何恢复的异常。首先处理最特殊的类型,在异常的继承关系中,位置越高的,越应该排在
rescue 后面。
begin
task.perform
rescue NetworkConnectionError => e
rescue InvalidRecordError => e
rescue => e
service.record(e)
raise
ensure
...
end
- 永远不要无条件retry,要把它看作代码中的隐式循环,在代码块外围定义重试次数。
retries = 0
begin
service.update(record)
rescue VendorDeadLockError => e
raise if retries >= 3
retries += 1
logger.warn("API failure: #{e}, retrying...")
sleep(5 * retries)
retry
end
- 性能分析工具Gem stackprof 和 memory_profiler
- 将循环中不会变化的对象字面量变成常量。
errors.any? {|e| e.code == "FATAL".freeze}