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.
source to share
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
source to share
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?
source to share
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.
source to share
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
source to share