# Getting to one attribute of my marshalled array of objects

**URL:** <https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050>\
**Category:** Ruby\
**Created:** [September 18, 2009, 8:00pm UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050 "2009-09-18T20:00:34Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 18, 2009, 8:00pm UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/1 "2009-09-18T20:00:34Z")

</div>

I have looked around for an answer to this on the mailing list and  
Google searches, but I am not finding anything, so sorry if this  
question seems too basic but…

I am just learning Ruby, and one of my class assignments is writing a  
command line app that tracks an inventory of cars, each car having 6  
attributes: VIN, year, make, model, color, and price. We are supposed  
to use 2 objects for this exercise, a Car class and a “List of Cars”  
class. Then a menu that allows adding a car to the list, deleting a car  
from the list, and showing the list.

I could probably done this assignment as-is :)…but I wanted to go a  
little bit further. It did not seem to make sense that this app did not  
call for any persistence and once you made your inventory of cars and  
then left the app they were gone. So, armed with pickaxe, I tried to  
add file handling, but…that didn’t make sense either: since I was  
using Car objects and a CarInventory object that is just an array of Car  
objects, converting the objects all the time into a format where I could  
put them into a delimited text file and then read them out again seemed  
crazy.

Then I read farther in pickaxe (past what my class goes into,  
unfortunately), and I found Marshalling. That seemed like exactly what  
I wanted…I could just dump the CarInventory object with it’s whole  
array of Cars (into a file called cars.data in this case) and then read  
it right back in when needed.

It is almost working, too :). But, I am having a problem figuring out  
how to connect all the dots from A to D. A couple of things are  
nagging:

- The first time the app is run, there is no cars.data file, so I have  
to handle that, which I am doing now but it’s far from elegant…the app  
will decide you don’t have any car inventory, initialize the inventory  
with a Car that has all nil attributes, and ask you to enter the first  
car, then you can Save. This has some issues in that even though I  
tried to write the code to later overwrite the “nil car”, it is still  
there somehow :).

- More importantly, once I have a Saved cars.data file and I use  
Marshal.load on it, I don’t want that “enter the first car” routine to  
kick in, so I try to check the first Car object in CarInventory and see  
if its VIN number is nil or not. Somehow I am missing the boat here,  
and I am sure it’s something simple. When I try to get from Marshalled  
object -\> CarInventory instance -\> first Car object -\> first Car.vin I  
get the following:

