Wednesday 4 December 2013

Railo supports array types

G'day:
This might be old news, but it's something I didn't know until today. Railo supports function return values and arguments of type array-of-something, eg:

function acceptArrayOfSamples(required Sample[] samples){
    // etc
}

Sample[] function returnArrayOfSamples(){
    return [new Sample(), new Sample()];
}

Where sample is a CFC, eg:


//Sample.cfc
component {

}

It also works (kinda) for inbuilt types too, like strings and numerics.

I had a look at how well this stuff works, writing these test functions:

// functionsToTest.cfm
private any function acceptArrayOfSamples(required Sample[] samples){
    return samples;
}

private Sample[] function returnArrayOfSamples(required array samples){
    return samples;
}

private any function acceptArrayOfStrings(required string[] strings){
    return strings;
}

private string[] function returnArrayOfStrings(required array strings){
    return strings;
}

private any function acceptArrayOfNumerics(required numeric[] numerics){
    return numerics;
}

private numeric[] function returnArrayOfNumerics(required array numerics){
    return numerics;
}

private any function acceptArrayOfStructs(required struct[] structs){
    return structs;
}

private struct[] function returnArrayOfStructs(required array structs){
    return structs;
}

These functions either accept an array of various types (Samples, strings, numerics, structs), or return same.

I've written some unit tests to see how well this lot works:

// TestArraysOfObjects.cfc
component extends="mxunit.framework.TestCase" {


    public void function beforeTests(){
        variables.arrayOfSamples    = [new Sample(), new Sample()];
        variables.arrayofSubSamples    = [new SubSample(), new SubSample()];
        variables.arrayofNotSamples    = [new NotSample(), new NotSample()];
        variables.arrayOfStrings    = ["array", "of", "strings"];
        variables.arrayOfNumerics    = [-1, 2.2, pi()];
        variables.arrayOfStructs    = [{one="tahi"}, {two="rua"}, {three="toru"}, {four="wha"}];

        include "./functionsToTest.cfm";
    }


    public void function testAcceptArrayOfSamples(){
        acceptArrayOfSamples(arrayOfSamples);
    }

    public void function testReturnArrayOfSamples(){
        returnArrayOfSamples(arrayOfSamples);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testAcceptArrayOfSamples_withStrings(){
        acceptArrayOfSamples(arrayOfStrings);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testReturnArrayOfSamples_withStrings(){
        returnArrayOfSamples(arrayOfStrings);
    }

    public void function testAcceptArrayOfSamples_withSubSamples(){
        acceptArrayOfSamples(arrayOfSubSamples);
    }

    public void function testReturnArrayOfSamples_withSubSamples(){
        returnArrayOfSamples(arrayOfSubSamples);
    }
    
    /**
    * @mxunit:expectedexception expression
    */ 
    public void function acceptArrayOfSamples_withNotSamples(){
        acceptArrayOfSamples(arrayOfNotSamples);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testReturnArrayOfSamples_withNotSamples(){
        returnArrayOfSamples(arrayOfNotSamples);
    }

    public void function testAcceptArrayOfStrings(){
        acceptArrayOfStrings(arrayOfStrings);
    }

    public void function testReturnArrayOfStrings(){
        returnArrayOfStrings(arrayOfStrings);
    }

    public void function testAcceptArrayOfNumerics(){
        acceptArrayOfNumerics(arrayOfNumerics);
    }

    public void function testReturnArrayOfNumerics(){
        returnArrayOfNumerics(arrayOfNumerics);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testAcceptArrayOfNumerics_withStrings(){
        acceptArrayOfNumerics(arrayOfStrings);
    }

    /**
    * @mxunit:expectedexception expression
    */ 
    public void function testReturnArrayOfNumerics_withStrings(){
        returnArrayOfNumerics(arrayOfStrings);
    }

    public void function testAcceptArrayOfStructs(){
        acceptArrayOfStructs(arrayOfStructs);
    }

    public void function testReturnArrayOfStructs(){
        returnArrayOfStructs(arrayOfStructs);
    }

}

Where Sample.cfc is as per above, and SubSample.cfc and NotSample.cfc are as follows:

// SubSample.cfc
component extends="Sample" {

}

// NotSample.cfc
component {
    
}

To summarise the tests, what I've done is:
  • for the functions expecting/returning a Sample array, passed in arrays of Samples, SubSamples, NotSamples, strings. The latter two are expected to error, and do;
  • for the functions expecting a string array, just tested with a string array
  • for the functions expecting a numeric array, tested with both a numeric array and a string array (the latter ones - correctly - error)
  • for the functions expecting a struct array, tested just with a struct array
The results were as follows:


So the following tests fail:
  • passing an array of strings to a function expecting... an array of strings;
  • same with the function expecting a numeric array, it fails when it correctly receives an array of numerics;
  • same with structs.
Oddly, it's only when passing 'em in that there's a problem: it sees the arrays as the correct types when returning them.

Update:

As Rory observes in his comment below: this has all been fixed in Lucee. Nice one.

This is a handy feature, but it's incomplete. I've gotta go do some work now, but I'll check if there's a bug report for this, and raise one if not. And I will cross-reference back here either way. I'll also check to see if there's a ticket to implement this in ColdFusion, and raise / cross-reference accordingly.

That's it.

--
Adam