我有一個類和一個哈希。我怎樣才能讓哈希的成員動態地成爲類的方法,並且使用方法名作爲鍵?如何使用散列鍵作爲類的方法?
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
end
end
例如,我想能夠有以下輸出Doe
:
u = User.new
puts u.sn
我有一個類和一個哈希。我怎樣才能讓哈希的成員動態地成爲類的方法,並且使用方法名作爲鍵?如何使用散列鍵作爲類的方法?
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
end
end
例如,我想能夠有以下輸出Doe
:
u = User.new
puts u.sn
def method_missing(name, *args, &blk)
if args.empty? && blk.nil? && @attributes.has_key?(name)
@attributes[name]
else
super
end
end
解釋:如果你調用一個方法,它不存在,method_missing方法被調用,方法的名稱作爲第一個參數,然後是給定的方法和塊的參數(如果給出的話)。
在上面我們說如果一個沒有定義的方法在沒有參數的情況下被調用,並且沒有一個塊並且這個散列有一個方法名作爲關鍵字的條目,它將返回該條目的值。否則,它將照常進行。
sepp2k的解決方案是要走的路。但是,如果你的@屬性初始化後,永遠不會改變,你需要速度,那麼你可以做這樣:
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
@attributes.each do |k,v|
self.class.send :define_method, k do v end
end
end
end
User.new.givenName # => "John"
這會產生提前的所有方法...
其實severin
有更好的主意,只是因爲method_missing的使用是一個不好的做法,並非所有的時間,但大部分時間。
severin
提供的代碼存在一個問題:它返回已傳遞給初始值設定項的值,因此無法對其進行更改。我建議你一點點不同的方法:
class User < Hash
def initialize(attrs)
attrs.each do |k, v|
self[k] = v
end
end
def []=(k, v)
unless respond_to?(k)
self.class.send :define_method, k do
self[k]
end
end
super
end
end
讓我們檢查一下:
u = User.new(:name => 'John')
p u.name
u[:name] = 'Maria'
p u.name
,你也可以用結構做到這一點:
attrs = {:name => 'John', :age => 22, :position => 'developer'}
keys = attrs.keys
user = Struct.new(*keys).new(*keys.map { |k| attrs[k] })
讓我們測試一下:
p user
p user.name
user[:name] = 'Maria'
p user.name
user.name = 'Vlad'
p user[:name]
甚至是OpenStruct,但是是ca reful它不會產生方法,如果它已經有它在實例方法,您可以通過使用OpenStruct.instance_methods
尋找那些(因爲類型的使用,我現在使用第二種方法):
attrs = {:name => 'John', :age => 22, :position => 'developer'}
user = OpenStruct.new(attrs)
是的,這樣容易:
user.name
user[:name] # will give you an error, because OpenStruct isn't a Enumerable or Hash
你的解釋和擴展例子真的很棒。謝謝! – 2013-03-07 16:55:43
只需使用OpenStruct:
require 'ostruct'
class User < OpenStruct
end
u = User.new :sn => 222
u.sn
您可以 「借用」 的ActiveResource這一點。它甚至處理嵌套哈希和分配:
require 'active_resource'
class User < ActiveResource::Base
self.site = '' # must be a string
end
用法:
u = User.new "sn" => "Doe", "givenName" => "John", 'job'=>{'description'=>'Engineer'}
u.sn # => "Doe"
u.sn = 'Deere'
u.job.description # => "Engineer"
# deletion
u.attributes.delete('givenName')
注意,美。job是一個User :: Job - 這個類是自動創建的。 分配給嵌套值時有一個問題。你不能只分配一個哈希,但必須在適當的類包起來:
u.job = User::Job.new 'foo' => 'bar'
u.job.foo # => 'bar
不幸的是,當你想添加不具有對應的級嵌套哈希,這是醜陋的,因爲你必須強制AR從哈希創建類:
# assign the hash first
u.car = {'make' => 'Ford'}
# force refresh - this can be put into a method
u = User.new Hash.from_xml(u.to_xml).values.first
請務必查看OpenStruct(標準庫中的struct.rb)。這與你所要求的有點不同:它允許OpenStruct的任何方法調用成爲一個訪問器,無論它是否已經被定義。但是它不需要編寫代碼,有時候可以加上代碼。 – 2010-02-10 22:22:28