From: Jeremy F. <jfi...@ma...> - 2002-10-31 22:26:18
|
Nathan - As for the XML descriptor format I totally agree. In fact, after we = discussed this last week I put together an example that is almost = identical to what you've constructed. This seems to be a no-brainer and = the parser just needs to be built. In terms of the array issue I keep harping on, your suggestion of a = generic 'array' field type got me thinking. What we're really talking = about here are collections (to use a java term). The "join" terminology = seems to come from the relational database world, and, please correct me = if I'm wrong, doesn't really fit into an OO context.=20 What if we extend basefield with a basecollection component. A few = accessor methods to basecollection are added, or maybe override = basefield, to enable access to multiple items, be they simple or complex = values. Then any type of collection can be constructed: the = contentObject array, an array of string values, maybe even structures. = Most of the basefield methods work 'out of the box' and problematic = methods, such as renderSimpleForm(), are overriden or nullified (made to = throw an exception perhaps). With the new XML descriptor format a 'key' = property to the collection type allows for simple identification: <contentType label=3D"Thread"> <fields> <field name=3D"thread" label=3D"Thread" type=3D"org.bacfug.modus.fields.text"> <rules> <rule type=3D"org.bacfug.modus.validation.full"/> <rule type=3D"org.bacfug.modus.validation.maxlength" value=3D"100"/> </rules>=20 </field> <field name=3D"postid" type=3D"org.bacfug.modus.fields.hidden"/> <field name=3D"posts" label=3D"Posts" type=3D"org.bacfug.modus.fields.objectcollection" key=3D"postid"/>=20 </fields> </contentType> It seems that this should work without having to create a baseFieldValue = component, as you've suggested - although if you're still thinking about = that, and it seems to offer more flexibility, let us know. I think a few more concrete examples might help flesh this thing out. = We've got the simple press release and I've offered a few examples of = 'parent is composed of child' type relationships. Maybe someone has a = scenario in which the relationship is not one of composition, such as = the thread containing posts. I'd be curious to see what a many-to-many = relationship would look like in object terms. Are there situations in = which this collection field type won't work? -Jeremy ----- Original Message -----=20 From: Nathan Dintenfass=20 To: mod...@li...=20 Sent: Tuesday, October 29, 2002 8:45 PM Subject: RE: [Modus-devs] Collections of content objects and = performance So, the question before us is whether "child" objects or "joined" = objects should be a separate kind of attribute of a contentObject from a = field. Hmm. One question I've been thinking about is whether this is about a parent-child relationship, in which case it makes more sense to use = the way you've been working on (and I'd consider calling it = getChildren("threads") or something like that. Or, is it more that a contentObject can have = some "joins" -- and really we need a way to define a join (as being either one-to-many or many-to-many, for instance). Seems the first issue is whether a field should be able to be of a = type that is actually just another contentObject. An interesting question. I = think the answer is yes, but I can also see how it causes a lot of problems = for the API and the cleanliness of the code base. One idea I floated was = that there should be a way for a field to be an array of values, = independent of whether it's contentObjects or not. Then, have a way for the value of = the field to be another contentObject. A field would then, perhaps, have methods like isArray() and isContentObject() -- which would help a = user who might want to build some abstracted interfaces. Both arrays of values = and contentObjects as values creates some nastiness in the interface, = though, potentially. For instance, renderSimpleForm() on a contentObject -- = could get ugly. Right now I'm thinking that moving towards the XML descriptor is the = first step to any of this. I am increasingly convinced that the CFPROPERTY = route is too limiting from an programming standpoint and apparently is also potentially a big problem when it comes to performance (per an = off-line discussion with Sean Corfield). Once we're using XML I think a lot of the other issues will be easier = to tackle. So, for instance, the pressRelease.cfc in the modustest app = might look like: <contentObjectType label=3D"Press Release"> <fields> <field name=3D"title" label=3D"Title" type=3D"org.bacfug.modus.fields.text"> <rules> <rule name=3D"org.bacfug.modus.validation.full" /> </rules> </field> <field name=3D"body" label=3D"Main Body" type=3D"org.bacfug.modus.fields.longText"/> <field name=3D"image" label=3D"Photo" type=3D"org.bacfug.modus.fields.webImage"/> <field name=3D"featured" label=3D"Featured?" type=3D"org.bacfug.modus.fields.yesno" default=3D"no"> <rules> <rule name=3D"org.bacfug.modus.validation.boolean"/> </rules> </field> <field name=3D"category" label=3D"Category" type=3D"modustest.contentObjects.categoryPicker"/> </fields> <contentObjectType> First question: does that make sense? So, now the issue is if I were building an app with child components = (or joined components) it might look like: <contentObject label=3D"Forum"> <field name=3D"threads" isArray=3D"yes" type=3D"myApp.threadComponent"> </contentObject> Or, it might look like: <contentObject label=3D"Forum"> <childObjects> <objectType type=3D"myApp.threadComponent"> </childObjects> </contentObject> Or, perhaps: <contentObject label=3D"Forum"> <joins> <join type=3D"one2many" objectType=3D"myApp.threadComponent"> </joins> </contentObject> Any thoughts from the rest of you about which would be best? In that last one, would we think that the thread than also has a join = in its definition? Would we need a separate place where a join is defined = outside of this config file? Tangent: if we have fields that have arrays of values, do we want to = expose the CF array, or would we want to create methods to traverse it (using = some kind of iterator) to maintain the object-based API? Hope that's enough to continue the conversation . . . - Nathan -----Original Message----- From: mod...@li... [mailto:mod...@li...]On Behalf Of Jeremy Firsenbaum Sent: Tuesday, October 29, 2002 2:51 PM To: modus devs Subject: [Modus-devs] Collections of content objects and performance Thought this thread might be of interest to others on the list. Nathan = and I have been talking about a couple of issues I've raised, namely, contentObjects containing collections of other contentObjects and the performance impact of instantiating so many objects. Note that this reads from the bottom up: Nathan - certainly not wed to the interface. But unless, as you = suggest, the definition of what a field is changes I don't see a way of using = basefield for content objects. Back to the forum example I've been using - = retrieving a collection of threads: The way I suggested: <cfset forum=3D createObject("component","forum").init(forumID)> <cfset threads=3D category.getObjectArray('threads')> <cfloop from=3D"1" to=3D"#arrayLen(thread)#" index=3D"i"> <!--- doing stuff with each thread, like: ---> #thread[i].getField('thread').getValue()# </cfloop> One possible way of doing what you suggest: <cfset forum=3D createObject("component","forum").init(forumID)> <cfset threads=3D category.getField('threads').getValue()> <cfloop from=3D"1" to=3D"#arrayLen(thread)#" index=3D"i"> #thread[i].getField('thread').getValue()# </cfloop> As you can see the only difference is the method call for the threads = array. And of course this gets back to the descriptor for the content object. = In the first case thread would have a property type of 'objectArray' or 'subcomponent' or something like that, and in the latter it has the = type 'field' with a particular fieldType. If the latter seems cleaner to you, fine. I just did it this way to = avoid reworking basefield - sort of a proof of concept. The developer using = the API already needs to know the 'type' for various fields. Most are = simple strings, but anything that extends basefile will certainly be handled differently, or at least make several new methods available. So making 'contentObject' be a possible fieldType should be doable - the basefieldValue idea is probably the way to go. Let me think about that = a bit. As for the performance issue, it comes in precisely when you want to instantiate a whole array of contentObjects. If I call load() on a 40 = page book object, a new instance has to be created for each of these pages. = The only way to avoid that is to reference the cached object, which opens = up a whole other can of worms - locking the server scope, concurrent access = to writable data... This may be possible, but is this a route worth going = down. Maybe others have thoughts. - Jeremy ----- Original Message ----- From: Nathan Dintenfass To: Jeremy Firsenbaum Sent: Tuesday, October 29, 2002 12:27 PM Subject: RE: Ah, I see what you mean. Yes, get() does that, but only if you wish = to populate from another instance -- which you pass in -- that is, if you = have an existing contentObject that you want to "load()", then you need to = do this. But, in this case you still never need to recreate a whole new instance as you already have the one you want to load and the one you retrieve from the cache (except on the first hit) -- but, what you are saying is that is a performance bottleneck for you, right? Hmm. On the "interface" I was talking about -- yes, the API exposed to the = end user. I figured you were not wed to it. My instinct says that it = would be better to do an overhaul of what a "field" is. First, I'd make it = possible to have an array of values, even if not other contentObject. Then, = I'd work on making it possible to have the value of a field be a contentObject. Perhaps baseFieldValue.cfc would be one way to get towards that?? I totally agree that one way or another there needs to be a way to = have a "book" and its "pages" (not to mention many-to-many relationships) and = have "book" and "page" each be instances of a contentObject. - Nathan -----Original Message----- From: Jeremy Firsenbaum [mailto:jfi...@ma...] Sent: Monday, October 28, 2002 5:33 PM To: na...@ch... Subject: Re: "By the way, the only place I am using the makeClone() is when I put = objects into the cache, not when I take them out of the cache" I'm sorry if I was misleading. "Cloning" takes place going in as well = as coming out, although the makeClone method is only called one way. = Going in, makeclone calls contentObjectPopulateFromInstance, which does all of = the actually duplicating. Coming out, get() calls contentObjectPopulateFromInstance as well: line 41 from simplefilesystempersister.cfc: if(instance.cache.isObjectCached(arguments.id)){ objectRetrieved =3D instance.cache.getObject(arguments.id); if(structCount(arguments) GT 1) return = contentObjectPopulateFromInstance(arguments[2],objectRetrieved); else return objectRetrieved; } I found the duplicating from a cached instance in contentObjectPopulateFromInstance to be the bottleneck. "I'm not sure I'm personally satisfied with the interface you are = using in your forums app." I'm all up for criticism - but not sure what you're referring to by interface. Do you mean the contentObjectArray within = basecontentobject, or the way in which the API is used externally to construct the forum. Obviously this was just an example I was using to work with the idea = of content objects within content objects. "Are you waiting for a signal from me as to what my alternative might = be?" Well - kinda, yeah. I mean I would like to know if this approach to "composition" even makes sense, or whether there's another way of = doing "parts of" relationships within modus. Many systems, content = management or otherwise, have this kind of structure. An artifact, say a book, has = many pages, each one of which has been scanned in. At one level, you want = to deal with the book as a whole (when presenting a library of books), on = another level, each one of the pages is a content object unto itself (so that = the book is browsable). I would like to be able to load the book, for a = single request or in session scope, and have the pages come along for the = ride, just as each thread is available within the forum content object. Does = this even make sense within the context of what modus was and is being = designed to do? ----- Original Message ----- From: Nathan Dintenfass To: Jeremy Firsenbaum Sent: Monday, October 28, 2002 7:56 PM Subject: RE: Ah, yes. Well, right now you are at the cutting edge of that = question. I'm not sure I'm personally satisfied with the interface you are using in = your forums app. Doesn't feel clean to me, but I have a hard time = justifying that feeling. Are you waiting for a signal from me as to what my alternative might be? By the way, the only place I am using the makeClone() is when I put = objects into the cache, not when I take them out of the cache -- are you experiencing serious performance problems with that? My main issue = with performance before the caching was with the getAll(). Load() is still = a bit slower than I'd like, but I figure that will be rarely called in = volume since getAll would be generally be faster. That is an area to work = on, though. - n -----Original Message----- From: Jeremy Firsenbaum [mailto:jfi...@ma...] Sent: Monday, October 28, 2002 4:45 PM To: na...@ch... Subject: Re: Sorry for being obtuse - I mean being able to declare other content = objects as being contained within another content object: forums are composed = of threads, which are composed of posts... ----- Original Message ----- From: Nathan Dintenfass To: Jeremy Firsenbaum Sent: Monday, October 28, 2002 7:37 PM Subject: RE: When you say "composition of content object" what do you mean? - n -----Original Message----- From: Jeremy Firsenbaum [mailto:jfi...@ma...] Sent: Monday, October 28, 2002 4:33 PM To: na...@ch... Subject: Re: Fair enough. Maybe it's all of the nested subcomponents that accounts = for the slowness in cloning from the cache. I'd love to do some more work on modus, maybe sink my teeth in the XML descriptor thing, but I feel like I can't do much with it until I know whether composition of content objects is feasible. This seems to be = the most important feature needed to model more complex systems. Looking = forward to seeing what you think. -Jeremy ----- Original Message ----- From: Nathan Dintenfass To: Jeremy Firsenbaum Sent: Monday, October 28, 2002 7:13 PM Subject: RE: Jeremy: I hadn't yet gotten to look in depth at your changes. I agree the = clone() method is far from optimal (as I mentioned in that posting). My comment was based on the dramatic performance enhancement I = witnessed on the modustest app after implementing the caching. My calls to = getAll() went from over 400ms with a handful of "press releases" to 20ms. - n -----Original Message----- From: Jeremy Firsenbaum [mailto:jfi...@ma...] Sent: Monday, October 28, 2002 3:52 PM To: Nathan Dintenfass Subject: Hey Nathan I saw your post to CFCDev today: "I have taken pages that were slow because they had to instantiate = dozens of components and made them very fast by just grabbing from the cache in = the server scope." This got me wondering if you have looked into the performance issue = with clone() that I mentioned last week. It seems like it wouldn't make a difference whether the subcomponents were instantiated within the contentObject, as I had tried to do, or if this were left to the = developer to do externally: forums =3D createObject (forum.cfc), loop for = threads =3D createObject(thread.cfc). If you need "dozens of components" the clone technique doesn't seem to be optimal. Were my performance numbers similar to yours (if you've had time to = look into this) or am I totally off base here? Jeremy ------------------------------------------------------- This sf.net email is sponsored by:ThinkGeek Welcome to geek heaven. http://thinkgeek.com/sf _______________________________________________ Modus-devs mailing list Mod...@li... https://lists.sourceforge.net/lists/listinfo/modus-devs |