cars.rb:106: undefined method `[]’ for #CarInventory:0x3fb97c8  
(NoMethodError)

I am including the entire app below, it’s not too big though I am sure  
it is much bigger and clunkier than it needs to be ;)…like I said I am  
jsut learning Ruby…any help would be appreciated in fixing my  
immediate and dire issues, and/or anything about making the whole app  
work more smoothly. You can ignore some of the other  
also-not-actually-required-for-this-assignment bells and whistles I have  
partially implemented for now. Sorry, I realize it’s a bit messy.

----SOURCE

class Car

def initialize(vin, year, make, model, color, price)  
@vin = vin  
@year = year  
@make = make  
@model = model  
@color = color  
@price = price  
end

def to\_s  
if !self.vin  
@displayVIN = “(No VIN)”  
else  
@displayVIN = self.vin  
end  
if !self.color  
@displayColor = “”  
else  
@displayColor = “(#@color)”  
end  
“#@displayVIN\t#@year #@make #@model #@displayColor\t#@price\n”  
end

def \<=\> (compareThisCar)  
self.year \<=\> compareThisCar.year  
end

attr\_reader :vin, :year, :make, :model, :color, :price  
attr\_writer :vin, :year, :make, :model, :color, :price

end

class CarInventory

@@inventoryFile = “cars.data”

def initialize  
if File.exists?(@@inventoryFile)  
File.open(@@inventoryFile) do |file|  
@carArray = Marshal.load(file)  
end  
else  
@carArray = Array.new  
@carArray[0] = Car.new(nil, nil, nil, nil, nil, nil)  
end  
end

def save  
File.open(@@inventoryFile, “w+”) do |file|  
Marshal.dump(self, file)  
end  
end

def addCar(car)  
if car.vin == nil  
@carArray[0] = car  
else  
@carArray \<\< car  
end  
end

def updateCar(car)  
updateIndex = @carArray.find {|vinMatch| car.vin == vinMatch}  
if updateIndex  
@carArray[updateIndex] = car  
end  
end

def delCar(car)  
@carArray = @carArray - car  
end

def to\_s  
@carArray.each {|car| car.to\_s if car.vin}  
end

def sort(sortBy)  
case sortBy  
when “V”:  
when “M”:  
when “P”:  
when “Y”:  
else  
end  
end

def filter(filterArray)

end

end

puts “Car Inventory 1.0\n”

cars = CarInventory.new  
firstCar = cars.carArray  
firstCar = firstCar[0]  
if !firstCar.vin  
puts “\nNo car inventory found, please enter the first car:”  
puts “\nVIN:”  
vin = gets.chomp  
puts “\nYear:”  
year = gets.chomp  
puts “\nMake:”  
make = gets.chomp  
puts “\nModel:”  
model = gets.chomp  
puts “\nColor:”  
color = gets.chomp  
puts “\nPrice:”  
price = gets.chomp  
newCar = Car.new(vin, year, make, model, color, price)  
cars.addCar(newCar)  
else  
cars.sort(“V”)  
cars.filter(nil)  
end

loop do  
puts “\nVIN#\t\tCar:\t\t\tPrice:\n”  
puts “------------------------------------------------\n”  
puts cars.to\_s  
puts “------------------------------------------------\n”  
puts “(A)dd a new car\n”  
puts “(E)dit a car\n”  
puts “(D)elete a car\n”  
puts “(L)ist cars by VIN\n”  
puts " by (M)ake and Model\n"  
puts " by §rice\n"  
puts " by (Y)ear\n"  
puts “(S)ave changes\n”  
puts “(Q)uit\n”  
puts "\nCommand: "  
command = gets.chomp.upcase  
case  
when command =~ /^A/ : puts “\nAdd a new car:\n”  
puts “\nVIN:”  
vin = gets.chomp  
puts “\nYear:”  
year = gets.chomp  
puts “\nMake:”  
make = gets.chomp  
puts “\nModel:”  
model = gets.chomp  
puts “\nColor:”  
color = gets.chomp  
puts “\nPrice:”  
price = gets.chomp  
newCar = Car.new(vin, year, make, model, color, price)  
cars.addCar(newCar)  
when command =~ /^E/ : puts “\nEdit a car:\n”  
when command =~ /^D/ : puts “\nDelete a car:\n”  
when command =~ /^L/ : puts “\nList car inventory by VIN:\n”  
when command =~ /^M/ : puts “\nList car inventory by Make and  
Model:\n”  
when command =~ /^P/ : puts “\nList car inventory by Price range:\n”  
when command =~ /^Y/ : puts “\nList car inventory by Year\n”  
when command =~ /^S/ : cars.save  
when command =~ /^Q/ : puts “Are you sure? [Y/N]”  
confirm = gets.chomp.upcase  
exit if confirm =~ /^Y/  
next  
else puts “Illegal Command: #{command}”  
end  
end

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 18, 2009, 9:12pm UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/2 "2009-09-18T21:12:13Z")

</div>

As a little off topic:

You should also read about YAML:  
[http://www.ruby-doc.org/core/classes/YAML.html](http://www.ruby-doc.org/core/classes/YAML.html).

I don’t really know about marshalling since i serialize all my data  
with YAML. It’s human readable and automatically handles  
dumping and loading your ruby objects.  
All you need is:

```
c = Car.new
dumped = c.to_yaml
c = file.open('dumpfile') { |f| YAML::load(f) }

```

Greetz!

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 19, 2009, 1:19am UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/3 "2009-09-19T01:19:37Z")

</div>

Fabian S. wrote:

> You should also read about YAML:  
> [http://www.ruby-doc.org/core/classes/YAML.html](http://www.ruby-doc.org/core/classes/YAML.html).

Thanks, I did look at YAML which has about a page in pickaxe like  
standard Marshalling, and I might use it later but for this assignment I  
just want the simplest way to reliably write and read my object  
(containing an array of objects) and read it back. The marshalling  
_seems_ to be working in that when I open the cars.data file it looks  
like it has data on the child Car objects, but I can’t tell for sure yet  
until I actually figure out how to get all the way to my Car attributes  
:).

Essentially I want a statement that acts like this:

cars = CarInventory.new # handle for object which is array of Cars  
if !cars.car[0].vin # grab the first car from CarInventory and check if  
the VIN is nil

That second statement does not fly and no matter how I deconstruct it I  
can’t seem to get at the array that CarInventory represents here.

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 19, 2009, 2:52am UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/4 "2009-09-19T02:52:17Z")

</div>

One other thing:

> > def initialize  
> > if File.exists?(@@inventoryFile)  
> > File.open(@@inventoryFile) do |file|  
> > @carArray = Marshal.load(file)  
> > end  
> > else  
> > @carArray = Array.new  
> > @carArray[0] = Car.new(nil, nil, nil, nil, nil, nil)

I would delete the above line, it is not needed. You should use the  
fact  
that the array itself is empty to denote there are no cars in the  
inventory,  
not a fictional marker car with nil VIN.

-Doug Seifert

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 19, 2009, 2:49am UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/5 "2009-09-19T02:49:21Z")

</div>

I won’t comment on your design as it seems straightforward enough. It  
could  
use some rethinking, but with time and experience you will come up with  
better designs. If you want some pointers let me know …

Some issues with the implementation though, my comments below …

> class Car
> 
> attr\_reader :vin, :year, :make, :model, :color, :price  
> attr\_writer :vin, :year, :make, :model, :color, :price
> 
> The above can be shorted to one line:

attr\_accessor :vin, :year, :make, :model, :color, :price

> ```
> end
> 
> ```
> 
> else  
> @carArray = Array.new  
> @carArray[0] = Car.new(nil, nil, nil, nil, nil, nil)  
> end  
> end
> 
> def save  
> File.open(@@inventoryFile, “w+”) do |file|  
> Marshal.dump(self, file)

Here you are dumping the CarInventory (self) object to the file. What  
you  
really want to do is dump the @carArray to the file. This is because  
above  
in the initialize method you are trying to read the @carArray from the  
file. This is the source of the undefined method `[]’ error you are  
getting  
when you run the program after saving.

