--------------020907030505000308020306
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

(Sorry for the long delay - I first had to find my old code and polish it a bit 
before posting.)

I implemented something like a Codd relation a couple of months ago. It is no 
persistence framework and it knows nothing about SQL. It's just a way to 
structure your classes like you'd do in a relational db.

Here is the example Curt described in his original mail using my Relation module:


   def test_journals

     # Define the classes journal, volume, and article using a common base class.

     named_object  truct.new :name

     def named_object.create *names
       names.map { |name| new name }
     end

     journal  lass.new named_object
     volume  lass.new named_object
     article  lass.new named_object

     # Define the relations between the classes and the names of the accessor
     # methods (read as "a journal consists of many volumes and a volume belongs
     # to one journal").

     Relation.new journal, :many, :volumes, volume, :one, :journal
     Relation.new volume, :many, :articles, article, :many, :volumes

     # Create some instances of the classes.

     j1, j2  ournal.create "j1", "j2"
     v11, v12, v21  olume.create "v11", "v12", "v21"
     a1, a2, a3  rticle.create "a1", "a2", "a3"

     # Connect the instances. You can do so from both sides of a relation. A :one
     # link is set using "(not shown here), a :many link using "<<".

     j1.volumes << v11 << v12
     j2.volumes << v21

     v11.articles << a1 << a2
     v12.articles << a2 << a3
     v21.articles << a3

     # Now the original task: name all the journals that have published a given
     # article.

     # When you start with the article, you can access the volumes it is
     # published in. From there you can get the journals of the volumes. Finally,
     # you can apply the "name" method to the result to get the names of all the
     # journals that have published the article.

     assert_equal [ "j1" ], a1.volumes.journal.name, "a1"
     assert_equal [ "j1" ], a2.volumes.journal.name, "a2"
     assert_equal [ "j1", "j2" ], a3.volumes.journal.name, "a3"

   end


The code is very simple, not commented, and doesn't pay attention to things like 
garbage collection. But it contains unit tests. If you have any questions, feel 
free to ask.

Regards,
Pit

--------------020907030505000308020306
Content-Type: application/x-zip-compressed;
 nameelation.zip"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filenameelation.zip"

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