Как использовать хеш-ключи как методы для класса?
У меня есть класс и хэш. Как я могу заставить хэшировать динамически методы в классе с ключом как имя метода?
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
end
end
Например, я хотел бы иметь следующий вывод Doe
:
u = User.new
puts u.sn
Ответы
Ответ 1
def method_missing(name, *args, &blk)
if args.empty? && blk.nil? && @attributes.has_key?(name)
@attributes[name]
else
super
end
end
Объяснение: Если вы вызываете метод, который не существует, метод method_missing вызывается с именем метода в качестве первого параметра, за которым следуют аргументы, заданные методу и блоку, если он был указан.
В вышеизложенном мы говорим, что если метод, который не был определен, вызывается без аргументов и без блока, а хэш имеет запись с именем метода как ключ, он возвращает значение этой записи. В противном случае он будет действовать как обычно.
Ответ 2
Просто используйте OpenStruct:
require 'ostruct'
class User < OpenStruct
end
u = User.new :sn => 222
u.sn
Ответ 3
Решение sepp2k - это путь. Однако, если ваши @attributes никогда не меняются после инициализации и вам нужна скорость, вы можете сделать это следующим образом:
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"
Это генерирует все методы заранее...
Ответ 4
На самом деле 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
А также вы можете сделать это с помощью Struct:
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, но будьте осторожны, чтобы он не создавал метод, если он уже имеет его в методах экземпляра, вы можете его найти, используя 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
Ответ 5
Вы можете "заимствовать" 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')
Обратите внимание, что u.job - это User:: Job - этот класс автоматически создается.
Там есть получение при назначении вложенного значения. Вы не можете просто назначить хэш, но должны обернуть его в соответствующий класс:
u.job = User::Job.new 'foo' => 'bar'
u.job.foo # => 'bar
К сожалению, если вы хотите добавить вложенный хеш, который не имеет соответствующего класса, он уродливее, потому что вам нужно заставить ARes создать класс из хэша:
# 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