Как использовать хеш-ключи в качестве методов в классе? - PullRequest
15 голосов
/ 11 февраля 2010

У меня есть класс и хэш. Как я могу заставить члены хеша динамически становиться методами класса с ключом в качестве имени метода?

class User
  def initialize
    @attributes = {"sn" => "Doe", "givenName" => "John"}
  end
end

Например, я бы хотел иметь следующий вывод Doe:

u = User.new
puts u.sn

Ответы [ 5 ]

30 голосов
/ 12 декабря 2010

Просто используйте OpenStruct:

require 'ostruct'
class User < OpenStruct
end

u = User.new :sn => 222
u.sn
14 голосов
/ 11 февраля 2010
def method_missing(name, *args, &blk)
  if args.empty? && blk.nil? && @attributes.has_key?(name)
    @attributes[name]
  else
    super
  end
end

Объяснение: Если вы вызываете метод, который не существует, method_missing вызывается с именем метода в качестве первого параметра, за которым следуют аргументы, переданные методу, и блок, если он был задан.

Выше мы говорим, что если метод, который не был определен, вызывается без аргументов и без блока, а в хэше есть запись с именем метода в качестве ключа, он вернет значение этой записи. В противном случае все будет продолжаться как обычно.

4 голосов
/ 11 февраля 2010

Решение 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"

Это генерирует все методы заранее ...

3 голосов
/ 31 октября 2010

На самом деле 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
2 голосов
/ 21 июля 2011

Для этого вы можете «позаимствовать» 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...