2016-05-05 2 views

ответ

6

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

class MyStorageClass 
    def initialize 
    @data = {} 
    end 

    def add_entry(key, value) 
    (@data[key] ||= []) << value 
    define_singleton_method(key){ @data[key] } 
    end 

    def get_entry(key) 
    @data.key?(key) or raise NoMethodError 
    @data[key] 
    end 
end 

Вы можете проверить, что вы не перекрывая предопределенный метод первого ([email protected]?(key) && self.respond_to?(key) в верхней части метода будет делать add_entry), но это для другого разговора. Может быть плохо, если кто-то попытался добавить ключ, который называется inspect, class, или, о, get_entry, например!

+0

Вы могли бы также INIT @data с Hash.new {[]}, так что вам не нужно, || = часть в add_entry методе – karina

+1

Danger ! Это должно быть «Hash.new {| h, k | h [k] = []} ', иначе все новые ключи получат значения, связанные с другими. Объект [], который вы передали, представляет собой единый объект в памяти, на который будут ссылаться все ключи, а используемый нами '<<' -модуль изменяет этот объект на месте. Попробуйте! 'h = Hash.new ([]); h [: x] << 1; h [: y] 'дает вам' [1] '. Обычно я предпочитаю инициализатор блока для Hash, если он более выгоден, чем сохранение '|| =' –

+0

. Я использовал фигурные скобки. – karina

0

Я не уверен, что вы называете «более разумным,» но здесь это шаблон без eval с, чтобы начать с:

def add_entry key, value 
    # define instance variable unless it is already defined 
    instance_variable_set :"@#{key}", [] \ 
    unless instance_variable_defined? :"@#{key}" 
    # add value to the array 
    instance_variable_set :"@#{key}", instance_variable_get(:"@#{key}") + value 
    # define getter 
    self.class.send :define_method key { instance_variable_get :"@#{key}" } \ 
    unless self.class.instance_methods.include?(key) 
end 

Поглотитель может быть определена в более читаемом виде:

self.class.send :attr_reader, key \ 
    unless self.class.instance_methods.include?(key) 
0

Это может быть достигнуто с помощью instance_variable_set и attr_accessor:

class MyStorageClass 
    def add_entry(key, value) 
    if respond_to?(key) 
     key << value 
    else 
     instance_variable_set("@#{key}", [value]) 
     self.class.send(:attr_accessor, key) 
    end 
    end 
end 

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

1

ИМО это действительно плохая идея. Не делай это! Вы будете добавлять сложность с очень небольшим преимуществом.

Я рекомендую вместо этого OpenStruct. Это отличные объекты - вы можете называть геттеры и сеттеры на них по своему усмотрению, не указав заранее атрибуты. Возможно, немного неэффективно, но это обычно не имеет значения.

Дополнительным преимуществом OpenStruct является то, что вы можете группировать свои атрибуты в логические наборы, например. connection_options, formatting_options и т.д. Вот пример сценария для иллюстрации:

#!/usr/bin/env ruby 

require 'ostruct' 

class MyClass 

    attr_reader :config_options # only if you want to expose this 

    def initialize 
    @config_options = OpenStruct.new 
    end 

    def do_something 
    config_options.color = 'yellow' 
    config_options.size = 'medium' 
    end 

    def to_s 
    config_options.to_h.to_s 
    end 
end 

my_class = MyClass.new 
my_class.do_something 
puts my_class # outputs: {:color=>"yellow", :size=>"medium"} 
+0

['Hashie :: Mash'] (https://github.com/intridea/hashie#mash) – mudasobwa

+0

@mudasobwa Контекст/разработка, пожалуйста? –

+0

Я просто хотел отбросить ссылку на большую библиотеку, это [ИМХО] во что бы то ни стало, лучший эквивалент 'OpenStruct'. – mudasobwa

Смежные вопросы