> end  
> end
> 
> def addCar(car)  
> if car.vin == nil  
> @carArray[0] = car  
> else  
> @carArray \<\< car  
> end

Not sure what you are trying to do with this implementation. I would  
change  
it to simply

@carArray \<\< car

> end
> 
> def updateCar(car)  
> updateIndex = @carArray.find {|vinMatch| car.vin == vinMatch}  
> if updateIndex  
> @carArray[updateIndex] = car  
> end

# This will not have the effect you want. I think you will have to define

on the Car class and implement it use vin as the equality check:

class Car  
…  
def ==(other)  
self.vin == other.vin  
end  
…  
end

Then, the updateCar method of the CarInventory class could be something  
like:

def updateCar(car)  
i = @carArray.index(car)  
if i  
@carArray[i] = car  
else  
# TODO: Print an error message? Raise an exception?  
end  
end

end

> def delCar(car)  
> @carArray = @carArray - car

Here, you need  
@carArray = @carArray - [car]

and it will only work if you define Car#== as above. The Array#- method  
only works on two Array objects. You can’t use it to remove an object.  
Alternatively, you could use Array#delete:

def delCar(car)  
@carArray.delete(car)  
end

> puts “Car Inventory 1.0\n”
> 
> cars = CarInventory.new  
> firstCar = cars.carArray  
> firstCar = firstCar[0]

I would change the above two lines to simply  
firstCar = cars.carArray.first

> if !firstCar.vin

Then instead of looking for a nil vin, just check if firstCar itself is  
nil:

if !firstCar

> color = gets.chomp  
> puts “\nPrice:”  
> price = gets.chomp  
> newCar = Car.new(vin, year, make, model, color, price)  
> cars.addCar(newCar)  
> else  
> cars.sort(“V”)  
> cars.filter(nil)  
> end

But really, I would just get rid of the entire if/else statment above  
and go  
directly into the loop below it. The initial logic is not DRY and  
doesn’t  
add anything the loop doesn’t already give you.

Hope that helps,  
Doug Seifert

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 19, 2009, 9:18pm UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/6 "2009-09-19T21:18:24Z")

</div>

Thanks Doug,

I implemented all those changes. I guess I just went too far down the  
wrong road with trying to handle the empty inventory, so I removed that  
whole mechanic. Part of that was that I tried to marshal @carArray  
originally but messed it up somehow (it only dumped the object ID or  
something), and that’s when I tried to marshal self instead and it  
worked. Not sure what I did there since when I tried again with  
@carArray it worked fine.

I am having another issue, though. In order to delete a car, I need to  
number the inventory when it displays so that I can ask the user to tell  
me which car to delete by its number on the list (this is part of the  
assignment so I can’t really do it another way).

At first I thought I would modify the to\_s on CarInventory which right  
now just iterates through the array of Cars using .each and calls  
Cars.to\_s, but then I decided it would be more robust if Cars.to\_s was  
aware of whether it was being called repeatedly as part of a listing or  
not, from anywhere.

So I added a parameter “def to\_s(listPosition=0)” to Car.to\_s and  
changed the .each call in the CarInventory.to\_s to pass a position  
number. When I run the progrma now though I get a really bizarre  
behavior…I was supposed to get this type of output with 2 cars in my  
data file (simplified because I am also formatting it to comform to  
columns, etc.):

