« July 2004 September 2004 »

August 19, 2004

Anonymous, inline, and named functions in ActionScript

Terms like inline, anonymous, named, standard, etc. get thrown around a lot but are not always used correctly. Hopefully this post will shed some light of some of the function terminology that you may have come across in your ActionScript adventures.

  • Named Functions

    A named function is of the form:

    function foo(param1, param2, etc) {
             // do stuff here
    }

    Named functions, no matter where they are defined on a frame, are placed at the front of the actions for a frame at compile time. Therefore, you can forward reference them. That is, I can call "foo()" before I defined the foo function and the function will still execute.

    Named functions can only define functions in the timelines that they are written in.

  • Anonymous Functions

    An anonymous function is defined like this:

    bar = function(param1, param2, etc) {
             // do stuff here
    }

    Anonymous functions are interesting beasts. Unlike named functions, when you create an anonymous function there is no way to reference the function via code. We can get around this with function literals.

    When the function is created it returns a reference to itself. In the example above, we set a variable "bar" to reference the anonymous function we create.

    Now, whenever bar is called, the anonymous function that it references will execute. Unlike named functions, anonymous functions cannot be forward referenced. If I make a call to bar before I set bar to reference the function, the call will fail.

    When creating an anonymous recursive function, the function can reference itself by using the arguments.callee property. Consider this example:

    // classic factorial recursion example
    factorial = function(n) {
             if (n <= 1) {
                      return n;
              } else {
                      return n*arguments.callee(n-1);  // call ourself to recurse
              }
    }

    Note that in the above example, we can replace arguments.callee with factorial and the example will still work. However, there are times in which we don't have a variable to call the function with. In those cases, arguments.callee is the only solution. An example of this might be passing an anonymous function as a parameter to another function.

    Function literals can define functions in any timeline/object, provided the path exists at the time of definition. For instance, to create a function inside of an object, we have to use a function literal like this:

    obj = new Object();
    obj.foo = function() {
         return "bar";
    }
    
    trace(obj.foo());  // bar
  • Inline Functions

    Inline functions do not exist in Flash. This is a common misconception - people tend to refer to function literals as inline functions, which is incorrect.

    The basic idea behind inline functions is to improve execution speed at the sacrifice of program size. Behind the scenes, the inline concept works something like this...

    When a function is created, a block of data is created containing the function code. Then, when a program executes a function call, the program stores is current state, jumps to the location of the function block, executes the code it finds there (the function body), jumps back to where it left off in normal execution and restores its previous state.

    Because of the state shuffling and the extra jumps to the function location, there is overhead associated with any function call. Inline functions are a way around this.

    Instead of jumping to a block of memory, an inline function will just insert the function code directly into the program ("in line"). The code no longer has to jump to the function block and deal with remembering it's current state - it can just continue execution normally. This, however, leads to larger program sizes.

    Image I call a function 10 times. With a function, the code is only inserted into the program 1 time and referenced 10 times through the 10 different function calls. Compare that to inline functions where the function code is inserted into the program 10 times at each different function call.

I realize that a lot of this information is old hat to a lot of people, but I still see some misuse of terms. Hopefully this post helps to clear things up a little for everyone...

August 15, 2004

ColdFusion struct properties converted to uppercase when using Flash Remoting

If you've ever passed a struct from ColdFusion to Flash via Flash Remoting, you've probably noticed that all of the keys for the struct come into Flash as uppercase. In the past, this wasn't that big of a deal, but when Flash Player 7 came around with case-sensitivity, some people started to encounter problems. Here's the simple workaround...

Consider the following code:

<cfcomponent>
  <cffunction name="getSomeData" returntype="struct" access="remote">
    <cfset oData = StructNew() />
    <cfset oData.key1 = "example 1" /> <!--- converts to KEY1 --->
    <cfset oData["key2"]= "example 2" /> <!--- preserves case --->
    <cfset StructInsert(oData, "key3", "example 3") /> <!--- preserves case --->
    <cfreturn oData />
  </cffunction>
</cfcomponent>

Note that accessing a struct in the "usual" way with dot notation will convert the key to all uppercase. By using array (bracket) notation, you can preserve the case of the key. Additionally, if you use the StructInsert function you can also preserve the case of the key.

I hope this post saves someone some debugging time in the future. This question recently came up on a mailing list, so hopefully google will find this post and make the answer easier to locate. :-)

August 10, 2004

Flash Quirk: Disabled buttons still broadcast click

