nar*_*ero 2 ruby module class extend
我创建了一个模块,在其中我使用新方法扩展了Fixnum类.但是当我require模块并尝试使用扩展方法时,它返回:
NoMethodError: undefined method `roundup' for 13:Fixnum
Run Code Online (Sandbox Code Playgroud)
这是我的模块的样子:
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
Run Code Online (Sandbox Code Playgroud)
这就是我正在做的事情:
require 'path_to_module'
12.roundup
# => NoMethodError: undefined method `roundup' for 13:Fixnum
Run Code Online (Sandbox Code Playgroud)
我该怎么解决这个问题?
Jör*_*tag 11
您的代码有三个问题:
您正在创建一个新类EanControl::Fixnum,但实际上您想要更改现有的内置类::Fixnum.解决方案:从顶层显式启动常量查找,或者更惯用地,只是删除模块.
module EanControl
class ::Fixnum
# …
end
end
# although it would be much simpler to just do this:
class Fixnum
# …
end
Run Code Online (Sandbox Code Playgroud)您将其定义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
Run Code Online (Sandbox Code Playgroud)Ruby语言规范实际上并不能保证甚至有一个Fixnum类.它只保证有一个Integer类,它允许不同的实现可以提供特定于实现的子类.(例如,YARV具有Fixnum和Bignum子类Integer.)由于您只添加方法Fixnum,因此它不适用于其他Integers,而不是Fixnums.并且由于Fixnums 的范围对于不同的体系结构实现是不同的(例如,在32位系统上的YARV,Fixnums是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
Run Code Online (Sandbox Code Playgroud)最后,我想建议至少在这里使用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
Run Code Online (Sandbox Code Playgroud)
现在,如果其他人调试你的代码,并想知道这个roundup方法的来源,那么祖先链中有一个明显的痕迹:
12.method(:roundup).owner
# => IntegerWithRoundup
Run Code Online (Sandbox Code Playgroud)
更好的方法是使用精化,这样你的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
Run Code Online (Sandbox Code Playgroud)