|
From: Jeremy F. <jfi...@ma...> - 2002-10-29 22:51:37
|
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()#=20
</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.=20
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 -----=20
From: Nathan Dintenfass=20
To: Jeremy Firsenbaum=20
Sent: Tuesday, October 29, 2002 12:27 PM
Subject: RE:=20
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:=20
"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;=20
}
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 -----=20
From: Nathan Dintenfass=20
To: Jeremy Firsenbaum=20
Sent: Monday, October 28, 2002 7:56 PM
Subject: RE:=20
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:=20
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 -----=20
From: Nathan Dintenfass=20
To: Jeremy Firsenbaum=20
Sent: Monday, October 28, 2002 7:37 PM
Subject: RE:=20
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:=20
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.=20
-Jeremy
----- Original Message -----=20
From: Nathan Dintenfass=20
To: Jeremy Firsenbaum=20
Sent: Monday, October 28, 2002 7:13 PM
Subject: RE:=20
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:=20
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
|
|
From: Nathan D. <na...@ch...> - 2002-10-30 01:44:08
|
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="Press Release">
<fields>
<field
name="title"
label="Title"
type="org.bacfug.modus.fields.text">
<rules>
<rule
name="org.bacfug.modus.validation.full" />
</rules>
</field>
<field
name="body"
label="Main Body"
type="org.bacfug.modus.fields.longText"/>
<field
name="image"
label="Photo"
type="org.bacfug.modus.fields.webImage"/>
<field
name="featured"
label="Featured?"
type="org.bacfug.modus.fields.yesno"
default="no">
<rules>
<rule
name="org.bacfug.modus.validation.boolean"/>
</rules>
</field>
<field
name="category"
label="Category"
type="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="Forum">
<field
name="threads"
isArray="yes"
type="myApp.threadComponent">
</contentObject>
Or, it might look like:
<contentObject label="Forum">
<childObjects>
<objectType type="myApp.threadComponent">
</childObjects>
</contentObject>
Or, perhaps:
<contentObject label="Forum">
<joins>
<join type="one2many" objectType="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= createObject("component","forum").init(forumID)>
<cfset threads= category.getObjectArray('threads')>
<cfloop from="1" to="#arrayLen(thread)#" index="i">
<!--- doing stuff with each thread, like: --->
#thread[i].getField('thread').getValue()#
</cfloop>
One possible way of doing what you suggest:
<cfset forum= createObject("component","forum").init(forumID)>
<cfset threads= category.getField('threads').getValue()>
<cfloop from="1" to="#arrayLen(thread)#" index="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 = 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 = createObject (forum.cfc), loop for threads =
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
|
|
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 |
|
From: Nathan D. <na...@ch...> - 2002-10-31 22:51:22
|
We are very much on the same track. I agree that "join" is the wrong word
in the OO context. Collection is much better. In fact, they probably
should even be arrays, they should be structs (in CF terms). I extended the
XML config I put together after my last post to toy with what it might look
like. I came up with:
<contentObjectType label="Press Release">
<fields>
<field
name="title"
label="Title"
type="org.bacfug.modus.fields.text">
<rules>
<rule
name="org.bacfug.modus.validation.full" />
<rule
name="org.bacfug.modus.validation.maxlength"
maxlength="50"/>
</rules>
</field>
<field
name="body"
label="Main Body"
type="org.bacfug.modus.fields.longText"/>
<field
name="image"
label="Photo"
type="org.bacfug.modus.fields.webImage"/>
<field
name="featured"
label="Featured?"
type="org.bacfug.modus.fields.yesno"
default="no">
<rules>
<rule
name="org.bacfug.modus.validation.boolean"/>
</rules>
</field>
<field
name="category"
label="Category"
type="modustest.contentObjects.categoryPicker"/>
<field name="type"
label="type"
type="org.bacfug.modus.fields.picklist">
<options>
<option value="normal" label="Normal"/>
<option value="special" label="Special"/>
</options>
</field>
<field name="related"
label="Related Press Releases"
type="org.bacfug.modus.fields.contentObject"
multiple="yes">
<objectType name="modustest.contentObjects.pressRelease"/>
</field>
</fields>
<contentObjectType>
I had originally called it contentObjectCollection, but then I was toying
with the idea of "multiple=yes" as a different way. The difference is that
rather than having a baseCollection I wanted to be able to have any field
have multiple values. That may be too ambitious given the difficulty it
creates for the "out-of-the-box" API. But, it would be great to be able to
just say that any field hold multiple values without having to write a
textCollection, longTextCollection, etc. Of course, it may be that the only
time a collection is needed would be for other objects, rather than for
"simple" values.
I rather like the "key" notion you introduce. I was also thinking that
there might be a reason to have a contentObjectType have a way to define
what it's "handle" should be -- then, for instance, in renderQuickForm() I
would be able to just make a SELECT field with press releases to choose
from, using the "key" field as the value of the OPTION tags and the "handle"
of the pressRelease object as the label. Though, I guess it might be nice
if the key and "handle" label were both defined in the descriptor that
actually defines the collection -- that would probably be cleaner. Hmm.
With many-to-many it might be OK to just define the relationship on both
sides. Take a basic example: Say, I have articles that have one or more
authors. I want to see which authors wrote any given article, but I also
want to see the articles written by any given author. In that case, I might
define the contentObject collection in both the articles descriptor and the
author descriptor to point at each other. But, I'm not sure how pretty that
is when saving. Hmm.
-----Original Message-----
From: mod...@li...
[mailto:mod...@li...]On Behalf Of Jeremy
Firsenbaum
Sent: Thursday, October 31, 2002 2:26 PM
To: modus devs
Subject: Re: [Modus-devs] Collections of content objects and performance
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.
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="Thread">
<fields>
<field name="thread"
label="Thread"
type="org.bacfug.modus.fields.text">
<rules>
<rule type="org.bacfug.modus.validation.full"/>
<rule type="org.bacfug.modus.validation.maxlength" value="100"/>
</rules>
</field>
<field name="postid"
type="org.bacfug.modus.fields.hidden"/>
<field name="posts"
label="Posts"
type="org.bacfug.modus.fields.objectcollection"
key="postid"/>
</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 -----
From: Nathan Dintenfass
To: mod...@li...
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="Press Release">
<fields>
<field
name="title"
label="Title"
type="org.bacfug.modus.fields.text">
<rules>
<rule
name="org.bacfug.modus.validation.full" />
</rules>
</field>
<field
name="body"
label="Main Body"
type="org.bacfug.modus.fields.longText"/>
<field
name="image"
label="Photo"
type="org.bacfug.modus.fields.webImage"/>
<field
name="featured"
label="Featured?"
type="org.bacfug.modus.fields.yesno"
default="no">
<rules>
<rule
name="org.bacfug.modus.validation.boolean"/>
</rules>
</field>
<field
name="category"
label="Category"
type="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="Forum">
<field
name="threads"
isArray="yes"
type="myApp.threadComponent">
</contentObject>
Or, it might look like:
<contentObject label="Forum">
<childObjects>
<objectType type="myApp.threadComponent">
</childObjects>
</contentObject>
Or, perhaps:
<contentObject label="Forum">
<joins>
<join type="one2many" objectType="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= createObject("component","forum").init(forumID)>
<cfset threads= category.getObjectArray('threads')>
<cfloop from="1" to="#arrayLen(thread)#" index="i">
<!--- doing stuff with each thread, like: --->
#thread[i].getField('thread').getValue()#
</cfloop>
One possible way of doing what you suggest:
<cfset forum= createObject("component","forum").init(forumID)>
<cfset threads= category.getField('threads').getValue()>
<cfloop from="1" to="#arrayLen(thread)#" index="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 = 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 = createObject (forum.cfc), loop for threads =
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
|
|
From: Jeremy F. <jfi...@ma...> - 2002-10-31 23:39:05
|
comments follow inline...
----- Original Message -----=20
From: Nathan Dintenfass=20
To: mod...@li...=20
Sent: Thursday, October 31, 2002 5:52 PM
Subject: RE: [Modus-devs] Collections of content objects and =
performance
We are very much on the same track. I agree that "join" is the wrong =
word in the OO context. Collection is much better. In fact, they =
probably should even be arrays, they should be structs (in CF terms). I =
extended the XML config I put together after my last post to toy with =
what it might look like. I came up with:
<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" />
<rule
name=3D"org.bacfug.modus.validation.maxlength"=20
maxlength=3D"50"/>=20
</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?"=20
type=3D"org.bacfug.modus.fields.yesno"=20
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"/>
<field name=3D"type"
label=3D"type"
type=3D"org.bacfug.modus.fields.picklist">
<options>
<option value=3D"normal" label=3D"Normal"/>
<option value=3D"special" label=3D"Special"/>
</options> =20
</field>
<field name=3D"related"
label=3D"Related Press Releases"
type=3D"org.bacfug.modus.fields.contentObject"
multiple=3D"yes">
<objectType name=3D"modustest.contentObjects.pressRelease"/>
</field> =20
</fields>
<contentObjectType>
I had originally called it contentObjectCollection, but then I was =
toying with the idea of "multiple=3Dyes" as a different way. The =
difference is that rather than having a baseCollection I wanted to be =
able to have any field have multiple values. That may be too ambitious =
given the difficulty it creates for the "out-of-the-box" API. But, it =
would be great to be able to just say that any field hold multiple =
values without having to write a textCollection, longTextCollection, =
etc. Of course, it may be that the only time a collection is needed =
would be for other objects, rather than for "simple" values.
I'd like to think that if it's not a simple value then it should be an =
object, but I'm open to suggestions on this one. With the =
basefieldcollection component (whoops, I almost said class) methods =
would just expect to handle arrays or structures. With the =
multiple=3Dyes idea it seems like each method would need some kludgy =
conditional logic.
I rather like the "key" notion you introduce. I was also thinking =
that there might be a reason to have a contentObjectType have a way to =
define what it's "handle" should be -- then, for instance, in =
renderQuickForm() I would be able to just make a SELECT field with press =
releases to choose from, using the "key" field as the value of the =
OPTION tags and the "handle" of the pressRelease object as the label. =
Though, I guess it might be nice if the key and "handle" label were both =
defined in the descriptor that actually defines the collection -- that =
would probably be cleaner. Hmm.
I would do the latter, put the handle attribute in the collection =
description. Which brings me to a nagging issue I have with OO in =
general - objects returning collections of themselves. Is it really =
"clean" to have a getAll() method in basecontentobject? I see this kind =
of thing all of the time in Java programs and it always makes me cringe. =
Maybe it's perfectly legal, but I would rather see something like a =
pressreleaseManager return collections of press releases rather than =
giving an instance that ability. It just doesn't seem appropriate to use =
an instance's interface to gather multiple instances.
In this context, the pressreleaseManager might solve the multiple joins =
problem posed below. With two collections, articles and authors, an =
additional field (2D array) could be used as a "lookup," matching =
authors to articles. In fact, this field could have a fieldType of =
"join" or "lookup" with two "key" attributes and the persister could be =
built to automatically construct the lookup array.=20
And with the new XML parser Brad will build, this will be a snap. By the =
way, where is everybody else? DevCon is over.
With many-to-many it might be OK to just define the relationship on =
both sides. Take a basic example: Say, I have articles that have one or =
more authors. I want to see which authors wrote any given article, but =
I also want to see the articles written by any given author. In that =
case, I might define the contentObject collection in both the articles =
descriptor and the author descriptor to point at each other. But, I'm =
not sure how pretty that is when saving. Hmm.
-----Original Message-----
From: mod...@li... =
[mailto:mod...@li...]On Behalf Of Jeremy =
Firsenbaum
Sent: Thursday, October 31, 2002 2:26 PM
To: modus devs
Subject: Re: [Modus-devs] Collections of content objects and =
performance
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 |
|
From: Sean A C. <se...@co...> - 2002-11-01 05:26:31
|
On Thursday, Oct 31, 2002, at 15:38 US/Pacific, Jeremy Firsenbaum wrote: > I'd like to think that if it's not a simple value then it should be an=20= > object Agreed! > I would do the latter, put the handle attribute in the collection=20 > description. Which brings me to a nagging issue I have with OO in=20 > general - objects returning=A0collections of themselves.=A0Is it = really=20 > "clean" to have a getAll() method in basecontentobject? I see this=20 > kind of thing all of the time in Java programs and it always makes me=20= > cringe. Yup. Legal but *ugly*... "I have always wished that my computer would be as easy to use as my=20 telephone. My wish has come true - I no longer know how to use my telephone." -- Bjarne Stroustrup |
|
From: Nathan D. <na...@ch...> - 2002-11-01 17:40:35
|
I too struggled a bit with getAll() as part of the baseContentObject -- this
too is a good candidate to be moved to the contentObjectUtility (which
probably has only static methods and can live in memory). One issue is that
you need to know which persister a given contentObject type is using before
you can get all of them. The only clean way I can think of to do this is to
have an instance of that type. I considered building some kind of
"registry" of contentObject types, but that sounds very messy. Still, from
an API viewpoint I agree it would be cleaner for getAll() to live outside of
the baseContentObject.
-------------------------
Sean and Jeremy:
I would do the latter, put the handle attribute in the collection
description. Which brings me to a nagging issue I have with OO in general -
objects returning collections of themselves. Is it really "clean" to have a
getAll() method in basecontentobject? I see this kind of thing all of the
time in Java programs and it always makes me cringe.
Yup. Legal but *ugly*...
|
|
From: Jeremy F. <jfi...@ma...> - 2002-10-31 23:45:22
|
One question about the XML below: how does categoryPicker work? Does it =
define a single field with a collection of category objects, or maybe =
two fields, one with a list of category names (the handles you =
mentioned) and another with a list of categoryids?
----- Original Message -----=20
From: Nathan Dintenfass=20
To: mod...@li...=20
Sent: Thursday, October 31, 2002 5:52 PM
Subject: RE: [Modus-devs] Collections of content objects and =
performance
We are very much on the same track. I agree that "join" is the wrong =
word in the OO context. Collection is much better. In fact, they =
probably should even be arrays, they should be structs (in CF terms). I =
extended the XML config I put together after my last post to toy with =
what it might look like. I came up with:
<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" />
<rule
name=3D"org.bacfug.modus.validation.maxlength"=20
maxlength=3D"50"/>=20
</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?"=20
type=3D"org.bacfug.modus.fields.yesno"=20
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"/>
<field name=3D"type"
label=3D"type"
type=3D"org.bacfug.modus.fields.picklist">
<options>
<option value=3D"normal" label=3D"Normal"/>
<option value=3D"special" label=3D"Special"/>
</options> =20
</field>
<field name=3D"related"
label=3D"Related Press Releases"
type=3D"org.bacfug.modus.fields.contentObject"
multiple=3D"yes">
<objectType name=3D"modustest.contentObjects.pressRelease"/>
</field> =20
</fields>
<contentObjectType>/modus-devs |
|
From: Nathan D. <na...@ch...> - 2002-10-31 23:49:45
|
Actually, it just creates a SELECT box of categories -- no child objects at
all. That was my first attempt at having fields drawn from discrete
options. With CFPROPERTY there was not good way to tell it what the options
were, so I built it as a one-off field just to see how onerous that would
be. It is OK, but not great. You may notice the field right after that,
called "type" -- I have not yet implemented that, but the new XML freedom
should allow for that kind of thing. Also notice that the second rule of
title has an extra attribute called maxlength (we need to come up with a
consistent scheme for how to pass "extra" information in the descriptor.
Should it always be a node, or use attributes?
-----Original Message-----
From: mod...@li...
[mailto:mod...@li...]On Behalf Of Jeremy
Firsenbaum
Sent: Thursday, October 31, 2002 3:45 PM
To: mod...@li...
Subject: Re: [Modus-devs] Collections of content objects and performance
One question about the XML below: how does categoryPicker work? Does it
define a single field with a collection of category objects, or maybe two
fields, one with a list of category names (the handles you mentioned) and
another with a list of categoryids?
----- Original Message -----
From: Nathan Dintenfass
To: mod...@li...
Sent: Thursday, October 31, 2002 5:52 PM
Subject: RE: [Modus-devs] Collections of content objects and performance
We are very much on the same track. I agree that "join" is the wrong
word in the OO context. Collection is much better. In fact, they probably
should even be arrays, they should be structs (in CF terms). I extended the
XML config I put together after my last post to toy with what it might look
like. I came up with:
<contentObjectType label="Press Release">
<fields>
<field
name="title"
label="Title"
type="org.bacfug.modus.fields.text">
<rules>
<rule
name="org.bacfug.modus.validation.full" />
<rule
name="org.bacfug.modus.validation.maxlength"
maxlength="50"/>
</rules>
</field>
<field
name="body"
label="Main Body"
type="org.bacfug.modus.fields.longText"/>
<field
name="image"
label="Photo"
type="org.bacfug.modus.fields.webImage"/>
<field
name="featured"
label="Featured?"
type="org.bacfug.modus.fields.yesno"
default="no">
<rules>
<rule
name="org.bacfug.modus.validation.boolean"/>
</rules>
</field>
<field
name="category"
label="Category"
type="modustest.contentObjects.categoryPicker"/>
<field name="type"
label="type"
type="org.bacfug.modus.fields.picklist">
<options>
<option value="normal" label="Normal"/>
<option value="special" label="Special"/>
</options>
</field>
<field name="related"
label="Related Press Releases"
type="org.bacfug.modus.fields.contentObject"
multiple="yes">
<objectType name="modustest.contentObjects.pressRelease"/>
</field>
</fields>
<contentObjectType>/modus-devs
|
|
From: Jeremy F. <jfi...@ma...> - 2002-11-01 00:24:16
|
"we need to come up with a consistent scheme for how to pass "extra" =
information in the descriptor"
I'd say go with the way you're doing it now - attributes of objects are =
written as xml attributes and items that are distinct objects are nodes, =
such as field and rule. If it makes a difference, attributes are =
accessed through the xmlAttributes structure and nodes are accessed =
through the xmlChildren array in CFMX.
----- Original Message -----=20
From: Nathan Dintenfass=20
To: mod...@li...=20
Sent: Thursday, October 31, 2002 6:51 PM
Subject: RE: [Modus-devs] Collections of content objects and =
performance
Actually, it just creates a SELECT box of categories -- no child =
objects at all. That was my first attempt at having fields drawn from =
discrete options. With CFPROPERTY there was not good way to tell it =
what the options were, so I built it as a one-off field just to see how =
onerous that would be. It is OK, but not great. You may notice the =
field right after that, called "type" -- I have not yet implemented =
that, but the new XML freedom should allow for that kind of thing. Also =
notice that the second rule of title has an extra attribute called =
maxlength (we need to come up with a consistent scheme for how to pass =
"extra" information in the descriptor. Should it always be a node, or =
use attributes?
-----Original Message-----
From: mod...@li... =
[mailto:mod...@li...]On Behalf Of Jeremy =
Firsenbaum
Sent: Thursday, October 31, 2002 3:45 PM
To: mod...@li...
Subject: Re: [Modus-devs] Collections of content objects and =
performance
One question about the XML below: how does categoryPicker work? Does =
it define a single field with a collection of category objects, or maybe =
two fields, one with a list of category names (the handles you =
mentioned) and another with a list of categoryids?
----- Original Message -----=20
From: Nathan Dintenfass=20
To: mod...@li...=20
Sent: Thursday, October 31, 2002 5:52 PM
Subject: RE: [Modus-devs] Collections of content objects and =
performance
We are very much on the same track. I agree that "join" is the =
wrong word in the OO context. Collection is much better. In fact, they =
probably should even be arrays, they should be structs (in CF terms). I =
extended the XML config I put together after my last post to toy with =
what it might look like. I came up with:
<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" />
<rule
name=3D"org.bacfug.modus.validation.maxlength"=20
maxlength=3D"50"/>=20
</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?"=20
type=3D"org.bacfug.modus.fields.yesno"=20
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"/>
<field name=3D"type"
label=3D"type"
type=3D"org.bacfug.modus.fields.picklist">
<options>
<option value=3D"normal" label=3D"Normal"/>
<option value=3D"special" label=3D"Special"/>
</options> =20
</field>
<field name=3D"related"
label=3D"Related Press Releases"
type=3D"org.bacfug.modus.fields.contentObject"
multiple=3D"yes">
<objectType name=3D"modustest.contentObjects.pressRelease"/>
</field> =20
</fields>
<contentObjectType>/modus-devs |
|
From: Sean A C. <se...@co...> - 2002-11-01 05:27:31
|
On Thursday, Oct 31, 2002, at 15:51 US/Pacific, Nathan Dintenfass wrote: > Actually, it just creates a SELECT box of categories -- no child=20 > objects at all.=A0 That was my first attempt at having fields drawn = from=20 > discrete options. What has an HTML SELECT got to do with data descriptors???? "I have always wished that my computer would be as easy to use as my=20 telephone. My wish has come true - I no longer know how to use my telephone." -- Bjarne Stroustrup |
|
From: Nathan D. <na...@ch...> - 2002-11-01 16:57:50
|
Sean asks: What has an HTML SELECT got to do with data descriptors????
Yes, what does an HTML SELECT have to do with a data descriptor?? OK, I
should have said that the categoryPicker manifests itself as a form widget
with an HTML SELECT. The original issue was that I wanted a way to have a
field that needed to be chosen from a discrete list of values. The way to
get this done in the "real world" was with a SELECT field. The CFPROPERTY
tags did not allow me to specify much about what those options might be, so
I did an experiment by creating my own "custom" field type called
categoryPicker. categoryPicker.cfc uses the formFieldFactory to make a
SELECT with some options in it. This really goes to the more philosophical
discussion about how much a contentObject (or, really, a field) should know
about how to render itself (see the generic discussion post that precedes
this message).
Sean says he shudders when he sees:
<field name="type"
label="Type"
type="org.bacfug.modus.fields.picklist">
<options>
<option value="normal" label="Normal"/>
<option value="special" label="Special"/>
</options>
</field>
I have mixed feelings about it. When I had to build the categoryPicker it
felt "dirty" -- I didn't want to have go build new field types every time I
just wanted a discrete list to choose from for a given field. So, when
moving to XML it seemed to offer a good deal of freedom to do something
simple like have a generic "picklist" field that can be fed some options. I
am very open to suggestions on to accomplish this in a better way. It did
not seem sufficient to me to just say that "type" in this case is just a
"string" and then make the developer worry about what the options for "type"
might be -- I don't see how that has much advantage over the "old way" with
an RDBMS and a bunch of templates for rendering forms. I also don't think
that every time I want to pick from a list of things that it should be a
list of other contentObjects -- seems like overkill for a "type" or a
"category" or a "state" to have to be a contentObject, when all it needs to
be is one (or more) from a list of defined options (though
org.bacfug.modus.fields.usastate is probably a candidate to be a built-in
field given how often it's used).
|
|
From: Sean A C. <se...@co...> - 2002-11-01 18:32:00
|
On Friday, Nov 1, 2002, at 08:58 US/Pacific, Nathan Dintenfass wrote: > Yes, what does an HTML SELECT have to do with a data descriptor?? OK, > I > should have said that the categoryPicker manifests itself as a form > widget > with an HTML SELECT. OK, I can see where you're going with this now (and you know I don't like that approach :) > <field name="type" > label="Type" > type="org.bacfug.modus.fields.picklist"> > <options> > <option value="normal" label="Normal"/> > <option value="special" label="Special"/> > </options> > </field> > > I have mixed feelings about it. When I had to build the > categoryPicker it > felt "dirty" -- I didn't want to have go build new field types every > time I > just wanted a discrete list to choose from for a given field. Hmm... I guess the conceptual question is: do you want the persistent implementation to be 0, 1, .., n (i.e., an integer) or a string? I can see how the 'simplicity' argument leans toward definition inline as a string. I don't really like it but I can't think of a better way off-hand... I'll give it some more thought. "Conform! Consume! Obey!" -- Mr Snaffleburger : http://www.matazone.co.uk/theotherside.html |
|
From: Nathan D. <na...@ch...> - 2002-11-01 20:54:27
|
> On Friday, Nov 1, 2002, at 08:58 US/Pacific, Nathan Dintenfass wrote: > > Yes, what does an HTML SELECT have to do with a data descriptor?? OK, > > I > > should have said that the categoryPicker manifests itself as a form > > widget > > with an HTML SELECT. > > OK, I can see where you're going with this now (and you know I don't > like that approach :) Yes, and that is why I would like to know some alternatives ;) More generally, do you think a field should not be responsible for rendering itself into a form widget? If not, can you give an example of what that might look like for the developer (in terms of configuration and page level coding)? > > I have mixed feelings about it. When I had to build the > > categoryPicker it > > felt "dirty" -- I didn't want to have go build new field types every > > time I > > just wanted a discrete list to choose from for a given field. > > Hmm... I guess the conceptual question is: do you want the persistent > implementation to be 0, 1, .., n (i.e., an integer) or a string? > > I can see how the 'simplicity' argument leans toward definition inline > as a string. Well, in this case it would be a string. If I had been referencing a linked object type then I would have expected the persister to store the id of that instance (as per Jeremy's "key" suggestion, with the default being the id). |
|
From: Sean A C. <se...@co...> - 2002-11-01 05:24:27
|
On Thursday, Oct 31, 2002, at 14:52 US/Pacific, Nathan Dintenfass wrote: > We are very much on the same track.=A0 I agree that "join" is the = wrong=20 > word in the OO context.=A0 Collection is much better.=A0 In fact, they=20= > probably should=A0even be arrays,=A0they should be structs (in CF = terms). See my other comment about complex fields. That's where our system=20 began to break down. Part of the issue seems to be around how=20 'holistic' you want the XML descriptor to be: in a forum application,=20 for examples, we have multiple 'forum' objects, each 'forum' has zero=20 or more 'thread' objects, each 'thread' has zero or more 'message'=20 objects, a message object has zero or more replies which are themselves=20= 'message' objects. Should there be a separate description for each=20 object, or is one descriptor sufficient (or desirable) for that cluster=20= of objects. > =A0=A0<field=A0name=3D"related" > =A0=A0=A0=A0label=3D"Related Press Releases" > =A0=A0=A0=A0type=3D"org.bacfug.modus.fields.contentObject" > =A0=A0=A0=A0multiple=3D"yes"> > =A0=A0=A0<objectType name=3D"modustest.contentObjects.pressRelease"/> > =A0=A0</field>=A0=A0 So this is an array of modustest.contentObjects.pressRelease? What has=20= "type=3D..." got to do with this? What value has the type got to do with=20= anything? "I can smell your brains!" -- Mittens the Kitten : http://www.matazone.co.uk/theotherside.html |
|
From: Jeremy F. <jfi...@ma...> - 2002-11-01 14:49:06
|
Sean - I've jumped into this thing in midstream, so I'm wondering what =
you're referring to when you say "our system."=20
I appreciate your concern with the descriptor mixing display and data =
attributes. It seems like a few of the items in the descriptor right now =
are there just to make renderSimpleForm() work and may be superfluous to =
the actual object model. I know that Nathan has been pushing Modus to be =
a complete system, with form generation built, but the system may be =
losing some of its "lightness" in doing so. I look forward to seeing his =
reply.
As for clustering the XML descriptor for a group of objects I would say =
no. I think it's legitimate to reference other objects within a =
descriptor ( a forum has threads), but the descriptor should continue to =
describe one content type. It seems cleaner and much more manageable to =
have separate forum, thread, and message descriptors than to wrap them =
all together in one mega/meta-file.
And you have to consider embedded vs referenced. The latter acts like a =
foreign key relationship, the former gets you into the same 'mess' as =
trying to implement arrays natively (as does trying to implement nested =
structs natively as far as I can see).I've been playing with nesting =
content objects a lot and the method that seems to offer the best =
balance of efficiency and flexibility is a mix of embedded and =
referenced objects. The init method basically constructs a map of all =
nested objects or subcomponents (the reference part), and a call to =
retrieve a subcomponent will then instantiate it. What this affords a =
developer using the modus API is to do something like:
<cfset forum =3D =
createObject("component","path.to.forum").init(attributes.forumID)>
<cfset threads =3D forum.getObjects('threads')>
I've got an example that I'll email you off list.
-Jeremy
----- Original Message -----=20
From: Sean A Corfield=20
To: mod...@li...=20
Sent: Friday, November 01, 2002 12:24 AM
Subject: Re: [Modus-devs] Collections of content objects and =
performance
On Thursday, Oct 31, 2002, at 14:52 US/Pacific, Nathan Dintenfass =
wrote:
We are very much on the same track. I agree that "join" is the =
wrong word in the OO context. Collection is much better. In fact, they =
probably should even be arrays, they should be structs (in CF terms).
See my other comment about complex fields. That's where our system =
began to break down. Part of the issue seems to be around how 'holistic' =
you want the XML descriptor to be: in a forum application, for examples, =
we have multiple 'forum' objects, each 'forum' has zero or more 'thread' =
objects, each 'thread' has zero or more 'message' objects, a message =
object has zero or more replies which are themselves 'message' objects. =
Should there be a separate description for each object, or is one =
descriptor sufficient (or desirable) for that cluster of objects.
<field name=3D"related"
label=3D"Related Press Releases"
type=3D"org.bacfug.modus.fields.contentObject"
multiple=3D"yes">
<objectType name=3D"modustest.contentObjects.pressRelease"/>
</field> =20
So this is an array of modustest.contentObjects.pressRelease? What has =
"type=3D..." got to do with this? What value has the type got to do with =
anything?
"I can smell your brains!"
-- Mittens the Kitten : http://www.matazone.co.uk/theotherside.html
|
|
From: Sean A C. <se...@co...> - 2002-11-01 15:26:07
|
On Friday, Nov 1, 2002, at 06:48 US/Pacific, Jeremy Firsenbaum wrote:
> Sean - I've jumped into this thing in midstream, so I'm wondering what=20=
> you're referring to when you say "our system."
Oh, sorry. Macromedia. Well, my team at Macromedia. We prototyped a=20
persistence mechanism that was pretty similar to what Modus tries to do=20=
(and was what Nathan was referring to in one of his earlier messages=20
when he named me).
> I appreciate your concern with the descriptor mixing display=A0and =
data=20
> attributes.=A0It seems like a=A0few of the items in the descriptor =
right=20
> now are there just to make renderSimpleForm() work and may be=20
> superfluous to the actual object model.=A0
Yes, I'm not convinced that it is possible to build a generic=20
form-rendering engine (without a huge amount of machinery) and I feel -=20=
very strongly as y'all might gather! - that this sort of display=20
machinery doesn't belong in a content model. What if your display is=20
Flash? WML? XML?
> As for=A0clustering the XML descriptor for a group of objects I would=20=
> say no. I think it's legitimate to reference other objects within a=20
> descriptor ( a forum has threads), but the descriptor should continue=20=
> to describe one content type. It seems cleaner and much more=20
> manageable to have separate forum, thread, and message descriptors=20
> than to wrap them all together in one mega/meta-file.
OK, just wanted to get a sense of the folks here on that one.
> <cfset forum =3D=20
> createObject("component","path.to.forum").init(attributes.forumID)>
> <cfset threads =3D forum.getObjects('threads')>
Just to clarify: the init() call would not instantiate/retrieve the=20
subobjects, that would only happen on the call to getObjects(), yes?=20
That was part of the problem we had with performance: if the first=20
init() call created the entire object (i.e., including nested=20
subobjects) it became very slow very quickly.
> =A0=A0<field=A0name=3D"related"
> =A0=A0=A0=A0label=3D"Related Press Releases"
> =A0=A0=A0=A0type=3D"org.bacfug.modus.fields.contentObject"
> =A0=A0=A0=A0multiple=3D"yes">
> =A0=A0=A0<objectType name=3D"modustest.contentObjects.pressRelease"/>
> =A0=A0</field>=A0=A0
>
> So this is an array of modustest.contentObjects.pressRelease? What has=20=
> "type=3D..." got to do with this? What value has the type got to do =
with=20
> anything?
I'm still confused by this XML example - it seems very strange to have,=20=
effectively, two types specifies for this field... Nathan, can you=20
elaborate?
"SOAP is not so much a means of transmitting data
but a mechanism for calling COM objects over the Web."
-- not Microsoft (surprisingly!)
|
|
From: Jeremy F. <jfi...@ma...> - 2002-11-01 15:45:57
|
I've offered this as an alternative to the objectType:
<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>
"Just to clarify: the init() call would not instantiate/retrieve the =
subobjects, that would only happen on the call to getObjects(), yes?"
Right, no instantiation until the item is required. Of course this is =
not a problem if only one post is needed, but if all of the posts are =
requested (getObjects('posts')) then things become crunchingly slow. =
However, this would be the case whether the posts were wrapped inside of =
the thread component or if they were requested externally with the =
post.getAll() method which I've already said irks me.
In either case, CFMX just doesn't seem up to the task of instantiating =
dozens of objects on a single request. In my crude performance tests it =
took roughly 50ms per object to retrieve a collection of threads from =
the cache.
----- Original Message -----=20
From: Sean A Corfield=20
To: mod...@li...=20
Sent: Friday, November 01, 2002 10:26 AM
Subject: Re: [Modus-devs] Collections of content objects and =
performance
On Friday, Nov 1, 2002, at 06:48 US/Pacific, Jeremy Firsenbaum wrote:
Sean - I've jumped into this thing in midstream, so I'm wondering =
what you're referring to when you say "our system."
Oh, sorry. Macromedia. Well, my team at Macromedia. We prototyped a =
persistence mechanism that was pretty similar to what Modus tries to do =
(and was what Nathan was referring to in one of his earlier messages =
when he named me).
I appreciate your concern with the descriptor mixing display and =
data attributes. It seems like a few of the items in the descriptor =
right now are there just to make renderSimpleForm() work and may be =
superfluous to the actual object model.=20
Yes, I'm not convinced that it is possible to build a generic =
form-rendering engine (without a huge amount of machinery) and I feel - =
very strongly as y'all might gather! - that this sort of display =
machinery doesn't belong in a content model. What if your display is =
Flash? WML? XML?
As for clustering the XML descriptor for a group of objects I would =
say no. I think it's legitimate to reference other objects within a =
descriptor ( a forum has threads), but the descriptor should continue to =
describe one content type. It seems cleaner and much more manageable to =
have separate forum, thread, and message descriptors than to wrap them =
all together in one mega/meta-file.
OK, just wanted to get a sense of the folks here on that one.
<cfset forum =3D =
createObject("component","path.to.forum").init(attributes.forumID)>
<cfset threads =3D forum.getObjects('threads')>
Just to clarify: the init() call would not instantiate/retrieve the =
subobjects, that would only happen on the call to getObjects(), yes? =
That was part of the problem we had with performance: if the first =
init() call created the entire object (i.e., including nested =
subobjects) it became very slow very quickly.
<field name=3D"related"
label=3D"Related Press Releases"
type=3D"org.bacfug.modus.fields.contentObject"
multiple=3D"yes">
<objectType name=3D"modustest.contentObjects.pressRelease"/>
</field> =20
So this is an array of modustest.contentObjects.pressRelease? What =
has "type=3D..." got to do with this? What value has the type got to do =
with anything?
I'm still confused by this XML example - it seems very strange to =
have, effectively, two types specifies for this field... Nathan, can you =
elaborate?
|
|
From: Nathan D. <na...@ch...> - 2002-11-01 17:54:35
|
[Hmm, somehow this never showed up in my mail. Sorry if it's a repost for some of you] Well, there is a lot of ground to cover from your last few posts. I'm sending this one first to lay out some generic responses. I'll respond to inline questions by replying to the individual posts. Let me say first that Modus is not intended to be a general application framework -- it is focused on building web-delivered applications written in CFML. Given the intended use of Modus, the "80% case" is the target for the code base (particularly for 1.0). I think we can spend some good energy discussing the places where clean separation of "data", "logic" and "display" could be better, but I come from a viewpoint that speed of development for a CFML developer wanting to build web pages that deliver dynamic content is a key consideration that, in my mind, may trump certain design philosophies. But, I remain very open to suggestions on ways to improve what we've got so far (it's only 0.2.x after all). For me, the modustest app in its current form is a living proof-of-concept that demonstrates how very very easy it is for someone to very rapidly create a very simple CMS. As we move towards linked contentObjects and other "advanced" features I don't want to lose such ease of use for the base case. A lot of the discussion in the last 24 hours has started to revolve around what a field is and what the descriptor should know. These are both good discussions to have, particularly as we prepare to rewrite the guts to use the XML descriptor. A general premise of Modus is that a "field" should know how to render itself into a form widget. This makes it very easy to create basic Add/Edit forms with a minimal amount of coding (a classic PITA task for a web app). The renderSimpleForm() is just a convenience that I think will move out of the baseContentObject and into some kind of contentObjectUtility. I am not looking to create a totally abstract form generation engine, but I do think that if I know a field is going to require a file upload, that I can just call renderFormField() on that field and get the proper HTML input. I don't think this is mutually exclusive with delivery to Flash or WML or whatever -- in those environments you wouldn't use the renderFormField() methods of the fields, but since 80% (or more) of Modus apps will be delivered via HTML I think it's a "good thing" for fields to be able to help out the developer. I could be much more easily convinced to do away with "label" and renderSimpleForm() than I could to not have fields know how to create the proper HTML form widget for themselves. But, it sure is handy to have that label ;) On the subject of embedded vs. reference for linked components: I tend to lean towards references. Although embedded can have some advantages, it will likely prove to be overly cumbersome from a performance standpoint (and from a code sanity standpoint). I'll try to cover the rest in the messages . . . Thanks for engaging in this discussion! - n |
|
From: Nathan D. <na...@ch...> - 2002-11-01 18:10:47
|
Well, there is a lot of ground to cover from your last few posts. I'm sending this one first to lay out some generic responses. I'll respond to inline questions by replying to the individual posts. Let me say first that Modus is not intended to be a general application framework -- it is focused on building web-delivered applications written in CFML. Given the intended use of Modus, the "80% case" is the target for the code base (particularly for 1.0). I think we can spend some good energy discussing the places where clean separation of "data", "logic" and "display" could be better, but I come from a viewpoint that speed of development for a CFML developer wanting to build web pages that deliver dynamic content is a key consideration that, in my mind, may trump certain design philosophies. But, I remain very open to suggestions on ways to improve what we've got so far (it's only 0.2.x after all). For me, the modustest app in its current form is a living proof-of-concept that demonstrates how very very easy it is for someone to very rapidly create a very simple CMS. As we move towards linked contentObjects and other "advanced" features I don't want to lose such ease of use for the base case. A lot of the discussion in the last 24 hours has started to revolve around what a field is and what the descriptor should know. These are both good discussions to have, particularly as we prepare to rewrite the guts to use the XML descriptor. A general premise of Modus is that a "field" should know how to render itself into a form widget. This makes it very easy to create basic Add/Edit forms with a minimal amount of coding (a classic PITA task for a web app). The renderSimpleForm() is just a convenience that I think will move out of the baseContentObject and into some kind of contentObjectUtility. I am not looking to create a totally abstract form generation engine, but I do think that if I know a field is going to require a file upload, that I can just call renderFormField() on that field and get the proper HTML input. I don't think this is mutually exclusive with delivery to Flash or WML or whatever -- in those environments you wouldn't use the renderFormField() methods of the fields, but since 80% (or more) of Modus apps will be delivered via HTML I think it's a "good thing" for fields to be able to help out the developer. I could be much more easily convinced to do away with "label" and renderSimpleForm() than I could to not have fields know how to create the proper HTML form widget for themselves. But, it sure is handy to have that label ;) On the subject of embedded vs. reference for linked components: I tend to lean towards references. Although embedded can have some advantages, it will likely prove to be overly cumbersome from a performance standpoint (and from a code sanity standpoint). I'll try to cover the rest in the messages . . . Thanks for engaging in this discussion! - n |
|
From: Sean A C. <se...@co...> - 2002-11-01 23:41:39
|
On Friday, Nov 1, 2002, at 08:46 US/Pacific, Nathan Dintenfass wrote: > Let me say first that Modus is not intended to be a general application > framework -- it is focused on building web-delivered applications > written in > CFML. Given the intended use of Modus, the "80% case" is the target > for the > code base (particularly for 1.0). OK, but it sounds like it's intended to be "more general" than 'just' a persistence mechanism which is part of what I'm really trying to get a handle on. > I come from a viewpoint that speed of > development for a CFML developer wanting to build web pages that > deliver > dynamic content is a key consideration that, in my mind, may trump > certain > design philosophies. Agreed. And performance is probably a higher goal than 'sweet design under the hood'. Take Fusebox as an example. > For me, the > modustest app in its current form is a living proof-of-concept that > demonstrates how very very easy it is for someone to very rapidly > create a > very simple CMS. Agreed, but it's not very fast, I gather :) > As we move towards linked contentObjects and other > "advanced" features I don't want to lose such ease of use for the base > case. Agreed. I'm actually trying to ensure that we don't try to build in features that make Modus complicated to use or make it terribly slow to run. I would avoid embedded components for those very reasons. > A lot of the discussion in the last 24 hours has started to revolve > around > what a field is and what the descriptor should know. These are both > good > discussions to have, particularly as we prepare to rewrite the guts to > use > the XML descriptor. Agreed. Can I put in a little vote for making the presentation specifications optional? And having the renderXxx() method(s) throw an exception if an attempt is made to render an object for which no specification is given... > The renderSimpleForm() is just a convenience that I think will move > out of > the baseContentObject and into some kind of contentObjectUtility. I think a field can render itself but I'm not sold on the idea of a whole object rendering itself as HTML... > I can just > call renderFormField() on that field and get the proper HTML input. Yes, I think that's reasonable (given my caveat about optional presentation specs). > I could be much more easily convinced to do away with "label" and > renderSimpleForm() than I could to not have fields know how to create > the > proper HTML form widget for themselves. But, it sure is handy to have > that > label ;) I think 'friendly name' is a good concept but it doesn't lend itself to localization so I'd be slightly wary of label=. I'm pretty solidly against renderSimpleForm() but I can't argue very convincingly against renderFormField() (at the moment! :) > On the subject of embedded vs. reference for linked components: I tend > to > lean towards references. Although embedded can have some advantages, > it > will likely prove to be overly cumbersome from a performance > standpoint (and > from a code sanity standpoint). Agreed. I'd be perfectly happy to not support embedded subcomponents - it certainly didn't help us much (it actually caused us more problems than it solved). We need to be careful about the array issue for similar reasons. Simple enough for the audience without allowing them to hang themselves on poor performance. "Conform! Consume! Obey!" -- Mr Snaffleburger : http://www.matazone.co.uk/theotherside.html |
|
From: Nathan D. <na...@ch...> - 2002-11-02 00:17:50
|
Well, I've managed to eat up sizable portions of my Friday talking Modus.
Which is fun.
My answers inline below . . .
>
> On Friday, Nov 1, 2002, at 08:46 US/Pacific, Nathan Dintenfass wrote:
> > Let me say first that Modus is not intended to be a general application
> > framework -- it is focused on building web-delivered applications
> > written in
> > CFML. Given the intended use of Modus, the "80% case" is the target
> > for the
> > code base (particularly for 1.0).
>
> OK, but it sounds like it's intended to be "more general" than 'just' a
> persistence mechanism which is part of what I'm really trying to get a
> handle on.
Yes, definitely not just persistence. It's about doing away with a lot of
mundane, repetitive tasks while offering a tight metaphor for coding. I
think, as Jeremy has demonstrated, that Modus 1.0 could be used in the
context of Fusebox -- though eventually Modus (or a related project) may
grow to supersede much of what Fusebox is about.
>
> > I come from a viewpoint that speed of
> > development for a CFML developer wanting to build web pages that
> > deliver
> > dynamic content is a key consideration that, in my mind, may trump
> > certain
> > design philosophies.
>
> Agreed. And performance is probably a higher goal than 'sweet design
> under the hood'. Take Fusebox as an example.
True, true. Though, personally performance tends to be a lower priority for
me because I rarely work on sites that have huge volume. I think the target
for Modus is more likely the "typical CF site" where you never have a user
base in the millions (or even in the hundreds of thousands). That is, it
wouldn't be a big deal in my mind if Modus 1.0 was not an "Enterprise Class"
system if it is useful to a large swath of CF folks.
> > For me, the
> > modustest app in its current form is a living proof-of-concept that
> > demonstrates how very very easy it is for someone to very rapidly
> > create a
> > very simple CMS.
>
> Agreed, but it's not very fast, I gather :)
Well, depends on what "fast" means. Is less than 500 ms for a page that
both displays all the press releases and has everything necessary to edit
one (including having retrieved one from persistence to get it ready to
edit)? For most applications that is acceptable performance. Is it great?
No. Could it be improved. Probably. But, as long as a typical Modus page
can stay in that range it's probably good enough (for 1.0 anyway). We can
go back later when the developer API is well baked to optimize (or people
can build different caching and/or persistence mechanisms to speed things
up). What I'm saying is that for 1.0 I think the primary objective should
be tight metaphor, rapid development and "acceptable" performance.
>
> > As we move towards linked contentObjects and other
> > "advanced" features I don't want to lose such ease of use for the base
> > case.
>
> Agreed. I'm actually trying to ensure that we don't try to build in
> features that make Modus complicated to use or make it terribly slow to
> run. I would avoid embedded components for those very reasons.
Agreed. Embedded components are a no go for the foreseeable future.
"Linked" components (the "foreign key" reference model) are the way to go
for Modus.
>
> > A lot of the discussion in the last 24 hours has started to revolve
> > around
> > what a field is and what the descriptor should know. These are both
> > good
> > discussions to have, particularly as we prepare to rewrite the guts to
> > use
> > the XML descriptor.
>
> Agreed. Can I put in a little vote for making the presentation
> specifications optional? And having the renderXxx() method(s) throw an
> exception if an attempt is made to render an object for which no
> specification is given...
Yes, I think that's reasonable.
>
> > The renderSimpleForm() is just a convenience that I think will move
> > out of
> > the baseContentObject and into some kind of contentObjectUtility.
>
> I think a field can render itself but I'm not sold on the idea of a
> whole object rendering itself as HTML...
Oh, yes. I never intended for pressRelease.renderHTML(), only
pressRelease.getField("image").renderHTML() -- which just outputs:
<img src="http://[serverPath]/imageName.gif">
That is probably the most extreme example of a field knowing about HTML.
I absolutely agree that a contentObject should not even try to render itself
in its entirety. I could see building some kind of display handler
mechanism where I might do pressRelease.renderDisplay("handerName"), but
that's not a priority for me. The renderHTML() on most field types just
outputs the raw value -- I just think from an interface standpoint it's
cleaner to use toHTML() rather than getValue() -- allows for things like a
field type that is a "webImage" (one of the built in field types in 0.2) or
perhaps a "colorPicker" (which might use a SWF or some other "rich" client).
renderSimpleForm() is a different animal -- I guess I see that as something
used only for the most basic of basic objects or for prototyping. I would
imagine that in a product app that renderSimpleForm() would rarely be used.
But, it's convenient enough that I built it in (works fabulously for
something super simple like the current modustest application). Understand
that I build a lot of "little" sites that do some fairly basic CMS for small
businesses, hobby sites, personal sites, etc. -- Modus was started more to
help me slam those out than to build something like macromedia.com (though,
it's nice to have that perspective on the team to keep me from making
blunders and, frankly, for my own education about OO
frameworks/methods/patterns).
>
> > I could be much more easily convinced to do away with "label" and
> > renderSimpleForm() than I could to not have fields know how to create
> > the
> > proper HTML form widget for themselves. But, it sure is handy to have
> > that
> > label ;)
>
> I think 'friendly name' is a good concept but it doesn't lend itself to
> localization so I'd be slightly wary of label=. I'm pretty solidly
> against renderSimpleForm() but I can't argue very convincingly against
> renderFormField() (at the moment! :)
Yes, label is "friendly name" -- localization is a great issue. I guess it
seems like someone who is building a multi-lingual site will need to find an
alternative way if they want some generic form generation to happen (or
submit changes to Modus that allow it ;). This is a great example of the
different perspective -- working on macromedia.com you are forced to think
about things like localization, whereas I have not dealt with it very much.
I think it's safe to say that the "label" mechanism in the descriptor is not
a requirement and not something that a lot of "advanced" sites would use
(like most advanced CF developers would never touch CFINSERT/CFUPDATE and
most would not use client variables, but those are handy features of the
language for a newbie -- I see renderSimpleForm() in the same category).
|
|
From: Sean A C. <se...@co...> - 2002-11-05 04:44:25
|
On Friday, Nov 1, 2002, at 16:19 US/Pacific, Nathan Dintenfass wrote:
> Well, I've managed to eat up sizable portions of my Friday talking
> Modus.
> Which is fun.
:)
> Yes, definitely not just persistence. It's about doing away with a
> lot of
> mundane, repetitive tasks while offering a tight metaphor for coding.
I guess my concern is that I believe we need a solid persistence layer
first, on which to build an application framework. I'm worried that by
trying to create a single all-purpose layer, it won't do either very
well.
> Jeremy has demonstrated, that Modus 1.0 could be used in the
> context of Fusebox -- though eventually Modus (or a related project)
> may
> grow to supersede much of what Fusebox is about.
I think it will be interesting to see how "Fusebox MX" will be
received... but Fusebox does not address persistence so I believe a
focused persistence layer can coexist peacefully with Fusebox MX :)
> True, true. Though, personally performance tends to be a lower
> priority for
> me because I rarely work on sites that have huge volume.
I think performance is important enough in real life for enough CFers
that we do need to consider it.
> wouldn't be a big deal in my mind if Modus 1.0 was not an "Enterprise
> Class"
> system if it is useful to a large swath of CF folks.
It would be nice if Modus 2.0 was not radically different to Modus 1.0
tho' so I think we need to get enough confidence in the APIs and
architecture of Modus 1.0 in terms of basic performance - or the
ability to improve performance without changing the API / architecture.
> Well, depends on what "fast" means. Is less than 500 ms for a page
> that
> both displays all the press releases and has everything necessary to
> edit
> one (including having retrieved one from persistence to get it ready to
> edit)? For most applications that is acceptable performance. Is it
> great?
I don't believe it's acceptable.
> "Linked" components (the "foreign key" reference model) are the way to
> go
> for Modus.
Agreed.
> Oh, yes. I never intended for pressRelease.renderHTML(), only
> pressRelease.getField("image").renderHTML() -- which just outputs:
>
> <img src="http://[serverPath]/imageName.gif">
OK. I'd still prefer separation of presentation from persistence of
course :)
> Yes, label is "friendly name" -- localization is a great issue. I
> guess it
> seems like someone who is building a multi-lingual site will need to
> find an
> alternative way if they want some generic form generation to happen (or
> submit changes to Modus that allow it ;)
As long as the Modus architecture doesn't *preclude* localization, I'll
be reasonably satisfied...
"I can smell your brains!"
-- Mittens the Kitten : http://www.matazone.co.uk/theotherside.html
|
|
From: Nathan D. <na...@ch...> - 2002-11-05 16:03:38
|
I've gotta run out, but just a quick note . . . > > Yes, definitely not just persistence. It's about doing away with a > > lot of > > mundane, repetitive tasks while offering a tight metaphor for coding. > > I guess my concern is that I believe we need a solid persistence layer > first, on which to build an application framework. I'm worried that by > trying to create a single all-purpose layer, it won't do either very > well. Yes, agreed. The notion of the "persister" in Modus is a move towards abstracting the persistence layer away from the contentObject API itself (though, the baseContentObject has some convenience methods that pass through the persister). Right now, each instance of the baseContentObject has its own reference to a persister (which in the current "build" literally creates a new instance of a persister!!), and each contentObjectType will have will have the ability to say which persister it wants to use (any persister must extend and "implement" the basePersister). I do think that before 1.0 we need to move away from having a separate instance of a persister in each contentObject instance, but I am not sure if the developer should need to worry about the persister -- that is, it seems very user friendly to be able to just say pressRelease.save() and have that deal internally with passing this to the persister.store(). Would you disagree? > > True, true. Though, personally performance tends to be a lower > > priority for > > me because I rarely work on sites that have huge volume. > > I think performance is important enough in real life for enough CFers > that we do need to consider it. Yes, but not prioritize it. I just think there are more important concerns for Modus given the audience. Thus, performance must be acceptable, not optimal. > > > wouldn't be a big deal in my mind if Modus 1.0 was not an "Enterprise > > Class" > > system if it is useful to a large swath of CF folks. > > It would be nice if Modus 2.0 was not radically different to Modus 1.0 > tho' so I think we need to get enough confidence in the APIs and > architecture of Modus 1.0 in terms of basic performance - or the > ability to improve performance without changing the API / architecture. Oh, absolutely. I think the API should be fairly stable before it's called 1.0 -- I am talking about making extensions to it in 2.0 and/or putting more energy into the underlying machinery for 2.0. > > > Well, depends on what "fast" means. Is less than 500 ms for a page > > that > > both displays all the press releases and has everything necessary to > > edit > > one (including having retrieved one from persistence to get it ready to > > edit)? For most applications that is acceptable performance. Is it > > great? > > I don't believe it's acceptable. Hmm, we'll have to agree to disagree. The vast majority of ColdFusion sites never deal with many simultaneous requests. I think 500ms should be a ceiling, but it is within a range for a "heavy" page that is not out of bounds, IMO. What would an acceptable ceiling be for you? I'm actually a little surprised given how many pages on macromedia.com don't possibly load faster. Well, they may load faster on the server, but then the Flash movie has to "load" -- the perceived time on MANY pages (including the home page) at macromedia.com is well over 1 second, no?? |
|
From: Sean A C. <se...@co...> - 2002-11-05 17:30:45
|
On Tuesday, Nov 5, 2002, at 08:05 US/Pacific, Nathan Dintenfass wrote: > I've gotta run out, but just a quick note . . . I'm on BART, but just a quick note . . . :) > Yes, agreed. The notion of the "persister" in Modus is a move towards > abstracting the persistence layer away from the contentObject API > itself > (though, the baseContentObject has some convenience methods that pass > through the persister). Excellent! I wasn't clear from our earlier conversations whether that was indeed a guiding principle for you or not. > Right now, each instance of the baseContentObject > has its own reference to a persister (which in the current "build" > literally > creates a new instance of a persister!!), and each contentObjectType > will > have will have the ability to say which persister it wants to use (any > persister must extend and "implement" the basePersister). Yes, that makes sense. > I do think that > before 1.0 we need to move away from having a separate instance of a > persister in each contentObject instance, but I am not sure if the > developer > should need to worry about the persister -- that is, it seems very user > friendly to be able to just say pressRelease.save() and have that deal > internally with passing this to the persister.store(). Would you > disagree? Well, the problem with that is that it means each content object instance must inherit from a class that knows how to save() and load() and so on. It's precisely this overhead that I'm suggesting we try to avoid. As Jeremy has discovered, object instantiation times are affected dramatically by inheritance and the # of methods. Furthermore, there's actually a problem with inheriting from a class that knows about persistence - in fact there are two main (design) problems: 1. it forces all your content objects to be persistable (or at least know about the persistence machinery) 2. it precludes making a content object from something that inherits from some other base type Let me explain those: 1. all content objects have save() and load() methods (because their base class has them) but you may want transient content objects - so it raises the question of what the default implementation for those methods is (as you say, you can have a range of persisters). 2. is the bigger issue. Suppose I have a vehicle component and I derived car and train from that. I can't easily make a persistent car if I need to inherit from some content object base class. This is why C++ has mixins and why Java has interfaces. You need a way to blend the implementation of one entity (car in this case) with the *interface* of another (content object in this case). My suggested approach - making persistence a *service* - allows you to side-step this. I can easily make my car object persistable by creating an XML descriptor for it (externally) and then binding it into the persistence system dynamically. Sure, it means you can't save honda.save() and instead you need to create a carPersister (binding in the car XML descriptor) and then say carPersister.save(honda) but that isn't really a big hardship - yet it buys a lot of flexibility AND it removes the per-instance overhead of the persistence mechanism (methods, metadata, inheritance). If CF had real interfaces, I wouldn't be as worried about this from a design point of view. If CF was higher-performance in its OO support, I might also concede this point on the grounds of simplicity. But neither of those are true so I'm going to stick to the idea of persistence-as-service (and would push for presentation-as-service too). Besides, a plugin-based architecture like that is very flexible and is well-enough documented in the literature that developers shouldn't find it too alien (note that Fusebox MX uses a plugin architecture, much to my surprise - and delight!). > Well, they may load faster on the server, but then the Flash movie > has to "load" -- the perceived time on MANY pages (including the home > page) > at macromedia.com is well over 1 second, no?? Yes, in fact our target for load time is well over a second - for a fully-loaded, interactive page (I don't have the thresholds on hand). However, what we are talking about with Modus is the time it takes to load individual objects. A very common scenario on a website is a summary page that displays, say, twenty items for the user to scroll through. If it takes Modus 500ms to load an object and render two or three fields (title, date, author for example), then that page will take well over TEN SECONDS to load in total. I would say that we should be aiming for a system that can load and render a 50 item summary table in around 5 seconds. That's a combined load and render time of just 100ms per item. "I can smell your brains!" -- Mittens the Kitten : http://www.matazone.co.uk/theotherside.html |