[1] VIN# Year Make Model Color Price  
[2] …

Instead I got the same results without any bracketed list numbers…so I  
added a debug counter and a puts of the relevent part of my string  
output, and I got this:

Called 1 times  
[1]  
Called 2 times  
[2]  
Called 3 times  
nil  
VIN# […]  
Called 4 times  
nil  
VIN# […]

Which is where I am stuck…after adding the parameter Ruby appears to  
be calling to\_s 4 times now instead of 2 times and I have no idea why.  
The first 2 times it gets the first part of the string I am building set  
up correctly, but loses everything else and the second 2 times it does  
the whole rest of the string build correctly but ignores the first part.  
I have tried everything I cna thinnk of to display the strings invloved  
at every stage/state and it seems ok…I just can’t figure out why the  
to\_s is calling four times when it should be twice.

Source below (you can run it, add a car or two, then uncomment the DEBUG  
lines to see what happens):

----SOURCE

class Car

# DEBUG: @@debugCounter = 0

def initialize(vin, year, make, model, color, price)  
@vin = vin  
@year = year  
@make = make  
@model = model  
@color = color  
@price = price  
end

def to\_s(listPosition=0) # allow for a listing position to be passed  
# DEBUG: @@debugCounter = @@debugCounter + 1  
# DEBUG: puts “Called #@@debugCounter times”  
displayString = “”  
80.times {displayString \<\< " "} # lay down a canvas of spaces to  
build the full displayString  
if !self.vin  
displayVIN = “(No VIN)”  
else  
displayVIN = self.vin  
end  
if !self.color  
displayColor = “”  
else  
displayColor = “(#@color)”  
end  
# DEBUG: debugString = “[” + listPosition.to\_s + “]” if listPosition  
!= 0  
# DEBUG: puts debugString  
displayString[0, 0] = “[” + listPosition.to\_s + “]” if listPosition  
!= 0  
displayString[6, 0] = displayVIN  
displayString[25, 0] = @year + " " + @make + " " + @model + " " +  
displayColor  
displayString[70, 0] = @price  
displayString = displayString.strip # make the format look like a  
“normal” to\_s call if passed no list position  
end

def \<=\> (compare)  
self.year \<=\> compare.year  
end

attr\_accessor :vin, :year, :make, :model, :color, :price

end

class CarInventory

@@inventoryFile = “cars.data”

def initialize  
if File.exists?(@@inventoryFile)  
File.open(@@inventoryFile) do |file|  
@carArray = Marshal.load(file)  
end  
else  
@carArray = Array.new  
end  
end

def save  
File.open(@@inventoryFile, “w+”) do |file|  
Marshal.dump(@carArray, file)  
end  
end

def addCar(car)  
@carArray \<\< car  
end

def updateCar(car)  
updateIndex = @carArray.index(car)  
if updateIndex  
@carArray[updateIndex] = car  
else  
puts “Cannot find car #car.vin\n”  
end  
end

def delCar(car)  
@carArray.delete(car)  
end

def to\_s  
if @carArray.first  
@carArray.each {|car| car.to\_s(@carArray.index(car) + 1)}  
else  
return “(No inventory)”  
end  
end

def ==(compare)  
self.vin == compare.vin  
end

def sort(sortBy)  
case sortBy  
when “V”:  
when “M”:  
when “P”:  
when “Y”:  
else  
end  
end

def filter(filterArray)

end

attr\_reader :carArray, :inventoryFile

end

puts “Car Inventory 1.0 - Brian T.\n”

cars = CarInventory.new

loop do  
puts “\n# VIN Car Description  
Price\n”  
puts  
“----------------------------------------------------------------------------\n”  
puts cars.to\_s  
puts  
“----------------------------------------------------------------------------\n”  
puts “(A)dd a new car\n”  
puts “(E)dit a car\n”  
puts “(D)elete a car\n”  
puts “(S)ave changes\n”  
puts “(Q)uit\n”  
puts "\nCommand: "  
command = gets.chomp.upcase  
case  
when command =~ /^A/ :  
puts “\nAdd a new car:\n”  
puts “\nVIN:”  
vin = gets.chomp  
puts “\nYear:”  
year = gets.chomp  
puts “\nMake:”  
make = gets.chomp  
puts “\nModel:”  
model = gets.chomp  
puts “\nColor:”  
color = gets.chomp  
puts “\nPrice:”  
price = gets.chomp  
newCar = Car.new(vin, year, make, model, color, price)  
cars.addCar(newCar)  
when command =~ /^E/ :  
puts “\nEdit a car:\n”  
when command =~ /^D/ :  
puts “\nDelete a car:\n”  
when command =~ /^S/ :  
cars.save  
puts “\nSaved to #cars.inventoryFile\n”  
when command =~ /^Q/ :  
puts “Are you sure? [Y/N]”  
confirm = gets.chomp.upcase  
exit if confirm =~ /^Y/  
next  
else puts “Illegal Command: #{command}”  
end  
end

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 20, 2009, 2:07pm UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/7 "2009-09-20T14:07:35Z")

