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.
Related
I use the mail() object in cfscript. I want to extend that object so I can overwrite the setTo() method. Here is the cfc code I have written.
component extends="com.adobe.coldfusion.mail"
{
public void function setTo(String recipients) {
machineName = createObject("java", "java.net.InetAddress").localhost.getCanonicalHostName();
if (FindNoCase("devcomputer", machinename) == 0)
{
super.setTo(arguments.recipients);
}
else
{
super.setTo(this.getFrom());
}
}
}
When this runs however, I get a message saying the setTo() method does not exist at the line calling super.setTo(). Digging further I looked at the super object and it inherits from java.lang.Class, not com.adobe.coldfusion.email.
What is the proper way to extend ColdFusion's mail object so I can override the setTo() method?
The getters/setters in com.adobe.coldfusion.mail are actually not functions, but accessors. Accessors are automatically generated by ColdFusion based on the properties in the component. Properties are inherited, accessors are not!
The accessors in the mail component do nothing but set/get the value of the property. The equivalent of super.setTo(arguments.recipients); thus is variables.to = arguments.recipients;. The equivalent of this.getTo() is variables.to etc.
Note: Using accessors="true" with the component that extends="com.adobe.coldfusion.mail" does not work with inherited properties either.
I want to add a mixin to an Ember class which has already been created. (The class is defined in a library, actually Ember itself; it's LinkView).
I see that we can do mixin.apply(obj), but this applies the mixin to an instance of the class. I want to add the mixin to the class, so it's automatically mixed-in to all newly created objects.
I attempted to override the init method of the class, using reopenClass, and do mixin.apply(this) there, to apply the mixin to the instance, and then call the original init method, but this does not seem to work because the mixin wiring is set up in the init method and it's already too late by the time I can get to it.
reopenClass does not seem to accept a mixin argument like extend does. Its code seems to suggest that it's doing something with mixins, but whatever it is it doesn't work:
a = Ember.Object.extend().reopenClass(Ember.Mixin.create({mixval: 1});
a.create().get('mixval'); // undefined
I know that I could create my own class with MyLinkView = Ember.LinkView.extend(mixin, ..., but unfortunately the original class name is referenced explicitly within the library, so I really would prefer to figure out how to extend that original class with my mixin.
I experimented with Ember.LinkView = Ember.LinkView.extend(mixin, .... This somehow seems dangerous, although it seems to work. But in this particular case it doesn't help me since the reference within the Ember code (in the definition of the {{link-to}} helper) is to an internal version of the class, not the fully qualified Ember.LinkView.
Any ideas?
The solution is simply
Klass = Parent.extend({init: {...}});
Mixin = Ember.Mixin.create({init: {...}});
Klass.reopen(mixin);
Everything works as expected, including the super chain. In other words, a call to Klass.create().init() will call the mixin's init, and a call to super from there will call the original Klass#init.
In the course of researching this question, I discovered something interesting about reopen. Even if the argument is not a mixin, it is treated as one (internally, it actually creates a temporary one). This means that if you do
Klass.reopen({
init: function() {
this._super.apply(this, arguments);
}
});
The call to super is actually calling the original init, not the init in the parent class. In other words, specifying init in a reopen does not replace the existing init on the class, it more or less layers on top of it. I can't see this behavior documented anywhere in the Ember docs, but it does seem useful in the right situation.
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.
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.