2015-08-19 1 views
1

Я создал модуль, в котором я расширяю класс Fixnum новым методом. Но когда я require модуль и попытаться использовать расширенный метод, он возвращает:Почему я не могу расширить класс Fixnum в модуле и использовать его?

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 

Как я могу это решить?

ответ

8

Есть три проблемы с вашим кодом:

  1. Вы создаете новый класс EanControl::Fixnum, но вы на самом деле хотите изменить существующего встроенного ::Fixnum , Решение: явно начать постоянный поиск с верхнего уровня, или, более идиоматически, просто отбросить модуль.

    module EanControl 
        class ::Fixnum 
        # … 
        end 
    end 
    
    # although it would be much simpler to just do this: 
    
    class Fixnum 
        # … 
    end 
    
  2. Вы определяете 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 
    
  3. Спецификация Рубин Язык фактически не гарантирует, что даже являетсяFixnum класса. Это только гарантирует, что существует класс Integer, и это позволяет использовать различные реализации для подклассов, специфичных для реализации. (Например, YARV имеет Fixnum и Bignum подклассы Integer.) Поскольку вы добавляете только метод к Fixnum, он не будет работать для других Integer s, которые не являются Fixnum s. А поскольку диапазон Fixnum s отличается для разных реализаций архитектур (например, на YARV на 32-битных системах, 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 
    

Наконец, я хочу предложить, по крайней мере, используя подмешать здесь:

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 
+0

Благодарим вас за фантастический андерсер. – narzero

1

В вашем расширении, вы определили roundup как метод класса, а 12 является экземпляр.

Try:

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 
+0

Это не будет работать либо, так как теперь он по-прежнему является метод экземпляра 'EanControl :: Fixnum' , а не ':: Fixnum'. –

+1

Это не сработало, получилась такая же ошибка. – narzero