</div>

Brian,

I am having another issue, though. In order to delete a car, I need to

> number the inventory when it displays so that I can ask the user to tell  
> me which car to delete by its number on the list (this is part of the  
> assignment so I can’t really do it another way).
> 
> At first I thought I would modify the to\_s on CarInventory which right  
> now just iterates through the array of Cars using .each and calls  
> Cars.to\_s, but then I decided it would be more robust if Cars.to\_s was  
> aware of whether it was being called repeatedly as part of a listing or  
> not, from anywhere.

I would not go down this road. A car should not care if it is being  
displayed as part of a listing of other cars or if it is alone. It  
should  
just know how to display itself. In addition, adding a parameter to  
to\_s is  
bad ruby style: to\_s has well-defined semantics in the ruby language. I  
suggest before you go any further, you pick up a copy of “Programming  
Ruby”. If you can’t afford the money, the first edition is available  
for  
free on line: [http://www.rubycentral.com/book/](http://www.rubycentral.com/book/). The book does a far  
better  
job than I ever could of explaining how the to\_s method works in the  
Ruby  
world.

> So I added a parameter “def to\_s(listPosition=0)” to Car.to\_s and  
> changed the .each call in the CarInventory.to\_s to pass a position  
> number. When I run the progrma now though I get a really bizarre  
> behavior…I was supposed to get this type of output with 2 cars in my  
> data file (simplified because I am also formatting it to comform to  
> columns, etc.):

After looking at your implementations, I see what you are trying to  
accomplish. A better idea in my opinion would be to leave the counting  
logic to the CarInventory class. It is the class that knows about a  
list of  
cars.

I noticed also that you tried to put some logic in to line up the car  
attributes in a nice column layout. You are reinventing the wheel here,  
unfortunately. One thing you should look up right away is is the %  
method  
of class String:

## $ ri String#% --------------------------------------------------------------- String#% str % arg =\> new\_str

```
 Format---Uses _str_ as a format specification, and returns the
 result of applying it to _arg_. If the format specification
 contains more than one substitution, then _arg_ must be an +Array+
 containing the values to be substituted. See +Kernel::sprintf+ for
 details of the format string.

    "%05d" % 123 #=> "00123"
    "%-5s: %08x" % ["ID", self.id] #=> "ID : 200e14d6"

```

You should do an ri Kernel#sprintf to see all the good stuff that can go  
in  
the format string. If you are familiar with c/c++ at all, this will be  
familiar to you.

I’m not sure also in your original to\_s method on the CarInventory class  
how  
it ever worked. The each method of class Array returns the Array  
itself.  
Your to\_s method would end up returning an array of Car objects and it  
might  
have been pure accident that it looked ok in your command loop.

Here is a revised to\_s for the CarInventory class. It handles the  
indexing  
problem and makes sure it returns a String object in all cases. I would  
implement Car#to\_s without a parameter and use the formatting methods  
above  
to get a nice column layout

def to\_s  
if @carArray.first  
counter = 0  
@carArray.map {|car| counter += 1; “[%3d] %s” % ]counter,  
car.to\_s]}.join(“\n”)  
else  
return “(No inventory)”  
end  
end

I am not sure how a beginner goes about accumulating the knowlege  
necessary  
to make implementing this method easy. There is a lot of knowledge  
bundled  
up in this implementation of to\_s: use of Array#map and Array#join.  
Knowledge of String#% and by implication, Kernel#sprintf. I have done a  
lot  
of reading I guess.

This whole thread is reminding me of a recent blog post by Jamis B., a  
guy  
who I think it is fair to say is a Ruby community luminary:

[http://weblog.jamisbuck.org/2009/9/17/there-is-no-magic-there-is-only-awesome-part-1](http://weblog.jamisbuck.org/2009/9/17/there-is-no-magic-there-is-only-awesome-part-1)

If you ever want to get good with Ruby, you need to apply his four  
rules:

1. _Know thy tools._
2. _Know thy languages._
3. _Know thy libraries._
4. _Know thy communities._

Applying these rules, you would know about the % method of the String  
class  
and about Kernel#sprintf and Array#map and Array#join.

So keep studying! It is a high hill to climb, but once you get over the  
top, your programs will reach the level of conciseness that Ruby is  
famous  
for.

-Doug Seifert

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 22, 2009, 4:57am UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/8 "2009-09-22T04:57:23Z")

