2013-12-13 3 views
1

Я хотел бы создать набор, который позволяет добавлять только определенные объекты (kind_of?) и исключение, которое должно быть поднято на попытайтесь добавить чужой объект.Ruby: Как разрешить добавление только определенного типа объектов в набор

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

Дополнение: Моя цель состоит в том, чтобы создать «шаблонный» контейнерный класс, как в C++ (например, Set), чтобы я мог установить тип один раз при определении экземпляра и сравнить два набора на уровне класса, если они то же (т. е. принимать один и тот же тип), но сохраняя совместимость с наборами по умолчанию, в качестве примера set_instance_a.class == set_instance_b.class должен выдавать true, если они принимают объект того же типа.

Идея у меня было перегрузить оператор ::[] так, что я мог бы написать что-то вроде my_set = MySet[Foo], который должен возвращать MySet экземпляр, который принимает только объекты типа Foo

Благодарности!

+0

Итак, вам действительно нужен класс-строитель, который позволяет вам сказать 'c = MySet.new (String) ', чтобы получить подкласс' Set' 'c', который позволяет вам добавлять' String'? –

+0

Да, у меня есть класс типа, который содержит атрибуты и коллекции объектов, которые позже сериализованы в JSON. «Селективный набор» позволяет мне определить атрибут Set на модели, которому разрешено удерживать строки или какой-либо другой тип объекта. Я думаю, что это не так уж далеко, чтобы сделать класс контейнера выборочным на то, что он может содержать, а что нет – sled

ответ

3

Class - это тоже объект, поэтому при необходимости вы можете создавать новые классы во время выполнения. Class конструктор позволяет указать базовый класс и даже оценивать код в контексте нового класса:

новый (super_class = Object) → a_class
новый (super_class = Object) {| мода | ...} → a_class

Создает новый анонимный (безымянный) класс с данным суперклассом (или Object, если параметр не задан). Вы можете присвоить классу имя, назначив объект класса константе.

Если задан блок, ему передается объект класса, а блок оценивается в контексте этого класса с использованием class_eval.

class_eval позволяет определять методы и псевдонимы. Все это означает, что вы можете сказать что-то вроде этого:

module Kernel 
    def MySet(c) 
     Class.new(Set) do 
      @@allowed = c 
      def add(o) 
       raise ArgumentError.new("#{o.class} is stinky!") if(!o.is_a?(@@allowed)) 
       super(o) 
      end 
      alias_method :<<, :add 
     end 
    end 
end 

И потом:

s = MySet(String).new 
s.add(6)  # Exception! 
s << 'pancakes' # Allowed 

Я залатали MySet метод в Kernel, чтобы соответствовать Array, Complex, ... методы-то -pretend-to-be-functions, вы можете поместить его куда угодно.

3
require "set" 

class MySet < Set 
    def add e 
    raise "Invalid element #{e}" unless e.kind_of?(Foo) 
    super(e) 
    end 
end 

или, если вы не хотите использовать подкласс, то:

require "set" 

class Set 
    alias original_add :add 
    def add e 
    raise "Invalid element #{e}" unless e.kind_of?(Foo) 
    original_add(e) 
    end 
end 

Редактировать добавляемые в 2017 году Для того, чтобы сделать это без подклассов в современном стиле,

require "set" 

module MySet 
    def add e 
    raise "Invalid element #{e}" unless e.kind_of?(Foo) 
    super(e) 
    end 
end 

class Set 
    prepend MySet 
end 
+0

большое спасибо! Мне также нужно было перегрузить оператор '<<', так как он не использует 'add' :) – sled

+0

жаль, что я не упомянул об этом в исходном вопросе: Моя цель состоит в том, чтобы создать« шаблонный »контейнерный класс (например, Установите в C++). Чтобы я мог установить тип, который он принимает один раз, а затем сравнивает два набора на уровне класса, если они являются «одинаковыми» (например, принимают один и тот же тип), но при этом сохраняют совместимость со «стандартными» наборами. – sled

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