为什么我不能在模块中扩展 Fixnum class 并使用它?
Why can't I extend the Fixnum class in a module and use it?
我创建了一个模块,在其中我使用新方法扩展了 Fixnum class。但是当我 require
模块并尝试使用扩展方法时,它 returns:
NoMethodError: undefined method `roundup' for 13:Fixnum
这是我的模块的样子:
module EanControl
# Extend Fixnum with #roundup
class Fixnum
def self.roundup
return self if self % 10 == 0 # already a factor of 10
return self + 10 - (self % 10) # go to nearest factor 10
end
end
# More code...
end
这就是我正在做的事情:
require 'path_to_module'
12.roundup
# => NoMethodError: undefined method `roundup' for 13:Fixnum
我该如何解决这个问题?
在您的扩展中,您已将 roundup
定义为 class 方法,而 12
是 实例.
尝试:
module EanControl
# Extend Fixnum with #roundup
class Fixnum
def roundup
return self if self % 10 == 0 # already a factor of 10
return self + 10 - (self % 10) # go to nearest factor 10
end
end
# More code...
end
您的代码存在三个问题:
您正在创建 新的 class EanControl::Fixnum
,但您实际上想要更改 现有的 内置 ::Fixnum
。解决方案:显式地从顶层开始常量查找,或者更通俗地说,只是删除模块。
module EanControl
class ::Fixnum
# …
end
end
# although it would be much simpler to just do this:
class Fixnum
# …
end
您将roundup
定义为对象Fixnum
的单例方法,但您将其调用为Fixnum
实例的实例方法。解决方案:使roundup
成为一个实例方法:
class Fixnum
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
Ruby 语言规范实际上并不能保证 是 一个 Fixnum
class。它只保证有一个 Integer
class,并且它允许不同的实现可以提供特定于实现的 subclasses。 (例如,YARV 有 Integer
的 Fixnum
和 Bignum
subclasses。)由于您只将方法添加到 Fixnum
,因此它不适用于其他方法Integer
不是 Fixnum
。并且由于 Fixnum
s 的范围对于不同的架构实现是不同的(例如,在 32 位系统的 YARV 上,Fixnum
s 是 31 位,在 64 位系统上,它们是 63 位,在 JRuby,它们始终是 64 位),您甚至不确定您的方法将适用于哪些数字以及何时会失败。 (例如:9223372036854775808.roundup # NoMethodError: undefined method 'roundup' for 9223372036854775808:Bignum
。) 解决方法:使该方法成为Integer
:
的实例方法
class Integer
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
最后,我想建议至少在这里使用 mixin:
module IntegerWithRoundup
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
class Integer
include IntegerWithRoundup
end
现在,如果其他人调试了您的代码,并且想知道此 roundup
方法来自何处,则祖先链中有清晰的痕迹:
12.method(:roundup).owner
# => IntegerWithRoundup
更好的方法是使用优化,这样你的 monkeypatch 就不会污染全局命名空间:
module IntegerWithRoundup
module Roundup
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
refine Integer do
include Roundup
end
end
12.roundup
# NoMethodError: undefined method `roundup' for 12:Fixnum
using IntegerWithRoundup
12.roundup
# => 20
我创建了一个模块,在其中我使用新方法扩展了 Fixnum class。但是当我 require
模块并尝试使用扩展方法时,它 returns:
NoMethodError: undefined method `roundup' for 13:Fixnum
这是我的模块的样子:
module EanControl
# Extend Fixnum with #roundup
class Fixnum
def self.roundup
return self if self % 10 == 0 # already a factor of 10
return self + 10 - (self % 10) # go to nearest factor 10
end
end
# More code...
end
这就是我正在做的事情:
require 'path_to_module'
12.roundup
# => NoMethodError: undefined method `roundup' for 13:Fixnum
我该如何解决这个问题?
在您的扩展中,您已将 roundup
定义为 class 方法,而 12
是 实例.
尝试:
module EanControl
# Extend Fixnum with #roundup
class Fixnum
def roundup
return self if self % 10 == 0 # already a factor of 10
return self + 10 - (self % 10) # go to nearest factor 10
end
end
# More code...
end
您的代码存在三个问题:
您正在创建 新的 class
EanControl::Fixnum
,但您实际上想要更改 现有的 内置::Fixnum
。解决方案:显式地从顶层开始常量查找,或者更通俗地说,只是删除模块。module EanControl class ::Fixnum # … end end # although it would be much simpler to just do this: class Fixnum # … end
您将
roundup
定义为对象Fixnum
的单例方法,但您将其调用为Fixnum
实例的实例方法。解决方案:使roundup
成为一个实例方法:class Fixnum def roundup return self if (self % 10).zero? # already a factor of 10 self + 10 - (self % 10) # go to nearest factor 10 end end
Ruby 语言规范实际上并不能保证 是 一个
的实例方法Fixnum
class。它只保证有一个Integer
class,并且它允许不同的实现可以提供特定于实现的 subclasses。 (例如,YARV 有Integer
的Fixnum
和Bignum
subclasses。)由于您只将方法添加到Fixnum
,因此它不适用于其他方法Integer
不是Fixnum
。并且由于Fixnum
s 的范围对于不同的架构实现是不同的(例如,在 32 位系统的 YARV 上,Fixnum
s 是 31 位,在 64 位系统上,它们是 63 位,在 JRuby,它们始终是 64 位),您甚至不确定您的方法将适用于哪些数字以及何时会失败。 (例如:9223372036854775808.roundup # NoMethodError: undefined method 'roundup' for 9223372036854775808:Bignum
。) 解决方法:使该方法成为Integer
:class Integer def roundup return self if (self % 10).zero? # already a factor of 10 self + 10 - (self % 10) # go to nearest factor 10 end end
最后,我想建议至少在这里使用 mixin:
module IntegerWithRoundup
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
class Integer
include IntegerWithRoundup
end
现在,如果其他人调试了您的代码,并且想知道此 roundup
方法来自何处,则祖先链中有清晰的痕迹:
12.method(:roundup).owner
# => IntegerWithRoundup
更好的方法是使用优化,这样你的 monkeypatch 就不会污染全局命名空间:
module IntegerWithRoundup
module Roundup
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
refine Integer do
include Roundup
end
end
12.roundup
# NoMethodError: undefined method `roundup' for 12:Fixnum
using IntegerWithRoundup
12.roundup
# => 20