</div>

Douglas S. wrote:

> I would not go down this road. A car should not care if it is being  
> displayed as part of a listing of other cars or if it is alone. It  
> should  
> just know how to display itself. In addition, adding a parameter to  
> to\_s is  
> bad ruby style: to\_s has well-defined semantics in the ruby language.

Ok, I will stay away from that. I normally stay pretty hands off on  
things like that, but I have seen a lot of examples of modifying really  
important things like Object and Integer in Ruby, so I thought it was  
just more normal to do this and that because it’s interpreted it would  
not be permanent. I did try to leave the functioning of to\_s untouched  
if the parameter was not passed by defaulting the parameter and making  
sure the output when not in a list was as expected, but maybe that only  
works with the \*p array method? Anyway, I see the point, so I took it  
out.

> I  
> suggest before you go any further, you pick up a copy of “Programming  
> Ruby”. If you can’t afford the money, the first edition is available  
> for  
> free on line: [http://www.rubycentral.com/book/](http://www.rubycentral.com/book/). The book does a far  
> better  
> job than I ever could of explaining how the to\_s method works in the  
> Ruby  
> world.

I do have it, and I have read the entire book ;). That’s how I deciding  
on marshalling, etc. I will say the book is great, but if you don’t  
have a C background or something similar (I do, but only at a basic  
level and dating from the 1980s ;)…), it’s hard to figure some things  
out. I ended up googling a lot of stuff to get sample code that I could  
understand readily for things that are not covered in depth.

> After looking at your implementations, I see what you are trying to  
> accomplish. A better idea in my opinion would be to leave the counting  
> logic to the CarInventory class. It is the class that knows about a  
> list of  
> cars.

Fixed.

> I noticed also that you tried to put some logic in to line up the car  
> attributes in a nice column layout. You are reinventing the wheel here,  
> unfortunately. One thing you should look up right away is is the %  
> method  
> of class String:  
> [snip…]  
> You should do an ri Kernel#sprintf to see all the good stuff that can go  
> in  
> the format string. If you are familiar with c/c++ at all, this will be  
> familiar to you.

I remembered this from C after you talked about it. Also fixed now, but  
I did end up having to Google around to find an example since the ones  
in the book don’t cover many of hte possible scenarios/uses for this.

> I’m not sure also in your original to\_s method on the CarInventory class  
> how  
> it ever worked. The each method of class Array returns the Array  
> itself.  
> Your to\_s method would end up returning an array of Car objects and it  
> might  
> have been pure accident that it looked ok in your command loop.

Hmmm…ok, I am confused on this one. I thought the whole point of  
.each was to iterate through the elements of arrays and hashes and  
perform actions on each one…like a for or while loop without the fuss  
and muss of counters, etc.

> Here is a revised to\_s for the CarInventory class. It handles the  
> indexing  
> problem and makes sure it returns a String object in all cases. I would  
> implement Car#to\_s without a parameter and use the formatting methods  
> above  
> to get a nice column layout
> 
> def to\_s  
> if @carArray.first  
> counter = 0  
> @carArray.map {|car| counter += 1; “[%3d] %s” % ]counter,  
> car.to\_s]}.join(“\n”)  
> else  
> return “(No inventory)”  
> end  
> end

Done, as you’ll see below.

> I am not sure how a beginner goes about accumulating the knowlege  
> necessary  
> to make implementing this method easy. There is a lot of knowledge  
> bundled  
> up in this implementation of to\_s: use of Array#map and Array#join.  
> Knowledge of String#% and by implication, Kernel#sprintf. I have done a  
> lot  
> of reading I guess.

One of the issues I am having in looking up what I need is that there’s  
no master hierarchy/chart/graph of Ruby objects _and_ mixins together in  
the book…and in this case, with Array, map is actually from the  
Enumerable mixin, although there is a connect! (which I guess is the  
same as map); but as a neophyte I was generally staying away from any  
commands that modify objects in-place and so I did not really look  
closely at what connect! was. Once I saw your code I was able to figure  
it out, though.

