From: Gavin Kistner Date: 2004-01-30T02:14:55+09:00 Subject: Read-Only Array Access While I understand the (very valid) reasons that my RCR [see: http://rcrchive.net/rcr/RCR/RCR201] got shot down, an underlying problem still remains for me. I want to provide access to a full collection of elements (returned as an array, or iterated through an arbitrary .each{} block), but not let the objects be modified. Is this possible? For example, assume that a House has Furniture and ArtObjects inside it. I need to be able to modify those collections independently; at any time give external objects access to read the full collection; and (the hard part) prevent the objects in the collections from being changed. class House attr_reader(:furniture,:jewelry,:furnitureValue,:jewelryValue) def initialize @furniture,@jewelry=[],[] @furnitureValue,@jewelryValue=0,0 end def addFurniture (*newPieces) @furniture.push(*newPieces) newPieces.each{ |p| @furnitureValue+=p.value } end def addJewelry (*newPieces) @jewelry.push(*newPieces) newPieces.each{ |p| @jewelryValue+=p.value } end end class Obj attr_accessor(:name,:value) def initialize(name,value) @name,@value=name,value end end myHouse = House.new myHouse.addFurniture( Obj.new('Chair',100), Obj.new('Table',200) ) myHouse.furniture.each{ |p| puts p.name } #fine myHouse.furniture.each{ |p| p.value*=2 } #uh-oh! myHouse.furnitureValue is now out of sync The problem cannot be solved by freezing @furniture, since I want to be able to modify it at any time. The problem could be sort of solved by... def furniture return @furniture.dup end ....except that (a) this provides 'silent' protection (the code can modify the values, it just doesn't do what was expected) and (b) the returned array is no longer synced with the original: foo = myHouse.furniture myHouse.addFurniture( Obj.new('Credenza',1000) ) # foo has two items, the house really has three. So...how do I solve this? The problem is that a 'read-only' copy of an array doesn't prevent code from getting a read-only reference to an element in that array, and then modifying it directly. [And as an aside...I realize that the particular problem could be solved if I either disallowed .value= or recalculated the furnitureValue by iterating the array each time it was asked for. Or I could have the furniture pieces know what house they are in, and any time they are changed have them tell the house that they were modified and any dependent information needs to be recalculated. While all are solutions to the above, I can sense that I'm going to be repeatedly bumping up against this problem...the desire to keep a collection of elements who should be able to be read, but not directly modified.]