Skip navigation
Currently Being Moderated

Stumped - Variable exists, then does not.

Oct 24, 2012 11:03 AM

Whew!  This is a new one on me!  One of those times CF simply isn't making any logical sense to how I understand it.  I've even gone through CF10 Application Server and IIS Admin service restarts and still getting the same problem.

 

In short, I have a template component which has a variable called rawCode.  It is the current request's generated HTML that is to be output to the buffer.  I am inside a method inside the template component, and I dump: THIS.rawCode.

 

Sure enough, it contains what I expect.

 

I then call a method from within this method (that method exists in the template component as well)

 

<cfinvoke component="#THIS#" method="writeOutputToBuffer">

 

All that method does right now is:

 

<cfoutput>#THIS.rawOutput#</cfoutput>

 

But guess what?  At that point, there's nothing!  Nothing is presented to the user.  I had it dump THIS.rawCode and BOOM, the rawCode is there and populated with what I wante returned to the user.  Has anyone seen something like this before?  I'm sure there's some rare thing I'm doing wrong here, like some recently unknown "CF10 can only support referencing a variable 20 times per request" rule or something as crazy.

 
Replies 1 2 Previous Next
  • Currently Being Moderated
    Oct 24, 2012 11:18 AM   in reply to Aegis Kleais

    You say "THIS.rawCode" at the top of your post, but refer to "THIS.rawOutput" in the method call.

    -Carl V.

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 11:38 AM   in reply to Aegis Kleais

    Show us more code. What does this "template component" look like?  How does it get populated with the "rawCode". How are you invoking the Component? 

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 11:40 AM   in reply to Aegis Kleais

    Dunno.  But two things.

     

    1. If you're within the same CFC, you don't need to specify the COMPONENT attribute of the <cfinvoke> tag.  Perhaps because you are passing it THIS, it's doing something other than you expect?  Not sure.
    2. For pity's sake, don't use <cfinvoke>.  Just go <cfset writeOutputToBuffer()>

     

    --

    Adam

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 12:04 PM   in reply to Aegis Kleais

    cfinvoke is an evil beast that serves a dark master. It is of occasional use, but in 99 out of 96 cases, it is ugly, ugly, and ugly.

     

    You know you can name variables the other way too, right?

     

    writeOutputToBuffer( arg1=arg1, arg1=arg2, arg1=arg3 )

     

    jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 12:04 PM   in reply to Aegis Kleais

    Does writeOutputToBuffer() have output="true"?

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 12:48 PM   in reply to Aegis Kleais

    To my understanding (which can be wrong), when true, the function is processed as if wrapped in a CFOUTPUT (and vice versa for false)  I have:

     

    Nope.  The OUTPUT attribute looks like it's boolean, but it's actually got three distinct states.

     

    TRUE = as if the code within is wrapped in <cfoutput> tags

    FALSE = as if the code within is wrapped in <cfsilent> tags

    [not specified at all] = just like any other CFML code: text will output, CF expressions will be processed if if surrounded in pound signs and <cfoutput> tags.

     

    --

    Adam

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 1:27 PM   in reply to Aegis Kleais

    Aegis Kleais wrote:

     

    But even as such, wrapping my code in <CFOUTPUT> should have still output it to the buffer right?

     

    No, that's not right.  Why don't you try it.  You could have typed output="true" a thousand times since I suggested it, instead you keep arguing back and forth. Try it.

     

     

    Aegis Kleais wrote:

     

    I've been testing it and it seems if I CFABORT in the writeOutputToBuffer() after CFOUTPUT-ing it, it shows up!..... What the funk 'n wagnalls is going on here?

    <cfabort> flushes the output buffer (or some techno jargon like that) and so it does nto matter if output="true".  No matter where you are (cfsilent, etc) if you do a dump + abort it will always show up.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 3:40 PM   in reply to Aegis Kleais

    So did you add output="true" to writeOutputToBuffer() or not?  How about you try what people suggest, and then if it works you ask why? 

     

    I'm glad that you think that this is a great way for you to learn, but you have to understand that when you don't even try what is suggested then you only serve to frustrate those who are trying to help you.

     

    To my knowledge you still have not tried the 2 second fix that I suggested. I'm done trying to help you.

     

    Stepping all the way back,

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 4:09 PM   in reply to Aegis Kleais

    And where did you tell me that you added output="true" to your function? And where did you tell me that that worked and that your problem was solved?

     

    I missed it amongst all the other learning.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 4:24 PM   in reply to Aegis Kleais

    Listen, I'm sorry that I got so upset. I strive to help people and I got frustrated. I apologize for that.

     

    In all seriousness, I suggested several times that you try adding output="true" to your function and I can see no where in this thread where you actually said you tried that. I see where you tell me that it is set to false, but I don't see where you tell me that you tried my suggestion. That is frustrating.  I tried helping you and it just seemed that you were repeatedly ignoring my advice. I understand that you don't just want an answer and that you want to learn. I respect that. But couldn't you try what I suggested, report back and ask questions about it?  I'm more than happy to answer questions. But when I feel like my advice is being completely ignored, I tend to get a little irritated. I suspect that I am not alone in that feeling.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 4:46 PM   in reply to Aegis Kleais

    Cool. Thank you for accepting my apology.

     

     

    Aegis Kleais wrote:

     

    ** UPDATE ** I might have another temporary issue that is skewing my results, so don't take what I just said to heart yet.  I was going through my code changing CFINVOKE's to <cfset path.to.functionName( arg='value' ) as suggested by Adam, and I noticed that when I request my hostname, I don't see it redirecting to the login page, like it should.

     

     

    As Jason suggested

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 24, 2012 5:08 PM   in reply to Aegis Kleais

    You're welcome.  Glad it worked out.

     

    jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 1:19 AM   in reply to 12Robots

    I was going through my code changing CFINVOKE's to <cfset path.to.functionName( arg='value' ) as suggested by Adam, and I noticed that when I request my hostname, I don't see it redirecting to the login page, like it should.

     

     

    As Jason suggested

     

     

    Whilst we're splitting hairs (and, seriously, I don't care), but it was me who suggsted getting rid of the <cfinvoke> tags in favour of just using method calls.  You (Jason) blathered on about evil and that sort of nonsense, but did clarify the name=value pairing thing.

     

    --

    Adam

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 5:55 AM   in reply to Adam Cameron.

    Of course, you are aright. I thought about that last night as I was trying to sleep. You beat me to the correction.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 6:05 AM   in reply to Aegis Kleais

    You should step back a little at this point.  I'm not sure if this was on my side or not, and I'm a proponent in the belief that communication through text-only mediums can often lead to miscues and inferred inflection of emotion that isn't there, but nobody is arguing with you.

     

    Hey guys, I know you've kissed and made up 'n' all, but just from someone on the periphery...

     

    Aegis, Jason did have a point that you tend to waffle a bit about your explorations and go off on tangents rather than either just doing what we've asked, or if you have: letting is know that you've done what we asked.  The findings and wafflings are good (I do mean this), but TBH, it'd be great if you could just focus on the problem you've raised, let us help you with it, get it sorted, then we can cover the peripheral musing or asides or what-have-you separately.  It's difficult to work out where we are at with getting the problem solved some times.  I was in exactly the same position as Jason was last night, not knowing whether you'd actually tried what was suggested or not.

     

    Jason: yeah, slightly surly there mate (and, yes, pot-kettle etc).  But TBH, I thought it wasn't entirely misplaced.  Well the message was correct if not perhaps the delivery. 

     

    Anyway: this post is motly pointless, but it was my reading of the latter part of the thread, anyhow.

     

    Cheers.

     

    --

    Adam

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 6:57 AM   in reply to 12Robots

    Someone just asked me offline, because of this thread, why I thought cfinvoke was evil. I figure I would post my response here to answer for anyone else that may be interested.

     

    Two reasons

     

    1. It is terribly, terribly, terrible verbose. Consider these two equivilent code blocks:

     

    <!--- non-cfinvoke version --->

    <cfset myvar =  myObject.myCustomFunction(arg1=arg1, arg2=arg2)  />

     

    <!--- cfinvoke version --->

    <cfinvoke component="#myobject#" method="myCustomeFunction" returnvariable="myvar">

         <cfinvokeargument name="arg1" value="#arg1#" />

         <cfinvokeargument name="arg2" value="#arg2#" />

    </cfinvoke>

     

    This extremely simple function takes almost 4x more characters to type as a cfinvoke than otherwise. Extrapolate that to larger functions and you get an ugly, ugly, hard-to-read mess. All that code just runs together in my eyes and it's hard ot tall where the arguments are, what the return variable will be etc. The non-cfinvoke version is so much cleaner and to anyone that has programmed using any other language, it is obvious what is going on.

     

    2. cfinvoke cannot create persistent objects

     

    If you use cfinvoke to create an object, you cannot reuse that object. So in most cases, you're going to have to use createObject() anyway to create the object for cfinvoke to use which removes half of the once perceived benefit of cfinvoke.

     

    Hope that clears it up,

     

    cfinvoke is just ugly syntax. ColdFusion gets enough grief about being verbose and ugly. Let's not make it worse by using cfinvoke when there are better, easier, more useful and effective options.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 7:06 AM   in reply to 12Robots

    I agree with everything you say.

     

    Bear in mind, though, that it's a fairly common practice to have an init() method in a CFC these days, so one can do this:

     

    <cfinvoke component="Foo" method="init" returnvariable="myFoo">

     

    Still, that's a bit rubbish compared to:

     

    <cfset myFoo = new Foo()>

     

    There's also another reason: <cfinvoke> is a peculiarity of CFML, and will be meaningless to anyone from outwith CF.

     

    Whereas these syntaxes (below) are going to be either familiar or easily inferable to most people coming from other popular languages:

     

    myFoo = new Foo();

    myFoo = createObject("Foo");

     

    --

    Adam

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 9:53 AM   in reply to Adam Cameron.

    Just a little addition to Adam's points on cfinvoke. I think that Jason is being too hard on cfinvoke.

     

    Why focus just on the invocation of methods? Take a look at cfinvoke's list of attributes. It contains additional functionality that is beyond the reach of any one single type of createobject() or <cfobject>. Also, with cfinvoke we can invoke a method stored in a dynamic variable more elegantly than with createobject, for example, like this:

     

    <cfinvoke component="Foo" method="#someDynamicVar#" returnvariable="myFoo">

     

     

    [edited emphasis: any one single type]

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 10:14 AM   in reply to BKBK

    I will agree that cfinvoke is useful for invoking methods when the methods name is stored in a variable.

     

    I will not, however, concede that there is anything else that cfinvoke can do that createObject() cannot do more eloquently and with less code.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 10:17 AM   in reply to Aegis Kleais

    >> <cfinvoke component="www.app.coms.template" returnvariable="REQUEST.template">

     

    Where is the method you are invoking?  If infact you meant:

     

    <cfinvoke component="www.app.coms.template" returnvariable="REQUEST.template" method="init">

     

    Where init() returns an instance of the object, then yes, you could reuse that object, but it only works if you have an init method and the init method returns an instance of the object. Not all objects are guaranteed to have such a method. createObject() will create an instance of the object and return it regardless of the availability of an init() method.

     

    Jason

     


     
    |
    Mark as:
  • Currently Being Moderated
    Oct 25, 2012 11:44 AM   in reply to Aegis Kleais

    Aegis Kleais wrote:

     

    Anyways, onRequestStart() I create 2 request-based instances off the APPLICATION instance components, namely:

     

    <cfset structInsert( REQUEST, 'error', APPLICATION.coms.error, true )>

    <cfset structInsert( REQUEST, 'template', APPLICATION.coms.template, true)>

     

    No, you aren't creating 2 request-based instances off the APPLICATION scope. You are adding two references to the object in the application scope. The objects that are shared by all users. Your users are not gettign their own instance of the objects per request, they are all sharing the same objects.

     

    structInsert() does nto make a copy of an object, it creates a new reference to the object. If you want each user to have their own instance of the objects on every request, then you need to createObject() on every request, probably in onRequestStart().

     

    If however all you need is for each user to have access to these singletons, then you can access them directly from the APPLICATION scope, there is no need to make a reference in the request scope.

     

    What you need depends on what those objects are actually doing.

     

    Jason

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 26, 2012 4:38 AM   in reply to Aegis Kleais

     

    • APPLICATION.coms.data
    • APPLICATION.coms.error
    • APPLICATION.coms.security
    • APPLICATION.coms.template
    • APPLICATION.coms.utility

     

    [...]

     

    Having assumed this is all good, I realized I don't need to send the THIS.datasource variable to the APPLICATION.coms.data component object's init() function, because since that component extends the root application, it shares the THIS.datasource variable, and has access to it anyways.

     

     

    I meant to comment on this.  Those CFCs you mention should not extend Application.cfc.  Only other Application.cfcs should extend an Application.cfc.  When using OO inheritance, you ought to follow the "is a" rule.  An Apple IS A Fruit, so Apple.cfc can extend Fruit.cfc.  An Apple is NOT an Orange, so Apple.cfc should not extend Orange.cfc.

     

    Data is not an Application, so it should not extend Application.cfc.

     

    You are using inheritance to make variable access more convenient, which is something one ought not do.

     

    There is another rule "has a" which would work (kind of) here.  A Data.cfc might HAVE A application context (ie: some settings from the Application.cfc), so this suggests Data.cfc should get initialised with an applicationContext object (or perhaps just a struct, to save too much horsing around), which would hold DSN values etc.

     

    But to be honest, Data.cfc probably just needs the DSN passed into it, yes?  So don't mess around with application context objects/structs; just give its init() a DSN argument, and initialise it accordingly.

     

    Or use ColdSpring to manage all of this sort of thing for you.

     

    --

    Adam

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 26, 2012 6:11 AM   in reply to Aegis Kleais

    Aegis Kleais wrote:

     

    It DID, however show me what the init() function is, and how it can customize the instantiated copy of that component.  Very powerful.  A lot of things are beginning to click, and it's been thanks to you guys explaining these rather advanced intricacies.  If I understand it correctly, if you had a component called dog.cfc, and it had a variable which held the name of the dog (defaulted to nothing), I could instantiate 2 dogs like:

     

    <cfset REQUEST.dog1 = createObject( 'www.app.coms.dog' ).init( name='HeyYou' )>

    <cfset REQUEST.dog2 = createObject( 'www.app.coms.dog' ).init( name='Mr.ChewyBiteUms' )>

     

    Each has it's own copy of internal variables and access to that component's methods, which can reference those unique instance variables to perform in different ways.

    That might indeed be so. However, there is one point I hope you will pay attention to. It relates to maintenance and is variously called "The Law of Demeter", "Principle of Least Knowledge", "Only talk to your friends" or "Don't talk to strangers".

     

    This is a software design rule that says that an object should avoid invoking the methods of a member object returned by another method. In other words, the rule says your code should, when invoking a method, use as few dots as possible, ideally just one. Thus, a.methodA() is preferable to a.b.methodB().

     

    The following pseudocode illustrates this:

     

    component A{

    methodA () {

    b = createobject("B").init();

    return b;

    }

    }

     

    component B{

    methodB () {

    // functionality needed by A

    }

    }

     

    Consider the code a.b.methodB(). The object a creates the object b by a method call, and uses b to call methodB(). This kind of chaining makes a component to be dependent on the internal structure of others. If you wish to change a component in such circumstances, you will have to rework all its callers.

     

    I'll borrow this line of code from you:

     

    REQUEST.dog = createObject( 'www.app.coms.dog' ).init( name='HeyYou' );

     

    Let us combine it with an often quoted Demeter example. 

     

    REQUEST.dog.getBody().getTail().wag();

     

    Programmers are advised to avoid writing code-chains like that. This is bad because that one line depends on the structure of three different objects. This style of coding creates structural dependencies (coupling) between unrelated objects. If you use it throughout, your software will be a nightmare to maintain.

     

    The solution is to re-engineer the example as follows:

     

    REQUEST.dog.expressHappiness();

     

    This leaves it up to the implementation of the dog to decide what to do when the bone appears.

     
    |
    Mark as:
1 2 Previous Next
Actions

More Like This

  • Retrieving data ...

Bookmarked By (0)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points