Sinatra::Base.set源码
用于定义类方法:b,b=,b?
三种用法:
set :b, 'a symbol named b'
set :b {'a symbol named b'}
set b: 'a symbol named b', c: 'a symbol named c'
def set(option, value = (not_set = true), ignore_setter = false, &block)
# 如果有block,又有value,则报错
raise ArgumentError if block and !not_set
# 如有block,则value赋值为block
value, not_set = block, false if block
# 如无block也无value,则option需为hash
if not_set
raise ArgumentError unless option.respond_to?(:each)
option.each { |k,v| set(k, v) }
return self
end
# 如已有对应setter,且真的想用该setter(无设ignore_setter的话),则调用它
if respond_to?("#{option}=") and not ignore_setter
return __send__("#{option}=", value)
end
# setter还是调用set,但传ignore_setter = true,以作赋值而非重定义setter
setter = proc { |val| set option, val, true }
getter = proc { value }
case value
when Proc
getter = value
# define_singleton会将字符串包装成def...来eval
when Symbol, Fixnum, FalseClass, TrueClass, NilClass
getter = value.inspect
# 若value是Hash,则以后每次赋值都是merge
when Hash
setter = proc do |val|
val = value.merge val if Hash === val
set option, val, true
end
end
# if setter/getter似乎没有意义,因它们肯定存在
define_singleton("#{option}=", setter) if setter
define_singleton(option, getter) if getter
define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
self
end
def define_singleton(name, content = Proc.new)
singleton_class.class_eval do
undef_method(name) if method_defined? name
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
end
end