Say I want to call a static function from a CFC from within a cfscript block. As far as I know, the only method is using createObject() which returns a reference to the CFC object.
Is this good practice? I think I remember reading that cfinvoke definitely instanciated objects smartly and would not instanciate a static CFC multiple times. Is this even true, and if so, is this still true when using createObject()?
CFOBJECT
CFOBJECT instantiates a component and creates a variable for it.
<cfobject type="component" name="path.to.my.CFC" name="myCFC" />
CFINVOKE
CFINVOKE can then reference the variable created by CFOBJECT, so that it doesn't have to re-create the object again.
<cfinvoke component="#myCFC#" method="foo" returnVariable="myValue" />
So you can call as many CFINVOKEs as you want on #myCFC# without recreating the object.
However, CFINVOKE can also implicitly create the object for you if you don't also use CFOBJECT.
<cfinvoke component="path.to.my.CFC" method="foo" returnVariable="myValue" />
Calling multiple functions in this manner will recreate the object each time.
CREATEOBJECT
createObject() works pretty much the same way. Either create the object first with a reference variable
<cfscript>
myCFC = createObject("component", "path.to.my.CFC");
myValue = myCFC.foo();
otherValue = myCFC.bar();
</cfscript>
or create the object with each function call.
<cfscript>
myValue = createObject("component", "path.to.my.CFC").foo();
otherValue = createObject("component", "path.to.my.CFC").bar();
</cfscript>
I prefer createObject() since I've been using CFSCRIPT as much as possible. And I always create the object first if I'm going to call multiple functions from it.
Related
I have a piece of code which is doing some DOM manipulation calling functions like appendChild and RemoveChild.
so, say my component has a div tag with id property
<div id="header"></div>
Inside my component in one of the functions I am getting hold of the mentioned tag and appending something
this.$.header.appendChild('<div>Hello</div>')
In my unit test I create test fixture with the responsible component and then inside I create a spy like
var testSpy = sinon.spy(Polymer.dom($el.$.header), 'appendChild')
this is done inside my setup. And then inside my it block I check for the spy to be called. But it not getting called. Though the appendChild is being called and I could see the dom correctly as well. But in the expect it is not working. Any help?
So, I was able to figure it out on my own. The only different thing which I did was the way I was getting hold of my DOM while creating the spy:
var testSpy = sinon.spy($el.querySelector('#header'), 'appendChild')
After that my spy started to be called.
Is there a way to explicitly reference the THIS scope defined in Application.cfc?
Say I have an Application.cfc like this:
component {
this.name="MyApplication"
..
I know that from any page in the site, you can access this.name this way...
<cfoutput>#this.name#</cfoutput>
...but if you are in another component, how would you reach the Application.cfc's "this" scope? Is it possible without handing the var off?
Adobe documentation says that you can reference the "THIS" scope by using the instance or object name as a prefix. I tried Application.this.name but it didn't work.
Most of the things that are set in "this" in Application.cfc are not accessible outside the execution of it. ColdFusion copies this.name into application.name at runtime, so you can access application.name from anywhere in your application. For the other settings in "this", they appear to be accessible from your pages because the pages are included into the application.cfc execution cycle by the OnRequest() method.
Once you instantiate a CFC and work inside it's methods, the context of "this" changes to the constructor of that CFC.
I'm looking for the best way to dynamically call a method from a different component in cfscript. Notice that it's concerning a method in a different component. So far I've tried 3 different methods, but none of them seem be exactly what I'm looking for:
All cases are written in cfscript inside a component method. Let's say I'm trying to dynamically call the setName(required string name) method in the MyComponent component. All cases have following variables defined:
var myComp = new MyComponent();
var myMethod = "setName";
var args = {"name"="foo"};
use evaluate() for the job
evaluate("myComp.#myMethod#(argumentCollection=args)");
pros: is done with very little code
cons: code is not very 'clean' and use of evaluate() seems to have an 'evil' reputation in the online community. I wouldn't want my code to be evil.
use a cfml wrapper for <cfinvoke>
invoke("MyComponent", myMethod, args);
pros: I can use all functionality of cfinvoke
cons: It creates a new instance of MyComponent with every invoke.
create a dynamicMethod method in MyComponent
myComp.dynamicMethod(myMethod, args);
dynamicMethod of MyComponent:
public any function dynamicMethod(required string methodName, required struct argumentColl){
var cfcMethod = variables[arguments.methodName];
return cfcMethod(argumentCollection=arguments.argumentColl);
}
pros: I can finally call myComp directly. Most comfortable solution so far.
cons: I can now call private methods of MyComponent via dynamicMethod.
(I've also tried the 'function as variable' solution outside of MyComponent, but then the function looses its working context. e.g. if MyComponent would extend a component, the 'super' scope would no longer refer to the extended component).
None of these solutions seem to be perfect, so is there no other way to call a dynamic function from a different controller?
And if there isn't, which one of these is the best solution?
Any advice is welcome, thanks.
Good analysis.
One thing you could do here is to more-closely emulate <cfinvoke> with your wrapper function. <cfinvoke> will take either a component path or a component instance (ie: an object) in that COMPONENT attribute. So your 'con' of 'It creates a new instance of MyComponent with every invoke.' isn't really valid.
ColdFusion 10, btw, adds a invoke() function to achieve just this. I note you're on CF9, so this is no help to you. But it's perhaps relevant for other people who might land on this question.
Is there a way to access functions through inheritance without setting function access to public?
For example: I have Foo.cfc and it extends Bar.cfc. If I want to call a Bar.cfc function from Foo.cfc, I have to set the function access to public.
If I set the function access to private, then it's only accessible from Foo.cfc. Is there no "intermediate" access level that is not quite public but not strictly private? i.e. it allows access through inheritance only...
Are you using the keyword super? Because private methods should be available to sub components like Foo.cfc.
Foo.cfc
<cfcomponent extends="Bar">
.....
<cffunction name="fooMethod" access="public" ...>
<cfreturn super.nameOfAMethodInBarCFC() />
</cffunction>
</cfcomponent>
If I set the function access to private, then it's only accessible from Foo.cfc
NOT TRUE! private access level in ColdFusion is same as protected in Java, so you can still call that private method of Bar from Foo
You need to set the access property in cffunction to package. That will allow it to be accessed by any component that extends the component.
Heres the situation. Component B extends component A and overrides the init method to accept a different parameter. A also has a create method that calls init.
If i have an instance of B and i call create, its calling the wrong init - it calls init in B, where i need it to call init in A.
I dont want to call super.init() as there may not always be a super. Is there any way to specify to call the init in the parent component?
Refactor the actual code you want to execute into their own methods, and have the Init methods call that code, and have the create method call that code as well.
It sounds like your objects are not well designed at the moment. Try to break your methods down to smaller, more constrained units.
Edit:
I think my answer still stands.
If the Init method in the parent component does some stuff, move that stuff to a new method, say, "initDoStuff(), and have the init method call that method.
Then, have your create method call the initDoStuff() method instead of init.
ColdFusion is a dynamically typed language, and you don't need to override methods just to accept different parameters. You can do that in other ways.
CF isn't able to pick methods based on argument signatures. So, if you have a situation like this, you need to handle it in a different way.
Basically, the idea of overriding a method to change its argument types is not really valid in ColdFuion.
Component A:
<cffunction name="init" access="public" output="false">
<cfargument name=... ...>
<cfreturn initDoSomething(argumentCollection=arguments)>
</cffunction>
<cffunction name="initDoSomething" access="package" output="false">
{do stuff}
<cfreturn {whatever}>
</cffunction>
Component B:
<cffunction name="create" access="package" output="false">
<cfset {something} = initDoSomething({whatever arguments})>
<cfreturn {whatever}>
</cffunction>
call the base initializer I don't know what language you're running but in C# it's for example B b = new B() : base(parameters...) of course you'd still have to check wether the component is in fact of type A or B but you can do that in most languages.
This will also run the extened initializer, but thats the way it is. If you want to get rid of the extended objects initializer you'll just have to create a parent type object I think. It's the only thing I can think of.
But of course it would help if you specified what language you are programming in, in order to know the options and syntax.
I solved this by extending the create method to call super.create and super.init. theres still a call to the child init that fails, but overall it works.
I would still prefer a solution that works on the parent init without having to override the create.