From: Vsevolod (S. I. <si...@cs...> - 2004-04-25 22:29:48
|
Chris and Ray, I have made some more progress in writing the new code and even more=20 progress in thinking about it. So here are several points: 1a. The whole thing won't work properly without caching turned on.=20 Assume that an A has many X'es, and the table X has a column 'a_id'.=20 When I pass a list of ids to the function A->list_of_x_add (similar to=20 linksto_add), the application may have various copies of X'es with those=20 ids floating around, some of whose field values may have been changed.=20 Since (at least in the auto-save case) it does not make sense to just=20 update the 'a_id' field in the database and not the rest of the fields,=20 we need to save the relevant X objects. But we don't know about them=20 without caching! So, Chris, if you have a chance, please review my bug report about=20 caching not working. The description of the issue is verbose, but the=20 fix is trivial. 1b. However, even the current cache is inadequate for the task. Right=20 now, the first time an object is retrieved, it's saved in the cache. If=20 it's retrieved again, a copy of the object is returned. Thus, whoever=20 asked for the object first, has the "master" copy, meaning that=20 everybody else will see his changes. But if other requestors make=20 changes and the first requestor's copy is saved, their changes will be=20 lost. Here is a sample code that illustrates the problem. Assume that A still=20 has many X'es. my $a =3D A->fetch(1); my $x =3D A->list_of_x->[0]; my $a1 =3D $x->myA; Here, logically, $a and $a1 refer to the same object with the same ID.=20 But they are different Perl objects. If I change $a1 and save $a, my=20 changes to $a1 will be lost. Is there a reason the cache does not simply return the stored object? 1c. Normally, calling $a->list_of_x_add($x) will make sure that the=20 changes to the 'a_id' field in the X table are saved. There is a fun=20 special case, though - what if $a has been just created and not saved=20 yet? There are two possible behaviors: a) save $a behind the scenes to=20 obtain a_id, or b) throw an error requiring the user to call save()=20 explicitly. Variant a) makes list_of_x_add() behave similarly to the=20 normal case, but does something that the user may not want. Variant b),=20 conversely, exposes some inner workings of SPOPS to the user, but does=20 not do a potentially undesirable save. What is preferable here? 2. You may have noticed that I used 'has_many', not 'has_a' as Ray=20 originally suggested. I do think it's cleaner to separate them, but if=20 you insist, I will eventually roll them back into one - I just separate=20 them now for the ease of coding. 3. For the many-to-many 'links_to' case (where A has-many Bs via the=20 linking table X), Ray suggested having the configuration hash in the X=20 class, not in the A class where 'links_to' lives now. This has the added=20 benefit of adding more fields to X if necessary, but IMO also a major=20 drawback of changing the API. Why don't we try to keep the API as=20 constant as possible and leave the 'links_to' stanza in A? We can add=20 new hash keys to specify extra X fields and to create a Perl class=20 corresponding to X if necessary. 4. Ray also suggested two different APIs for the simple has_a case (an X=20 has one A). If a dependent object is autofetched, $x->myA returns an=20 instance of A. However, if the fetch is manual, $x->myA returns a_id,=20 and only $x->fetch_myA returns an actual object. Is there a reason to do=20 it differently? 5. The issue of avoiding circular saves can be addressed simply by=20 setting a certain flag after an object is saved and checking for this=20 flag each time an object is reached in the relationship graph during the=20 save. (Obviously, this will require full caching as described above.)=20 Let me know if this for some reason won't work. Simon --=20 Simon (Vsevolod ILyushchenko) si...@cs... http://www.simonf.com The unknown is honoured, the known is neglected - until all is known. The C=FA Chulaind myth |