替换鳕鱼中的静态变量

时间:2014-11-14 21:56:27

标签: ruby

class Numeric
 @@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}

  def method_missing(method_id, *args, &block) 
    singular_currency = method_id.to_s.gsub(/s$/, '')
    if @@currencies.has_key?(singular_currency) 
      self * @@currencies[singular_currency]
    else
      super
    end
  end
end

puts 3.yen
# Output is
# 0.039

我的问题是,如果我们使用即时变量@currencies替换@@ currency并添加了attr_reader,那么为什么这段代码不会起作用?货币

像这样的东西

class Numeric
 @currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
 attr_accessor :currencies

  def method_missing(method_id, *args, &block) 
    singular_currency = method_id.to_s.gsub(/s$/, '')
    if @currencies.has_key?(singular_currency) 
      self * @currencies[singular_currency]
    else
      super
    end
  end
end

puts 3.yen
# Output
# method_missing': undefined method `has_key?' for nil:NilClass (NoMethodError)
#   from Untitled.rb:15:in `<main>'

Isn&#t; t 3已经是类Numeric的瞬间,因此,setter货币应该能够工作并返回正确的散列组合?

编辑:那么method_missing是一个静态方法呢?为什么不用self.method_missing定义?

4 个答案:

答案 0 :(得分:1)

在类声明的上下文中设置@currencies会在类本身上设置一个实例变量:

Numeric.instance_variable_get(:@currencies)
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}

另一方面,@currencies类和method_missing访问者中的currencies是指特定数字实例上的@currencies变量,其中不是{\ n}}定义:

Numeric.new.instance_variable_get(:@currencies)
#=> nil

您可以通过在类本身上定义访问器并使用method_missing方法调用该访问器来解决此问题:

class Numeric
  @currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
  class << self
    attr_accessor :currencies
  end

  def method_missing(method_id, *args, &block)
    singular_currency = method_id.to_s.gsub(/s$/, '')
    if self.class.currencies.has_key?(singular_currency)
      self * self.class.currencies[singular_currency]
    else
      super
    end
  end
end

Numeric.currencies
#=> {"yen"=>0.013, "euro"=>1.292, "rupee"=>0.019}

但这种方法仍然存在问题。即使currencies访问器现在引用类上的实例变量(而不是之前该类的特定实例上的实例变量),@currencies仍然只在{ {1}}类,而不是它的任何子类:

Numeric

要解决此问题,您可以修改属性访问者以自动为每个单独的类提供默认值(因此Fixnum.currencies #=> nil Fixnum每个都有自己独立的Float变量,或者回到使用类变量,如下所示:

@currencies

答案 1 :(得分:0)

实例变量仅适用于类的实例。类变量可供整个班级使用。

换句话说,代码中的@currencies.has_key?未定义,因为它无法在第2行看到@currencies

答案 2 :(得分:0)

问题是您在方法之外初始化@currencies。任何不在方法内的代码都在类对象的上下文中进行评估,而不是实例。

看看这个示例类:

class Foo
  @bar = "baz"

  def self.bar; @bar; end

  def bar; @bar; end
end

现在让我们看看我们定义的这两种方法的结果。

Foo.bar     # => "baz"
Foo.new.bar # => nil

这意味着@bar属于Foo类,而不属于Foo的实例。

您的问题可以通过在方法中初始化@currency来解决,通常是initialize

class Numeric
  def initialize
    @currency = ...
  end
end

答案 3 :(得分:0)

你可以这样做:

class Numeric
  @currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
  class << self
    attr_reader :currencies
  end
  def method_missing(method_id, *args, &block) 
    singular_currency = method_id.to_s.gsub(/s$/, '')
    if Numeric.currencies.key?(singular_currency) 
      self * Numeric.currencies[singular_currency]
    else
      super
    end
  end
end

行:

class << self
  attr_reader :currencies
end

为类实例变量@currencies创建一个读访问器。

你可能倾向于写(就像我最初做的那样):

if self.class.currencies.key?(singular_currency) 

但这不起作用,因为method_missing3的{​​{1}},Fixnum的实例,Numeric的子类。回想一下,子类不能直接访问他们的祖先类方法。这就是为什么我们需要明确地将Numeric识别为接收者。

如:

Fixnum.ancestors => [Fixnum, Integer, Numeric,...]

我们看到了:

if self.class.superclass.superclass.currencies.key?(singular_currency) 

适用于Fixnum,但不适用于Numeric的其他子类:BignumFloat