ruby 模块如何对包含它的 类 执行条件?
How can a ruby module enforce conditions on classes in which it is included?
我如何编写一个 ruby 模块,对包含它的 classes 施加一些条件,这些条件必须满足当前打开的 class 定义的结尾?
具体来说,假设条件是"a class variable @@foo
should be defined to be >0 "
我想写一个看起来像这样的模块:
module NeedPositiveFoo
module ClassMethods
def validate_positive_foo
raise unless defined?(@@foo) && @@foo > 0
end
end
def included(other)
other.extend(ClassMethods)
end
end
那么这个 class 定义将是有效的:
class ValidClass
include NeedPositiveFoo
@@foo = 3
end
但是这些 class 定义会在 end
结束后出现:
class InvalidClass1
include NeedPositiveFoo
# @@foo is not defined
end
class InvalidClass2
include NeedPositiveFoo
@@foo = -2
end
你不能挂钩 class 定义的末尾,因为没有一个 - class 可以在不同的文件、不同的时间、甚至不同的库中声明.
您可以做的是检查条件 包含模块时 ,并在定义的末尾声明包含:
module NeedPositiveFoo
def included(other)
raise unless defined?(@@foo) && @@foo > 0
end
end
class ValidClass
@@foo = 3
include NeedPositiveFoo
end
class InvalidClass1
# @@foo is not defined
include NeedPositiveFoo
end
class InvalidClass2
@@foo = -2
include NeedPositiveFoo
end
class InvalidClass3
include NeedPositiveFoo
@@foo = 4 # declared after inclusion - not a valid state...
end
虽然 Uri Agassi 的答案在您被允许将 includes 放在 class 定义的最后时完美地工作,但下面的代码将工作,尽管放置了 include
。
def included(other)
TracePoint.new(:end) do |tp|
if tp.self == other
tp.disable
raise unless defined?(other.class_variable_get(:@@foo)) # checks
end
end.enable
other.extend(ClassMethods)
end
问这样的问题时,看看 Ruby 核心库是如何做的通常很有用。 Ruby 核心库中有两个 well-known mixins,它们在 classes 上设置了某些条件,它们被混合到:
Enumerable
要求 class 有一个 each
方法,可以用零参数调用并接受一个块,它 yield
所有元素collection 连续。
Comparable
要求 class 有一个 <=>
方法,该方法可以用单个参数调用并用 -1
、0
、或 1
,取决于参数是大于、等于还是小于接收者。
在这两种情况下,需求只是在文档中说明,而不是在代码中说明。由 class 作者来确保他们得到满足。
事实上,在 Enumerable
情况下,根本没有真正说明要求 ,只是假设任何有能力的 Ruby 程序员都知道他们。
我会遵循核心库作者设定的这种风格,因为这是 Ruby 支持者所习惯的。
我如何编写一个 ruby 模块,对包含它的 classes 施加一些条件,这些条件必须满足当前打开的 class 定义的结尾?
具体来说,假设条件是"a class variable @@foo
should be defined to be >0 "
我想写一个看起来像这样的模块:
module NeedPositiveFoo
module ClassMethods
def validate_positive_foo
raise unless defined?(@@foo) && @@foo > 0
end
end
def included(other)
other.extend(ClassMethods)
end
end
那么这个 class 定义将是有效的:
class ValidClass
include NeedPositiveFoo
@@foo = 3
end
但是这些 class 定义会在 end
结束后出现:
class InvalidClass1
include NeedPositiveFoo
# @@foo is not defined
end
class InvalidClass2
include NeedPositiveFoo
@@foo = -2
end
你不能挂钩 class 定义的末尾,因为没有一个 - class 可以在不同的文件、不同的时间、甚至不同的库中声明.
您可以做的是检查条件 包含模块时 ,并在定义的末尾声明包含:
module NeedPositiveFoo
def included(other)
raise unless defined?(@@foo) && @@foo > 0
end
end
class ValidClass
@@foo = 3
include NeedPositiveFoo
end
class InvalidClass1
# @@foo is not defined
include NeedPositiveFoo
end
class InvalidClass2
@@foo = -2
include NeedPositiveFoo
end
class InvalidClass3
include NeedPositiveFoo
@@foo = 4 # declared after inclusion - not a valid state...
end
虽然 Uri Agassi 的答案在您被允许将 includes 放在 class 定义的最后时完美地工作,但下面的代码将工作,尽管放置了 include
。
def included(other)
TracePoint.new(:end) do |tp|
if tp.self == other
tp.disable
raise unless defined?(other.class_variable_get(:@@foo)) # checks
end
end.enable
other.extend(ClassMethods)
end
问这样的问题时,看看 Ruby 核心库是如何做的通常很有用。 Ruby 核心库中有两个 well-known mixins,它们在 classes 上设置了某些条件,它们被混合到:
Enumerable
要求 class 有一个each
方法,可以用零参数调用并接受一个块,它yield
所有元素collection 连续。Comparable
要求 class 有一个<=>
方法,该方法可以用单个参数调用并用-1
、0
、或1
,取决于参数是大于、等于还是小于接收者。
在这两种情况下,需求只是在文档中说明,而不是在代码中说明。由 class 作者来确保他们得到满足。
事实上,在 Enumerable
情况下,根本没有真正说明要求 ,只是假设任何有能力的 Ruby 程序员都知道他们。
我会遵循核心库作者设定的这种风格,因为这是 Ruby 支持者所习惯的。