让我们先看看有助于我想要实现的代码:
class PostalInfo
attr_reader :name, :code
def initialize (id, name, code)
@id = id
@name = name
@code = code
end
def method_missing(method, *args, &blk)
if method.to_s == "#{name}"
return code
else
super
end
end
end
pi1 = PostalInfo.new(1, 'united_states', 'US')
pi2 = PostalInfo.new(2, 'united_kingdom', 'UK')
因此,当我运行下面的代码时,它给出的输出为:
pi1.united_states => 'US'
pi2.united_kingdom => 'UK'
到这里为止很好,但我也想做一些类似的事情
PostalInfo.united_states => 'US'
PostalInfo.united_kingdom => 'UK'
怎么做,提前谢谢
这将设置一个类属性来保存数据,并且每当初始化实例时,它都会添加到该数据结构中,并使用类似的类级method_missing
。
class PostalInfo
attr_reader :name, :code
@@postal_info = {}
def self.method_missing(method, *args, &blk)
name = method.to_s
if @@postal_info[name]
@@postal_info[name]
else
super
end
end
def initialize (id, name, code)
@id = id
@name = name
@code = code
@@postal_info[@name] = @code
end
def method_missing(method, *args, &blk)
if method.to_s == "#{name}"
return code
else
super
end
end
end
pi1 = PostalInfo.new(1, 'united_states', 'US')
pi2 = PostalInfo.new(2, 'united_kingdom', 'UK')
PostalInfo.united_states #=> 'US'
PostalInfo.united_kingdom #=> 'UK'
我会说,这似乎是一个奇怪的设计,我通常建议避免在类方法中使用可变状态,并尽可能method_missing
。
你可以写这样的东西:
class PostalInfo
POSTAL_HASH = {
united_states: 'US',
united_kingdom: 'UK',
}.freeze
def self.method_missing(method, *args, &blk)
POSTAL_HASH[method] || super
end
end
跳过缺少的方法可能会提高性能:
class PostalInfo
POSTAL_HASH = {
united_states: 'US',
united_kingdom: 'UK',
}.freeze
class << self
POSTAL_HASH.each do |name, code|
define_method(name) do
code
end
end
end
end
除了一个例外,您需要在类的单例类中模仿答案第一部分中的代码。差异涉及实例变量的初始化。而不是使用 PostalInfo::new
和 PostalInfo#initialize
,你需要创建一个类方法来执行此操作(我称之为 add_country_data
(。请注意,由于不使用类的实例变量id
,因此我没有将其包含在代码中。
class PostalInfo
class << self
attr_reader :country_data
def add_country_data(name, code)
(@country_data ||= {})[name] = code
end
def add_country_data(name, code)
@country_data[name] = code
end
def method_missing(method, *args, &blk)
return country_data[method.to_s] if country_data.key?(method.to_s)
super
end
end
end
PostalInfo.add_country_data('united_states', 'US')
PostalInfo.add_country_data('united_kingdom', 'UK')
PostalInfo.united_states
#=> "US"
PostalInfo.united_kingdom
#=> "UK"
PostalInfo.france
#=> NoMethodError (undefined method `france' for PostalInfo:Class)
虽然这符合您的要求,但我倾向于以更传统的方式构建类:
class PostalInfo
attr_reader :name, :code
@instances = []
def initialize(name, code)
@name = name
@code = code
self.class.instances << self
end
singleton_class.public_send(:attr_reader, :instances)
end
us = PostalInfo.new('united_states', 'US')
uk = PostalInfo.new('united_kingdom', 'UK')
us.code
#=> "US"
uk.code
#=> "UK"
PostalInfo.instances
#=> [#<PostalInfo:0x00005c1f24c5ccf0 @name="united_states", @code="US">,
# #<PostalInfo:0x00005c1f24c71858 @name="united_kingdom", @code="UK">]