的問題是,類定義只是達到定義時被執行的代碼。這就像問你是否可以訪問你尚未定義的局部變量的值。
可能的解決方法是延遲對常量的評估,直到執行類體。請注意,這不是很值得,它表明,不應該在實踐中使用一個有效的解決方案:
# BasicObject for minimal amount of methods
class Retarded < BasicObject
def initialize(&value)
@value = value
end
# Make sure we remove as many methods as we can to
# make the proxy more transparent.
(instance_methods - %i(__id__ __send__ __binding__)).each do |name|
undef_method name
end
def method_missing(*args, &block)
# Get the actual value
value = @value.call
# Suppress warnings while we traverse constants
warn_level, $VERBOSE = $VERBOSE, nil
traversed_modules = []
constant_finder = -> (root) do
constant_name = root.constants.find do |constant|
# We undefed ==, so we need a different way to compare.
# Given that this class should be used for constant values in place,
# comparing object ids does the job.
root.const_get(constant).__id__ == __id__
end
if constant_name
# Just set the constant to the actual value
root.const_set(constant_name, value)
else
# Recursively search for the containing module of the constant
root.constants.each do |child_name|
child_constant = root.const_get(child_name)
if child_constant.is_a?(::Module) &&
!traversed_modules.include?(child_constant)
# Handle circular references
traversed_modules.push child_constant
constant_finder.(child_constant)
end
end
end
end
# ::Object is the root module where constants are contained
constant_finder.(::Object)
# Bring back warnings
$VERBOSE = warn_level
# We have already found the constant and set its value to whatever was
# passed in the constructor. However, this method_missing was called
# in an attempt to call a method on the value. We now should make that
# invocation.
value.public_send(*args, &block)
end
end
# I know, the example is mononic.
class EdwardKing
DEAD = Retarded.new { [new('I'), new('II'), new('III')] }
def initialize(number)
@number = number
end
def whoami
"Edward #{@number} of England"
end
end
p EdwardKing::DEAD
# [#<EdwardKing:0x007f90fc26fd10 @number="I">, #<EdwardKing:0x007f90fc26fc70 @number="II">, #<EdwardKing:0x007f90fc26fc20 @number="III">
p EdwardKing::DEAD.map(&:whoami)
# ["Edward I of England", "Edward II of England", "Edward III of England"]
來源
2017-03-22 10:54:05
ndn
一個_can_做到這一點,但不應該。 :) –
@SergioTulentsev這是另一個問題;) –
是的,它絕對是:) –