With your code Bob (I had to piece it together from your site, so I'm
posting the basic decorator here) I simply created
abstractDecorator.cfc:
<cfcomponent displayname="abstractDecorator" output="false"
extends="transfer.com.TransferDecorator">
<cffunction name="configure" access="private" returntype="void"
hint="I am like init() but for decorators">
<cfset variables.myInstance = StructNew() />
<cfset variables.myInstance.CleanseInput = true />
</cffunction>
<cffunction name="populate" access="public" output="false"
returntype="void" hint="Populates the object with values from the
argumemnts">
<cfargument name="args" type="any" required="yes" />
<cfargument name="FieldList" type="any" required="no" default="" /
>
<cfset var theFieldList = "" />
<cfset var TransferMetadata = getTransfer().getTransferMetaData
(getClassName()) />
<cfset var Properties = TransferMetadata.getPropertyIterator() />
<cfset var theProperty = 0 />
<cfset var varName = 0 />
<cfset var varType = 0 />
<cfset var varValue = 0 />
<cfset var CompType = 0 />
<cfset var hasIterator = false />
<cfset var theIterator = 0 />
<cfset var theComposition = 0 />
<cfset var ChildClass = 0 />
<cfset var ChildPKName = 0 />
<cfset var theChild = 0 />
<cfloop condition="#Properties.hasnext()#">
<cfset theProperty = Properties.next() />
<cfset varName = theProperty.getName() />
<cfset varType = theProperty.getType() />
<cfif NOT ListLen(arguments.FieldList) OR ListFindNoCase
(arguments.FieldList,varName)>
<cfif varName EQ "LastUpdateTimestamp" AND varType EQ "Date">
<cfset setLastUpdateTimestamp(Now()) />
<cfelseif Right(varName,4) EQ "Flag" AND varType EQ "Numeric">
<cfif StructKeyExists(arguments.args,varName)>
<cfset varValue = Val(arguments.args[varName]) />
<cfelse>
<cfset varValue = 0 />
</cfif>
<cfinvoke component="#this#" method="set#varName#">
<cfinvokeargument name="#varName#" value="#varValue#" /
>
</cfinvoke>
<cfelseif StructKeyExists(arguments.args,varName)>
<cfset varValue = arguments.args[varName] />
<cfif variables.myInstance.CleanseInput>
<cfset varValue = HTMLEditFormat(varValue) />
</cfif>
<cfif IsValid(varType,varValue)>
<cfinvoke component="#this#" method="set#varName#">
<cfinvokeargument name="#varName#"
value="#varValue#" />
</cfinvoke>
<cfelseif theProperty.getIsNullable() AND NOT Len(varValue)
>
<cfinvoke component="#this#"
method="set#varName#Null" />
<cfelse>
<cfinvoke component="#this#"
method="setInvalid_#varName#">
<cfinvokeargument name="1" value="#varValue#" />
</cfinvoke>
<cfset ArrayAppend(arguments.args.Errors,"The contents
of the " & varName & " field must be a valid " & varType & " value.") /
>
</cfif>
</cfif>
</cfif>
</cfloop>
<cfloop list="ManyToOne,ParentOneToMany" index="CompType">
<cfinvoke component="#TransferMetadata#" method="has#CompType#"
returnvariable="hasIterator" />
<cfif hasIterator>
<cfinvoke component="#TransferMetadata#"
method="get#CompType#Iterator" returnvariable="theIterator" />
<cfloop condition="#theIterator.hasnext()#">
<cfset theComposition = theIterator.next() />
<cfset varName = theComposition.getName() />
<cfset ChildClass = theComposition.getLink().getTo() />
<cfset ChildPKName = theComposition.getLink().getToObject
().getPrimaryKey().getName() />
<cfif StructKeyExists(arguments.args,ChildPKName)>
<cfset varValue = arguments.args[ChildPKName] />
<cfset theChild = getTransfer().get
(ChildClass,varValue) />
<cfif theChild.getIsPersisted()>
<cfif CompType CONTAINS "Parent">
<cfset varName = "Parent" &
theComposition.getLink().getToObject().getObjectName() />
</cfif>
<cfinvoke component="#this#"
method="set#varName#">
<cfinvokeargument name="transfer"
value="#theChild#" />
</cfinvoke>
</cfif>
</cfif>
</cfloop>
</cfif>
</cfloop>
</cffunction>
<cffunction name="onMissingMethod" access="public" output="false"
returntype="Any" hint="Very useful!">
<cfargument name="missingMethodName" type="any" required="true" />
<cfargument name="missingMethodArguments" type="any"
required="true" />
<cfset var varName = 0 />
<cfset var ReturnValue = "" />
<cfif Left(arguments.missingMethodName,Len("setInvalid_")) EQ
"setInvalid_" AND StructKeyExists(arguments.missingMethodArguments,"1")
>
<cfset varName = ReplaceNoCase
(arguments.missingMethodName,"setInvalid_","") & "_Invalid" />
<cfset variables.myInstance[varName] =
arguments.missingMethodArguments.1 />
<cfelseif Left(arguments.missingMethodName,Len("getInvalid_")) EQ
"getInvalid_">
<cfset varName = ReplaceNoCase
(arguments.missingMethodName,"getInvalid_","") & "_Invalid" />
<cfif StructKeyExists(variables.myInstance,varName)>
<cfset ReturnValue = variables.myInstance[varName] />
</cfif>
</cfif>
<cfreturn ReturnValue />
</cffunction>
<cffunction name="copyToStruct" access="public" output="false"
returntype="void" hint="Copies data from the TO into a Struct">
<cfargument name="theStruct" type="any" required="false"
default="#StructNew()#" hint="A struct to receive the data" />
<cfargument name="Overwrite" type="any" required="false"
default="true" hint="Should existing values in the struct be
overwritten?" />
<!--- Get the MetaData and Properties --->
<cfset var TransferMetadata = getTransfer().getTransferMetaData
(getClassName()) />
<cfset var Properties = TransferMetadata.getPropertyIterator() />
<cfset var PrimaryKey = TransferMetadata.getPrimaryKey() />
<cfset var theProperty = 0 />
<cfset var varName = 0 />
<cfset var varType = 0 />
<cfset var varValue = 0 />
<cfset var i = 0 />
<!--- Put the Id into the Struct --->
<cfset varName = PrimaryKey.getName() />
<cfinvoke component="#this#" method="get#varName#"
returnvariable="varValue" />
<!--- Need cftry because if overwrite is false an error can be
thrown --->
<cftry>
<cfset StructInsert
(arguments.theStruct,varName,varValue,arguments.Overwrite) />
<cfcatch type="any"></cfcatch>
</cftry>
<!--- Put the properties into the theStruct --->
<cfloop condition="#Properties.hasnext()#">
<cfset theProperty = Properties.next() />
<cfset varName = theProperty.getName() />
<cfinvoke component="#this#" method="get#varName#"
returnvariable="varValue" />
<cftry>
<cfset StructInsert
(arguments.theStruct,varName,varValue,arguments.Overwrite) />
<cfcatch type="any"></cfcatch>
</cftry>
</cfloop>
<!--- Add the extra properties into the Struct --->
<cfif IsDefined("variables.myInstance.ExtraProperties") AND IsArray
(variables.myInstance.ExtraProperties) AND ArrayLen
(variables.myInstance.ExtraProperties)>
<cfloop from="1" to="#ArrayLen
(variables.myInstance.ExtraProperties)#" index="i">
<cfinvoke component="#this#"
method="get#variables.myInstance.ExtraProperties[i]#"
returnvariable="varValue" />
<cftry>
<cfset StructInsert
(arguments.theStruct,variables.myInstance.ExtraProperties
[i],varValue,arguments.Overwrite) />
<cfcatch type="any"></cfcatch>
</cftry>
</cfloop>
</cfif>
</cffunction>
</cfcomponent>
I then had my regular decorators for each object (such as
components.decorators.post for my "
posts.post" Transfer object) extend
this abstractDecorator, so:
<cfcomponent displayname="post" output="false"
extends="components.decorators.abstractDecorator">
<cfproperty name="postID" type="numeric" default="" />
<cfproperty name="postTitle" type="string" default="" />
</cfcomponent>
Now I can simply do:
post1 = instance.Transfer.get("
posts.post", rc.postID);
post2 =
instance.Transfer.new("
posts.post");
tempTO = structNew();
post1.copyToStruct(tempTO);
post2.populate(tempTO);
instance.Transfer.save(post2);
This effectively makes a copy of post1 to post2 and saves it.
Excellent work Bob and thank you!
On Nov 5, 2:00 pm, Bob Silverberg <
bob.silverb...@gmail.com> wrote:
> Hmm, I've never even heard of listColumns(). If it were me, I'd use the
> metadata that Transfer makes available for just this purpose. In case you
> didn't see it, here's a link to a post I did about using Transfer's metadata
> to create your own implementation of getMemento():
http://www.silverwareconsulting.com/index.cfm/2008/6/17/Using-Transfe...
>
> Cheers,
> Bob
>
>
>
>
>
> On Thu, Nov 5, 2009 at 4:40 PM, Jim Rising <
cfflex...@jimrising.com> wrote:
> > what about listColumns() ?
>
> > <cffunction name="copyTransferObject" output="false" hint="I copy one
> > transfer object to another">
> > <cfargument name="aObject" required="true"
> > type="transfer.com.TransferObject">
> > <cfargument name="bObject" required="true"
> > type="transfer.com.TransferObject">
> > <cfset var aObject = arguments.aObject>
> > <cfset var bObject = arguments.bObject>
>
> > <cfset bColumns = bObject.listColumns()>
> > <cfloop list="#bColumns#" index="columnName">
> > <cfset getter = "aObject.get#columnName#()">
> > <cfset columnValue = Evaluate(getter)>
> > <cfset setter = 'bObject.set#columnName#("#columnValue#")'>
> > <cfset Evaluate(setter)>
> > </cfloop>
> > <cfreturn bObject />
> > </cffunction>
>
> > On Thu, Nov 5, 2009 at 3:23 PM, Bob Silverberg <
bob.silverb...@gmail.com>wrote:
>
> >> I'm sure Mark will correct me if I'm mistaken, but I don't think you
> >> should be calling setTransferObject() in your code. I'm guessing you could
> >> get yourself into all kinds of trouble.
>
> >> On Thu, Nov 5, 2009 at 3:26 PM, John Watson <
wizpu...@gmail.com> wrote:
>
> >>> I'm actually running into issues of losing my decorator methods after
> >>> using the setTransferObject(TO). I haven't had time to debug yet (as this is
> >>> not a production app yet).
>
> >>> I do this inside a "myDecoratedObject" called someobject:
>
> >>> <cfset TO = getTransfer().readByPropertyMap("myDecoratedObject",filter)
> >>> />
> >>> <cfif TO.getIsPersisted()>
> >>> <cfset setTransferObject(TO) />
> >>> <cfelse>
> >>> <!--- code that initializes this object based on the filter and saves it
> >>> --->
> >>> </cfif>
>
> >>> After running this code, randomly (haven't investigated yet; believe it
> >>> somehow related to my filter). "someobject" loses all of its decorator
> >>> methods until I clear the transfer cache.
>
> >>> Bob, I read your blog post:
>
> >>>
http://www.silverwareconsulting.com/index.cfm/2008/7/22/How-I-Use-Tra...
> >>> And i think I will try that out whenever I go back to working on my
> >>> project. Thanks for the tip!
>
> >>> Mark, any plans for implementing something similar?
>
> >>> Thanks,
> >>> John
>
> >>> On Thu, Nov 5, 2009 at 12:11, Jim Rising <
cfflex...@jimrising.com>wrote:
>
> >>>> Mark,
>
> >>>> Thanks. I'll look into it a bit further.
>
> >>>> -jim
>
> >>>> On Thu, Nov 5, 2009 at 1:14 PM, Mark Mandel <
mark.man...@gmail.com>wrote:
>
> >>>>> Probably because you are using a variety of undocumented badness :o(
>
> >>>>> getMemento() , switching out the inner object if a Decorator... all
> >>>>> sounds pretty bad if you ask me.
>
> >>>>> I think Bob's approach of using his memento function is probably the
> >>>>> way to go.
>
> >>>>> Mark
>
> >>>>> On Fri, Nov 6, 2009 at 6:07 AM, Jim Rising <
cfflex...@jimrising.com>wrote:
>
> >>>>>> ok... so here is what I did in my decorator:
>
> >>>>>> <cffunction name="copyContentTemplate">
> >>>>>> <cfargument name="contentTemplate"
> >>>>>> type="transfer.com.transferObject" required="true">
> >>>>>> <cfset var contentTemplate = arguments.contentTemplate>
> >>>>>> <cfset var content = getTransfer().get("content.Content", "")>
>
> >>>>>> <cfset content = setTransferObject(contentTemplate)>
> >>>>>> </cffunction>
>
> >>>>>> now... this does seem to copy a 'contentTemplate' TO into a 'content'
> >>>>>> TO without any issues, but when i attempt to save using the following:
>
> >>>>>> <cffunction name="saveContent" access="public" returntype="any">
> >>>>>> <cfargument name="content" type="transfer.com.transferObject"
> >>>>>> required="true">
> >>>>>> <cfset var content = arguments.content>
>
> >>>>>> <cfset getTransfer().save(content)>
>
> >>>>>> </cffunction>
>
> >>>>>> the object does not save.
>
> >>>>>> any ideas?
>
> >>>>>> -jim
>
> >>>>>> On Thu, Oct 29, 2009 at 11:21 AM, John Watson <
wizpu...@gmail.com>wrote:
>
> >>>>>>> Someone who knows a little more detail about how the caching works
> >>>>>>> may say what I suggest is a bad a idea, but here's a method I've used with
> >>>>>>> no issues yet.
>
> >>>>>>> Inside the decorator I've used the TO.setTransferObject(otherTO)
> >>>>>>> method. Though both the setting object and the TO object are both the same
> >>>>>>> (IE: content/content).
>
> >>>>>>> Another idea may be to use setMemento(mementoStruct)
>
> >>>>>>> John
>
> >>>>>>> On Thu, Oct 29, 2009 at 09:10, Jim Rising <
cfflex...@jimrising.com>wrote:
>
> >>>>>>>> I'm needing to copy one TO to another TO, and wondered if anyone had
> >>>>>>>> any ideas on the best way to do that? Basically I have one TO that needs to
> >>>>>>>> serve as a 'template' of another.
>
> >>>>>>>> Example:
>
> >>>>>>>> I have a TO called 'contentTemplate' and another called 'content'.
> >>>>>>>> I want to be able to create a 'content' object from the
> >>>>>>>> 'contentTemplate' object.
>
> >>>>>>>> I'm thinking I will need to just get / set everything between each
> >>>>>>>> of them, but was hoping I would have another option available.
>
> >>>>>>>> Thanks!
> >>>>>>>> --
> >>>>>>>> Jim Rising
> >>>>>>>> Serial Entrepreneur
> >>>>>>>> Software Engineer
> >>>>>>>> Web Developer
>
> >>>>>>>> "Knowledge work requires both autonomy and accountability."
>
> >>>>>> --
> >>>>>> Jim Rising
> >>>>>> Serial Entrepreneur
> >>>>>> Software Engineer
> >>>>>> Web Developer
>
> >>>>>> "Knowledge work requires both autonomy and accountability."
>
> >>>>> --
> >>>>> E:
mark.man...@gmail.com