> This whole thread is reminding me of a recent blog post by Jamis B., a  
> guy  
> who I think it is fair to say is a Ruby community luminary:
> 
> [http://weblog.jamisbuck.org/2009/9/17/there-is-no-magic-there-is-only-awesome-part-1](http://weblog.jamisbuck.org/2009/9/17/there-is-no-magic-there-is-only-awesome-part-1)

I read this and I like the ideas. Hopefully the next installment is  
coming soon.

> If you ever want to get good with Ruby, you need to apply his four  
> rules:
> 
> 1. _Know thy tools._
> 2. _Know thy languages._
> 3. _Know thy libraries._
> 4. _Know thy communities._
> 
> Applying these rules, you would know about the % method of the String  
> class  
> and about Kernel#sprintf and Array#map and Array#join.

Well, I have to be honest here, I will never be a master of Ruby. I am  
a jack-of-all-trades in general, plus I left my programming days behind  
me long ago. I am evaluating a bunch of languages/technologies right  
now for my website/garage startup (I am also taking more classes…in  
PHP, Python, C#, Ajax, Perl, and I have been going to Drupal meetups  
since I did not see any classes on it). Right now, I like Ruby the best  
(more of a gut feeling than anything else), and I have not even looked  
at Rails yet ;). I have not really touched the .NET side of the house  
yet, though, to be fair.

Once I do have my chosen technology, though, I will probably end up  
having to code the first mockup of the site myself, so I do need to know  
as much as I can.

I want to thank you for all your help, Doug, it was very much  
appreciated. You mentioned in an earlier post that if I wanted to know  
how to design this app better you could give me some tips…up to you,  
but I am all ears. Frankly, when I have been doing these Ruby  
assignments, they seem more like generic “learn this language’s syntax”  
exercises and don’t seem to be asking me to do things that will show me  
the power of Ruby.

> So keep studying! It is a high hill to climb, but once you get over the  
> top, your programs will reach the level of conciseness that Ruby is  
> famous  
> for.

I have finishing pickaxe/Programming Ruby, and I also read a certain  
Ruby guide involving chunky bacon. I pretty much had to, my class is  
online-only and I have had to learn everything on my own.

As far as conciseness goes, I’m sure this does not qualify…but here’s  
the final source for my assignment (in case anyone is looking at this  
thread for answers somewhere down the road and needs to see it):

----SOURCE

# Car Inventory 1.0

# Brian T.

# 9/2009

INVENTORY\_FILE = “cars.data”

class Car

def initialize(vin, year, make, model, color, price)  
@vin = vin  
@year = year  
@make = make  
@model = model  
@color = color  
@price = price  
end

