Array of ruby ​​objects ... or hash

Now I have an object:

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

      

I am assigned it as a separate object into an array ...

@result = []

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

      

But instead of assigning my only object to an array ... how can I make the object itself a collection?

Basically, one needs to have an object in such a way that I can define methods like

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

      

I hope this makes sense, perhaps I'm missing some grand scheme that will make my life infinitely easier in two lines.

+2


source to share


4 answers


A few comments before I post an example on how to redo it.

  • Providing a multiple name for a class can lead to a lot of semantic problems when declaring new objects, since in this case you are calling Items.new, implying that you are creating multiple items when you actually create them. Use a unique shape for individual objects.
  • Be careful when calling arbitrary methods, as you will throw an exception on any miss. Either make sure you can call them first, or escape imminent disaster, where applicable.


One way to approach your problem is to create a custom collection class specifically for Item objects where it can provide you with the information you need about names etc. For example:

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

      

+6


source


Do you know what the Ruby keyword is?

I'm not really sure what exactly you want to do. I have two interpretations of your intentions, so I'll give you an example that does two completely different things, one of which hopefully answers your question:

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}

      



Outputs

mug
cup

mug
a big cup

      

Have you asked for something like Items.each or a.each or for something completely different?

+2


source


Answering only the additional question you asked in your comment on tadman's solution: if you replace in your tadman code the definition of method names in the ItemsCollection class with

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

      

For its sample data, you will get the following outputs:

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

      

As I don't know how to check if the method name comes from an attribute or from another method (other than overriding attr_accessor, attr, etc. in a class module), I added some sanity checks: the corresponding method and an instance variable of that name exist. Since the ItemsCollection does not provide that only Item objects are added, I only select items that do both checks. You can also remove the selection and put the test on the card and return zero if validation fails.

+1


source


The key is the return value. If no 'return' expression is specified, the result of the last statement is returned. You last operator returns a hash.

Add 'return self' as the last initialization line and you are golden.

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

    return self
  end
end

      

-1


source







All Articles