The official status on this one is "Not a Bug." This behavior was done by design, but it's a little bit counter-intuitive and not consistent with what I've experienced in other languages. I wanted to mention it here as another example of a Flash Quirk.

Consider the following code: (drag a v2 button onto the stage and give it an instance name of "testBtn")

import mx.controls.Button;
import mx.utils.Delegate;

var testBtn:Button;
var onClick:Function = function():Void {
 	trace("testBtn clicked!");
}

// call onClick whenever testBtn is pressed
testBtn.addEventListener("click", Delegate.create(this, onClick));

// "press" the button programmatically
testBtn.onRelease();

When the movie is tested, you'll see this code work perfectly. The button press is simulated via ActionScript, and we get the expected trace outpout. Why would you want to simulate a button press through code? The classic example is implementing keyboard shorcuts that invoke button methods.

Now, add this line BEFORE the onRelease call:

// disable the button
testBtn.enabled = false;

Now run the modified code and notice the results. The button was still pressed, even though it was disabled.

Whenever I disable a JButton in Java or a Button in C# and attempt to programmatically press the button (doClick or PerformClick, respectively), the "click" event is not generated. I expected the same behavior out of Flash, but we all know Flash is a very different beast and somehow I'm not surprised.

The workaround is simple, just change the onRelease call to this:

// "press" the button programmatically, but only if it's enabled
if (testBtn.enabled) testBtn.onRelease();

Still though, that just feels wrong to me.

What do you think - is this a bug, or just another quirk? To me, it's a bug since it's unexpected behavior. However, it's officially not a bug (which makes it a quirk) and I don't think Macromedia is going to do anything about it.

August 04, 2004

Feedback on CFC-Based Security System

Jim Davis is looking for some feedback on a CFC-based security system that he has recently implemented (open source). If you use ColdFusion and have some time to spare, kick this thing around a bit and give him some feedback to help make the first official release a solid one.

Here is his original announcement:

A coupla weeks ago I posted on CF-Talk about how to build a decent portable, CFC-based security system. The original thread was here.

In that thread I explained my plans and concepts. Since then I've gone ahead the built the thing. A zip of the system and some test scripts is here:

ftp://ftp.depressedpress.com/DP_Security.zip

This system should be considered REALLY, REALLY BETA!

To run the test scripts you'll need to set up a mapping (sorry, but I gave up trying to work around it and still maintain a decent file structure and access). The mapping should be "cfc_DepressedPress" and point to the "cfc_DepressedPress" folder. Right now the only database supported is MS SQL Server 2000 (although 7.0 should also work - heck, I didn't do any MS-specific code that I know of so a lot of DBs might work, but the only thing I've used is SQL Server 2000).

To actually use it you should take a look at the test files (they assume, but this can be changed, a datasource called "test" which can be written to) and run the "TestSecurity_Install.cfm" template to create the data tables.

The system (when done) will be open source and published for free at http://www.depressedpress.com/.

Some of the features I'm rather proud of:

1) The system has intelligent caches for all components - you pass references to these caches, not data. This means that changes made to the information take effect for everybody using the information immediately (not "next login").

2) The system defines database "broker" components which, in theory, should make it quite easy to add database support (or any persistence layer really: flat files, XML, LDAP, etc). You just extend the existing Broker templates and create only the DB-specific code (most of while is really simple).

3) The system holds user specific information in three component types: "Credentials" store system information about the user (like password hash, userID, login count, etc), "Profiles" store personal information (name, address, phone numbers, etc) and "Entitlements" store group access information.

Each of these objects is cached separately and each can be easily extended to add more properties or validation rules (the actual CFCs used to define each are dynamically determined at install time and can be changed later).

4) The system doesn't assume or need session scope access - rather you pass it some session identifier and use it as your link into the system. The system itself (because of the caching) works best if you cache it, but it can be cached in the Application or Server scopes (or in the Session scope for that matter, but I'm not sure why you'd want to).

The main things missing from the system (aside from extensive testing!) are methods to work with system information from an admin perspective (things like "getAllUsers()"). Most of these will end up in the Users.CFC. The system also doesn't have an interface, but that was by design (I want the system to be pluggable into pretty much any CF application).

When it's released I hope to have at least one complete sample interface for admin functions and end-user functions.

I'd love feedback on this and really love any testing you might do.

Again, the more feedback the better. If you get a chance to use this, let Jim know your thoughts on it.