元编程
避免无谓的元编程。
[link]
当编写程序库时,不要使核心类混乱(不要使用 monkey patch)。
[link]
对于class_eval
方法,倾向使用区块形式,而不是字符串插值形式。
[link]当使用字符串插值形式时,总是提供
__FILE__
及__LINE__
,以使你的调用栈看起来具有意义:class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__
倾向使用
define_method
而不是class_eval { def ... }
当使用class_eval
(或其他的eval
)的字符串插值形式时,添加一个注释区块来说明它是如何工作的(来自 Rails 代码中的技巧)。
[link]# 摘录自 activesupport/lib/active_support/core_ext/string/output_safety.rb
UNSAFE_STRING_METHODS.each do |unsafe_method|
if 'String'.respond_to?(unsafe_method)
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{unsafe_method}(*params, &block) # def capitalize(*params, &block)
to_str.#{unsafe_method}(*params, &block) # to_str.capitalize(*params, &block)
end # end
def #{unsafe_method}!(*params) # def capitalize!(*params)
@dirty = true # @dirty = true
super # super
end # end
EOT
end
end
避免使用method_missing
。它会使你的调用栈变得凌乱;其方法不被罗列在#methods
中;拼错的方法可能会默默地工作(nukes.launch_state = false
)。优先考虑使用委托、代理、或是define_method
来替代。如果你必须使用method_missing
的话,务必做到以下几点:
[link]- 确保同时定义了
respond_to_missing?
。 - 仅仅捕获那些具有良好语义前缀的方法,像是
find_by_*
——让你的代码愈确定愈好。 - 在语句的最后调用
super
。 委托到确定的、非魔术的方法,比如:
# 差
def method_missing?(meth, *params, &block)
if /^find_by_(?<prop>.*)/ =~ meth
# ... 一堆处理 find_by 的代码
else
super
end
end
# 好
def method_missing?(meth, *params, &block)
if /^find_by_(?<prop>.*)/ =~ meth
find_by(prop, *params, &block)
else
super
end
end
# 最好的方式可能是在每个需要支持的属性被声明时,使用 define_method 定义对应的方法
- 确保同时定义了
倾向使用public_send
而不是send
,因为send
会无视private/protected
的可见性。
[link]module Activatable
extend ActiveSupport::Concern
included do
before_create :create_token
end
private
def reset_token
...
end
def create_token
...
end
def activate!
...
end
end
class Organization < ActiveRecord::Base
include Activatable
end
linux_organization = Organization.find(...)
# 差 - 会破坏对象的封装性
linux_organization.send(:reset_token)
# 好 - 会抛出异常
linux_organization.public_send(:reset_token)
倾向使用__send__
而不是send
,因为send
可能会被覆写。
[link]require 'socket'
u1 = UDPSocket.new
u1.bind('127.0.0.1', 4913)
u2 = UDPSocket.new
u2.connect('127.0.0.1', 4913)
# 这里不会调用 u2 的 sleep 方法,而是通过 UDP socket 发送一条消息
u2.send :sleep, 0
# 动态调用 u2 的某个方法
u2.__send__ ...