From: Vsevolod (S. I. <si...@cs...> - 2004-11-07 19:15:31
|
Ray, I've mulled over your comments, and I think I'm starting to see the light. :) I've looked at other OOP frameworks (Alzabo, Tangram, Hibernate, XORM), and they all seem to define the relationship in the class that has the ID field which refers to the other class. I continue the discussion of cache and the 'manual' keyword below, but when we converge to an opinion on those, I'll redo the code your way. It's less work than it seems. However, Chris has not replied yet. I'll ask him again about the compatibility issue. However, if I simply extend the has_a syntax the way you propose and smartly determine whether the old or the new syntax is used (I do it anyway now), it should not be a problem. > I assume that's why you changed the key values in the 'has_a' spec to > the class name instead of the field name? But isn't this a problem if > you have multiple has_a fields of the same class, with different > behavior for fetching/saving/removing? Or do you just use an arrayref of > hashrefs instead of the single hashref in that case? Blech. I agree, it's detestable. > And you say this full cache is necessary for the consistency circular > references. You mean to avoid infinite loops when you have A set to > auto-fetch B which is set to auto-fetch A? Seems to me that you should > be able to detect this type of thing when the classes are configured. > While I think a cache is a nice option that I may very well use, I don't > think it should be mandatory unless it's absolutely necessary. Can you > give me an example where consistency makes it absolutely necessary? For example, you have a Book object with many Authors. If the application loads a book with the list of authors, adds another author to this book and asks the new author about its parent book, the current SPOPS implementation will re-fetch the book object, potentially ignoring the changes that were made to original book object. However, I only now realized that you suggest saving both the parent-to-child reference and the reverse reference in the object fields. (I distinguish between $author->{book_id}, a number, and $author->{book}, an object. Let me know if I understood this correctly.) Thus, once $book->{list_of_authors} is populated, adding a new author to the book should add the new object to this list, plus it should set the field $author->{book} to the original book object. This will make the above situation impossible. However, inconsistencies still may occur if: 1) I create a new author-book relationship by setting the field $author->{book_id} instead of saying $book->add_author($author). This can be discouraged as an incorrect way of altering data, of course, but logically both make sense, and I'd like to be able to use them both. Or, 2) if I am working with a second relationship, say books-to-artists (illustrators). In this case, in one place in my code, I could retrieve a book object by saying $artist->book, and then in another place I'll call $author->book, and even though they may refer to the same book, they will always be two different objects. So looks like cache is still necessary. > I've used only application level caching, never SPOPS-level caching so > I'm not clear on how this works. Does this mean that SPOPS objects no > longer go out of scope and get destroyed until the program ends or the > cache is manually flushed? In a mod_perl environment then, do you flush > the cache at the end of every request or what? Seems like you could get > some huge apache children pretty quickly if you're not careful. It has to be flushed, of course. > It was for completeness and to offer a mode that is equivalent to > current has_a behavior, that is, the field normally just gives you an > id, but you also have a convenience method for fetching the object as > well. My idea was that any 'has_a' spec, including 'manual', would > create convenience methods for fetching the related objects. The 'auto' > and 'lazy' options would simply call these methods automatically at the > appropriate time and stash the return values in the object. So in the > way I was picturing things, implementing 'manual' would simply be the > first step in implementing 'auto' and 'lazy'. I feel dumb - I still don't quite get it. However, in your original examples the method X->myA returns the id of A in the case of manual fetch and A itself in the case of lazy/auto fetch, right? In my view, X->myA always return the id and X->fetch_myA always returns the object (I tend to use them like $author->book_id and $author->book in my applications). So there is no need for manual fetching. I think that having X->myA return inconsistent values may be confusing. Let me know what you think. Perhaps I am still missing the utility of manual fetching. > Without the manual option, you can't specify a relationship at all > without having it define auto-fetching behavior. You can't, for example, > auto-remove an object without having it also auto-fetched (which I can > imagine you might want if you typically only need to deal with the ID of > the secondary object). But in this case you still have to fetch the dependent object, because it may define its own rules of auto-removal of even more objects. > Just curious, does your implementation of 'auto' generate a public > 'fetch_myA' method, for example? See above - even if the fetch method name ('alias' in the current terminology') is not specified in the configuration, it'll be auto-created by using the name given to the target class. (I mean the name of config hash key for the target class, not its Perl name. In the example I sent you, X_alias is such a name.) >> Autosaving is always off by default, to preserve compatibility. > > > I'm not sure I follow. Auto-fetching is new so there is no previous > corresponding save behavior to be backward compatible with. Classes > defined without any auto-fetch/auto-remove behavior, could behave as > always. Classes defining new auto behavior could have whatever default > 'save' behavior we think makes sense. So I'm not sure there is a > backward compatibility issue here. > And the save behavior described in my updated proposal posted 4 Jan 2002 > still seems to be the most consistent and make the most sense to me. Yes, if we change the syntax, we will be free to follow you rules. >> OTOH, there are three types of removes - 'auto', 'manual' and >> 'forget'. 'Auto' means complete removal of dependent objects, 'forget' >> - nullifying id fields pointing to the removed objects, and 'manual' - >> no action. The default should logically be 'forget', but it may >> conflict with no autosaving, so I'll have to set it to 'manual'. > > > OK, but what is the 'reverse_remove'? Is specifying 'reverse_remove' => > 'forget' in a 'has_a' the same as specifying 'remove' => 'forget' in the > corresponding 'has_many'? If so, which one takes precedence if they are > inconsistent? It looks like 'reverse_remove' => 'forget' is equvalent to > what I called 'null_by', right?. I personally think that having multiple > (and possibly conflicting) ways/places of defining the behavior for a > single relationship is asking for trouble. I think it will make it > difficult to write correct and clear documentation and it will create > some debugging nightmares. (More on this below) This should not be a problem, because in my current proposal the programmer specifies either has_a or has_many (which implies the reverse has_a), so no conflicts should be possible. However, if we change the syntax, this issue will go away. > Why do you include both 'link_class' and 'link_class_alias'? Aren't they > redundant? (see [1] below). 'Link_class' refers to the Perl class name, 'link_class_alias' - to the method name used to retrieve its instances (this is your 'list_field' in the 'link' hash). > And I suppose the 'table' is only necessary if you don't specify the > 'link_class' and vice versa, right? Yup. I am a little unhappy that in your proposal one has to have a Perl class for the linking table even if one is never going to use it, but I guess this is necessary for the sake of the uniform syntax. > * didn't see any mention of the 'name' option for explicitly specifying > the name of generated methods It's called 'alias'. > * not clear to me what auto/lazy fetching, auto removing, etc options > are implemented for links_to If we use your syntax, they will be the same as in the fetch_by case. > [1] I confess I never really did understand the purpose of the alias. > What is the difference between the alias and the class? Isn't one of > them redundant? The alias is used to generate access methods in other classes referring to this one. In your configuration examples you always give a value to the 'name' key, but if it's omitted, methods are given names like 'fetch_X_alias'. Simon -- Simon (Vsevolod ILyushchenko) si...@cs... http://www.simonf.com Terrorism is a tactic and so to declare war on terrorism is equivalent to Roosevelt's declaring war on blitzkrieg. Zbigniew Brzezinski, U.S. national security advisor, 1977-81 |