def to\_s  
if !self.vin or self.vin == “”  
displayVIN = “(No VIN)”  
else  
displayVIN = self.vin  
end  
if !self.color or self.color == “”  
displayColor = “”  
else  
displayColor = “(#{self.color})”  
end  
displayString = “%-20s %-40s %10s” % [  
displayVIN,  
(@year + " " + @make + " " + @model + " " + displayColor),  
(“$” + @price.to\_s) ]  
end

def \<=\> (compare)  
self.year \<=\> compare.year  
end

attr\_accessor :vin, :year, :make, :model, :color, :price

end

class CarInventory

def initialize  
if File.exists?(INVENTORY\_FILE)  
File.open(INVENTORY\_FILE) do |file|  
@carArray = Marshal.load(file)  
end  
else  
@carArray = Array.new  
end  
end

def save  
File.open(INVENTORY\_FILE, “w+”) do |file|  
Marshal.dump(@carArray, file)  
end  
end

def addCar(car)  
@carArray \<\< car  
end

def updateCar(car)  
updateIndex = @carArray.index(car)  
if updateIndex  
@carArray[updateIndex] = car  
else  
puts “Error: Cannot find car #{car.vin}\n”  
end  
end

def deleteCar(car)  
@carArray.delete(car)  
end

def getCar(listPosition)  
@carArray[listPosition - 1]  
end

def to\_s  
if @carArray.first  
displayString = “”  
counter = 0  
priceTotal = 0  
displayString \<\< “\n# VIN Car Description  
Price\n”  
displayString \<\<  
“------------------------------------------------------------------------------\n”  
displayString \<\< @carArray.map {|car| counter += 1 ;   
priceTotal += car.price.to\_i;   
“[%3d] %s” % [counter, car.to\_s]}.join(“\n”)  
displayString \<\<  
“\n------------------------------------------------------------------------------\n”  
displayString \<\< “%78s” % (“Total Inventory Value: $” +  
priceTotal.to\_s)  
else  
return “(No inventory)”  
end  
end

def ==(compare)  
self.vin == compare.vin  
end

end

def inputValue(inputLabel, inputRegexp, inputWarningString,  
allowBlankValue=false)  
loop do  
puts inputLabel  
input = gets.chomp  
return “” if allowBlankValue and input == “”  
if input =~ inputRegexp  
return input  
else  
puts inputWarningString  
end  
end  
end

puts “Car Inventory 1.0 - Brian T.\n”

cars = CarInventory.new

loop do  
puts cars.to\_s  
puts “(A)dd a new car\n”  
puts “(E)dit a car\n”  
puts “(D)elete a car\n”  
puts “(S)ave changes\n”  
puts “(Q)uit\n”  
puts “\nCommand: "  
command = gets.chomp.upcase  
case  
when command =~ /^A/ :  
puts “\nAdd a new car:\n”  
vin = inputValue(”\nVIN:“, /\w{3,17}/, “Please enter a VIN or  
identifier that is 3-17 alphanumeric characters.”)  
year = inputValue(”\nYear:“, /^[12][90]\d{2}$/, “Please enter a  
year between 1901-2099.”)  
puts “\nMake:”  
make = gets.chomp  
puts “\nModel:”  
model = gets.chomp  
puts “\nColor:”  
color = gets.chomp  
price = inputValue(”\nPrice:“, /^\d\*$/, “Please enter an integer  
(round to the nearest dollar).”)  
newCar = Car.new(vin, year, make, model, color, price)  
cars.addCar(newCar)  
when command =~ /^E/ :  
puts “\nEdit a car:\n”  
puts “\nEnter the number of the car to be edited:”  
edit\_selection = gets.chomp.to\_i  
editCar = cars.getCar(edit\_selection)  
puts “\nCar: " + editCar.to\_s  
puts “(leave fields blank if you want them to keep their current  
values)”  
if editCar  
vin = inputValue(”\nVIN:”, /\w{3,17}/, “Please enter a VIN or  
identifier that is 3-17 alphanumeric characters.”, true)  
year = inputValue(“\nYear:”, /^[12][90]\d{2}$/, “Please enter a  
year between 1901-2099.”, true)  
puts “\nMake:”  
make = gets.chomp  
puts “\nModel:”  
model = gets.chomp  
puts “\nColor:”  
color = gets.chomp  
price = inputValue(“\nPrice:”, /^\d\*$/, “Please enter an integer  
(round to the nearest dollar).”, true)  
puts “\nUpdate VIN# #{editCar.vin}? [Y/N]”  
confirm = gets.chomp.upcase  
if confirm =~ /^Y/  
editCar.vin = vin if vin != “”  
editCar.year = year if year != “”  
editCar.make = make if make != “”  
editCar.model = model if model != “”  
editCar.color = color if color != “”  
editCar.price = price if price != “”  
end  
else  
puts “Cannot access car #” + edit\_selection.to\_s  
end  
when command =~ /^D/ :  
puts “\nDelete a car:\n”  
puts “\nEnter the number of the car to be deleted:”  
delete\_selection = gets.chomp.to\_i  
delCar = cars.getCar(delete\_selection)  
puts "\nCar: " + delCar.to\_s  
if delCar  
puts “\nDelete VIN# #{delCar.vin}? [Y/N]”  
confirm = gets.chomp.upcase  
cars.deleteCar(delCar) if confirm =~ /^Y/  
else  
puts “Cannot access car #” + delete\_selection.to\_s  
end  
when command =~ /^S/ :  
cars.save  
puts “\nSaved to #{INVENTORY\_FILE}\n”  
when command =~ /^Q/ :  
puts “Are you sure? [Y/N]”  
confirm = gets.chomp.upcase  
exit if confirm =~ /^Y/  
next  
else puts “Illegal Command: #{command}”  
end  
end

---

<div class="post-metadata">

**Author:** ![bct](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/6a8cbe/32.png) [@bct](https://www.ruby-forum.com/u/bct)\
**Post date:** [September 22, 2009, 6:47pm UTC](https://www.ruby-forum.com/t/getting-to-one-attribute-of-my-marshalled-array-of-objects/226050/9 "2009-09-22T18:47:21Z")

</div>

> 

That is correct. But unlike a C-style for or while loop, a call to  
#each()  
does  
have a return value. And again unlike C, a function always returns its  
last  
value, e.g:

```
def foo
    5
end
foo() # returns 5

```

And since #each() returns an array and the call to #each() was the  
last thing in the #to\_s() method, #to\_s() would just return what  
#each() returned – an array.

What you wanted was probably #inject(). It’s a very powerful method  
that let’s you do all kinds of neat things. See [1](http://www.ruby-doc.org/core/classes/Enumerable.html#M003140) and [2](http://blog.jayfields.com/2008/03/ruby-inject.html)

Greetz!
