ruby对象数组...或哈希

时间:2009-09-09 14:08:46

标签: ruby-on-rails ruby arrays class methods

我现在有一个对象:

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>>

但不是将我的单个对象分配给数组......我怎样才能使对象本身成为一个集合?

基本上想要以这样的方式拥有对象,以便我可以定义诸如

之类的方法
def self.names
   @items.each do |item|
      item.name
   end
end

我希望这是有道理的,可能我会忽略一些可以让我的生活在两行中变得更加轻松的宏伟计划。

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

你知道Ruby关键词 yield 吗?

我不太确定你到底想做什么。我对你的意图有两种解释,所以我给出了一个例子,它使两个完全不同的东西,其中一个希望回答你的问题:

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 或完全不同的内容?

答案 2 :(得分:1)

回答您在对tadman解决方案的评论中提出的其他问题:如果您在tadman的代码中替换了

类中的方法名称的定义

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

由于我不知道有什么方法可以检查方法名是来自属性还是来自其他方法(除了你在类Module中重新定义attr_accessor,attr等)我添加了一些健全性检查:我测试是否存在相应的方法和该名称的实例变量。由于类ItemsCollection不强制只添加类Item的对象,因此我只选择满足两个检查的元素。您还可以删除选择并将测试放入地图中,如果检查失败,则返回nil。

答案 3 :(得分:-1)

关键是返回值。如果没有给出'return'语句,则返回最后一个语句的结果。你最后一个语句返回一个哈希。

添加'return self'作为初始化的最后一行,你就是黄金。

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

    return self
  end
end