2009-09-09 3 views
2

У меня есть объект в настоящее время:рубин массив объектов ... или хэш

class Items 
    attr_accessor :item_id, :name, :description, :rating 

    def initialize(options = {}) 
     options.each { 
     |k,v| 
     self.send("#{k.to_s}=".intern, v) 
     } 
    end 

end 

Я его, назначаемые отдельных объектов в массив ...

@result = [] 

some loop>> 
    @result << Items.new(options[:name] => 'name', options[:description] => 'blah') 
end loop>> 

Но вместо назначения My особый объект к массиву ... как я могу сделать сам объект коллекцией?

В основном хотят иметь объект таким образом, чтобы я мог определить методы, такие как

def self.names 
    @items.each do |item| 
     item.name 
    end 
end 

Я надеюсь, что имеет смысл, возможно, я с видом на какой-то грандиозный план, который сделает мою жизнь гораздо проще в 2 линии.

ответ

6

Несколько замечаний, прежде чем я отправлю пример того, как его переделать.

  • Давать класс множественного имя может привести к большому количеству смысловых вопросов при объявлении новых объектов, так как в этом случае вы бы назвали Items.new, подразумевая, вы создаете несколько пунктов, когда на самом деле на самом деле делает один , Используйте единственную форму для отдельных объектов.
  • Будьте внимательны при вызове произвольных методов, поскольку вы будете генерировать исключение при любых промахах. Либо проверьте, что вы можете позвонить им сначала, либо спастись от неизбежной катастрофы там, где это применимо.

Одним из способов решения вашей проблемы является создание специального класса коллекции специально для объектов Item, где он может предоставить вам необходимую информацию об именах и т. Д.Например:

class Item 
    attr_accessor :item_id, :name, :description, :rating 

    def initialize(options = { }) 
    options.each do |k,v| 
     method = :"#{k}=" 

     # Check that the method call is valid before making it 
     if (respond_to?(method)) 
     self.send(method, v) 
     else 
     # If not, produce a meaningful error 
     raise "Unknown attribute #{k}" 
     end 
    end 
    end 
end 

class ItemsCollection < Array 
    # This collection does everything an Array does, plus 
    # you can add utility methods like names. 

    def names 
    collect do |i| 
     i.name 
    end 
    end 
end 

# Example 

# Create a custom collection 
items = ItemsCollection.new 

# Build a few basic examples 
[ 
    { 
    :item_id => 1, 
    :name => 'Fastball', 
    :description => 'Faster than a slowball', 
    :rating => 2 
    }, 
    { 
    :item_id => 2, 
    :name => 'Jack of Nines', 
    :description => 'Hypothetical playing card', 
    :rating => 3 
    }, 
    { 
    :item_id => 3, 
    :name => 'Ruby Book', 
    :description => 'A book made entirely of precious gems', 
    :rating => 1 
    } 
].each do |example| 
    items << Item.new(example) 
end 

puts items.names.join(', ') 
# => Fastball, Jack of Nines, Ruby Book 
+0

есть способ сделать так, чтобы все атрибуты элементов автоматически велись, нравится метод имен? IE. Вызов .description возвращает то же самое, что и .names ...? – holden

+0

Если вы хотите получить adventuresome, вы можете либо построить эти методы динамически, либо использовать метод_missing для вызова вызовов. – tadman

+0

Могу ли я использовать Ruby mixins для добавления таких функций ко всем массивам, содержащим объекты ActiveRecord? Некоторые указатели будут полезны. – Sanjay

-1

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

Добавить «вернуть себя» в качестве последней строки инициализации, и вы золотой.

Class Item 
    def initialize(options = {}) 
    ## Do all kinds of stuff. 

    return self 
    end 
end 
+0

ну, эта часть на самом деле работает нормально, это просто то, что я кормлю свои объекты в массив, когда захочу иметь мега объект (коллекцию), который я могу выполнять функции. – holden

+0

Item.new всегда возвращает вновь созданный независимо от возвращаемого значения initialize(). Добавление return self не имеет никакого значения. – molf

+1

В отличие от других языков, возвращаемое значение инициализатора Ruby игнорируется, и созданный объект всегда возвращается. Это может быть изменено путем повторного написания нового метода класса, но это, вероятно, плохая идея. – tadman

2

Вы знаете ключевое слово рубин выход?

Я не совсем уверен, что именно вы хотите сделать. У меня есть две интерпретации ваших намерений, так что я приведу пример, который делает две совершенно разные вещи, один из них, мы надеемся, отвечая на ваш вопрос:

class Items 
    @items = [] 
    class << self 
    attr_accessor :items 
    end 
    attr_accessor :name, :description 
    def self.each(&args) 
    @items.each(&args) 
    end 
    def initialize(name, description) 
    @name, @description = name, description 
    Items.items << self 
    end 
    def each(&block) 
    yield name 
    yield description 
    end 
end 

a = Items.new('mug', 'a big cup') 
b = Items.new('cup', 'a small mug') 
Items.each {|x| puts x.name} 
puts 
a.each {|x| puts x} 

Воспроизводит

mug 
cup 

mug 
a big cup 

ли вы просите что-то вроде Items.each или a.each или что-то совершенно другое?

+0

Кажется странным здесь, используя два выхода, когда один с двумя аргументами, вероятно, будет более полезен. – tadman

+0

Зависит от того, что нужно делать. Метод «каждый» обычно вызывает данный блок один раз для каждого элемента, вставляющего текущий элемент. Конечно, вы правы в случае моего примера (что не имеет особого смысла, я просто хотел показать использование). Учет обоих аргументов вызовет блок только один раз, вставив два элемента. – Whoever

1

Отвечая только дополнительный вопрос, который вы просили в своем комментарии к решению Тадмана: если вы замените в коде Тадмана в определение метода имен в классе ItemsCollection по

def method_missing(symbol_s, *arguments) 
    symbol, s = symbol_s.to_s[0..-2], symbol_s.to_s[-1..-1] 
    if s == 's' and arguments.empty? 
    select do |i| 
     i.respond_to?(symbol) && i.instance_variables.include?("@#{symbol}") 
    end.map {|i| i.send(symbol)} 
    else 
    super 
    end 
end 

Для его примерных данных вы получите следующие выходы:

puts items.names.join(', ') 
# => Fastball, Jack of Nines, Ruby Book 
puts items.descriptions.join(', ') 
# => Faster than a slowball, Hypothetical playing card, A book made entirely of precious gems 

Как я не знаю ни о каком способе проверить, имя происходит из атрибута или из другого метода (за исключением того, что вы переопределяете attr_accessor, attr и т. д. в модуле класса). Я добавил некоторые проверки на работоспособность: я тестирую, существует ли соответствующий метод и переменная экземпляра этого имени. Поскольку класс ItemsCollection не предусматривает, что добавляются только объекты класса Item, я выбираю только элементы, выполняющие обе проверки. Вы также можете удалить выбор и поместить тест на карту и вернуть нуль, если проверки не пройдены.

+0

Я только что увидел это здесь, добавив свой комментарий выше. Если вы используете Rails, вы можете использовать метод сингулярности, чтобы сделать деплюрализацию для вас, вместо того, чтобы прибегать к 's'-stripping. – tadman

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