Ruby: добавьте пользовательские свойства в встроенные классы

StackOverflow https://stackoverflow.com/questions/2398923

Вопрос

Вопрос:Используя Ruby, просто добавить пользовательские методы к существующим классам, но как вы добавляете пользовательские характеристики? Вот пример того, что я пытаюсь сделать:

myarray = Array.new();
myarray.concat([1,2,3]);
myarray._meta_ = Hash.new();      # obviously, this wont work
myarray._meta_['createdby'] = 'dreftymac';
myarray._meta_['lastupdate'] = '1993-12-12';

## desired result
puts myarray._meta_['createdby']; #=> 'dreftymac'
puts myarray.inspect()            #=> [1,2,3]

Цель состоит в том, чтобы построить определение класса таким образом, чтобы материал, который не работает в примере выше, будет работать, как ожидалось.

Обновлять: (Уточнить вопрос) Один аспект, который был исключен из исходного вопроса: также является целью добавления «значений по умолчанию», которые обычно будут настроены в инициализация метод класса.

Обновлять: (Зачем это) нормально, очень просто просто создать пользовательский класс, который наследует от массива (или любого встроенного класса, который вы хотите подражать). Этот вопрос вытекает из какого-либо «только тестирования» кода, и не пытается игнорировать этот вообще приемлемый подход.

Это было полезно?

Решение

Напомним, что в Ruby у вас нет доступа к атрибутам (переменные экземпляра) вне этого экземпляра. У вас есть доступ только к публичным методам экземпляра.

Вы можете использовать attr_accessor Чтобы создать метод для класса, который действует как свойство, как вы описываете:

irb(main):001:0> class Array
irb(main):002:1>  attr_accessor :_meta_
irb(main):003:1> end
=> nil
irb(main):004:0> 
irb(main):005:0* x = [1,2,3]
=> [1, 2, 3]
irb(main):006:0> x._meta_ = Hash.new
=> {}
irb(main):007:0> x._meta_[:key] = 'value'
=> "value"
irb(main):008:0> 

Для простого способа сделать инициализацию по умолчанию для Accessior, нам понадобится в основном перерыв attr_accessor сами:

class Class
  def attr_accessor_with_default accessor, default_value
    define_method(accessor) do
      name = "@#{accessor}"
      instance_variable_set(name, default_value) unless instance_variable_defined?(name)
      instance_variable_get(name)
    end

    define_method("#{accessor}=") do |val|
      instance_variable_set("@#{accessor}", val)
    end
  end
end

class Array
    attr_accessor_with_default :_meta_, {}
end

x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_

y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_

Но ждать! Выход неверный:

{:key=>"value"}
{:foo=>"bar", :key=>"value"}

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

Лучший способ, возможно, просто использовать блок:

class Class
  def attr_accessor_with_default accessor, &default_value_block
    define_method(accessor) do
      name = "@#{accessor}"
      instance_variable_set(name, default_value_block.call) unless instance_variable_defined?(name)
      instance_variable_get(name)
    end

    define_method("#{accessor}=") do |val|
      instance_variable_set("@#{accessor}", val)
    end
  end
end

class Array
    attr_accessor_with_default :_meta_ do Hash.new end
end

x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_

y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_

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

{:key=>"value"}
{:foo=>"bar"}

Другие советы

Разве недвижимость не просто добыча и сеттер? Если это так, не могли бы вы просто сделать:

class Array
  # Define the setter
  def _meta_=(value)
    @_meta_ = value
  end

  # Define the getter
  def _meta_
    @_meta_
  end
end

Тогда вы можете сделать:

x = Array.new
x._meta_
# => nil

x._meta_ = {:name => 'Bob'}

x._meta_
# => {:name => 'Bob'}

Это помогает?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top