Showing posts with label ColdFusion 2016. Show all posts
Showing posts with label ColdFusion 2016. Show all posts

Tuesday 10 May 2016

CFML (yes, shut-up): sorted struct literal syntax

G'day:
Yeah, all right. I'm gonna be admonished by at least one of my mates (that's you, Brian) for writing another article about CFML, but... oh well. This is not gonna become a habit as I feel bad writing it.

You might remember that CF2016 promised to have sorted structs. I documented this ages ago "ColdFusion 2016: ordered/sorted structs". One thing you might have noticed is that whilst ordered structs made the cut; sorted ones did not. Adobe's briefly documented the ordered ones here: "Collection support (Ordered)".

To recap: structs in CFML have always been intrinsically unordered collections. If you want an ordered collection: use an array. Structs to preserve an internal ordering, but how that's done is specifically undocumented and one should never rely on it. Previously if one's needed to access a struct's values in a specific order, one's needed to maintain a separate index of what that order is, or just use the sort method to return an array of they keys in a prescribed order and then iterate over that, using each array element value to access the struct. It's all in the docs. The chief problem with sorting a struct was it was only ever based on the keys, and only ever in a lexical ordering. Which is almost never a sensible ordering to use. But this is just the penalty one pays for trying to use an intrinsically unordered data type as an ordered one.

ColdFusion 2016 added ordered structs, and was supposed to add sorted ones too, but sorted ones did not make the cut for release (and that's as much as I can say about that due to NDA reasons). I had pointed out to Adobe that "ordered" and "sorted" means the same thing for all intents and purposes... I don't actually know if they've taken this on board. I hope so, so they don't just look like dicks when they do get around to releasing the sorted variety.

I do feel safe to say they do intend to finish this particular job... I don't think that's breaking any NDA. I cannot say when any updates to ColdFusion 2016 might come out though, as I'm completely out of that loop now. Except for elements of the matter that I will not refer to directly, but you can read between the lines that they're perhaps under discussion somewhere.

Now... the contrived difference in definition that Adobe have invented between "ordered" and "sorted" is that an ordered struct preserves the order in which the keys are added:

person = [firstName="Kiri", lastName="Te Kanawa"];

If one iterates over that, we get Dame Kiri's name-order preserved:

C:\Users\adam.cameron>cf
person = [firstName="Kiri", lastName="Te Kanawa"];
person.each(function(key, value){
CLI.writeln("#key#: #value#");
});
^Z

FIRSTNAME: Kiri
LASTNAME: Te Kanawa
C:\Users\adam.cameron>

Compare that to a normal struct:


C:\Users\adam.cameron>cf
person = {firstName="Kiri", lastName="Te Kanawa"};
person.each(function(key, value){
    CLI.writeln("#key#: #value#");
});
^Z

LASTNAME: Te Kanawa
FIRSTNAME: Kiri
C:\Users\adam.cameron>

You see the key order is not preserved.

OK, this is all lovely, but this is about sorted structs. So what's the diff? To recap, a Sorted struct is one in which one can specify the order the keys should iterate in. This is a bit edge-case-y, but one might want to iterate over they values in some different order other than insertion or key-name order. Perhaps alphabetically by value. Or by some other enumeration, or by length of value or something. There's a use case there, even if it's also edge-case.

One of the big challenges with sorted structs is the literal expression syntax for them. You know: array literals are expressed like this:

["tahi", "rua", "toru", "wha"]

Structs:

{red="whero", green="kakariki", blue="kikorangi"}

And now ordered structs:

[firstName="Kiri", lastName="Te Kanawa"]

Sorted structs need to impart two different things:
  • the key/value pairs
  • the sorting mechanism

Within the sorting mechanism, there are some variations:
  • declaratively: sorting ascending, alphabetically, using a specific locale as the collation order
  • functionally: using a callback 

So how the hell to impart all that info in a literal?

There's been a bunch of suggestions (none of which I can share, sorry), but none have been complete or "natural-seeming". Abram floated some suggestions in a bug ticket - 4126544 - which are good suggestions, but not really solving the "literal" problem.

But I was discussing this tonight with my mate and ColdFusion testing machine Aaron Neff and we finally nailed a decent syntax I think (I await Sean to tell me otherwise ;-)

For the declarative approach:

sortedStruct = [key=value, key=value, struct]

Where the struct argument has the config details for the sort:

{direction="ASC|DESC", on="key|value", type="text|numeric", caseSensitive=boolean(false), locale="fr_FR"}

Put together as an example:

sortedStruct = [january=29, february=17, march=31, {type="numeric", on="value"}];

This would yield a struct that when iterated over would return the key/values in order Feb, Jan, March, as it's sorting on value (ascending is implicit given it'd be the default for that setting).

And for using a callback, instead of a struct one just gives the function:

sortedStruct = [key=value, key=value, function(element1, element2, struct)]

 eg:

sortedStruct = [key=value, key=value, function(element1, element2, struct)

{
    var monthLengths = [january=31, february=28, march=31];
    return element1.value / monthLengths[element1.key] - element2.value / monthLengths[element2.key];
}];

This sorts the struct on the proportionate daily value per month. Oh of course the comparator function returns one of <1, 0 or >1 as per all sorting comparator functions. I don't use the third argument to the callback in this example, but that's there to mirror other iteration callbacks which take a reference the whole collection as the last argument (should one need it).

I think that's a reasonable literal syntax for sorted structs, don't you? The only slight quirk is that there's the "special treatment" of the "last" item of the list of elements in the brackets, but I don't mind that. I suppose we could do this:

sortedStruct = [key=value, key=value](struct|callback)

?


But I think either syntax would be unambiguously parseable by ColdFusion, and I think it's clear what's going on when one eyeballs the code.

What do you think? Shall I float this as an idea?

Now... back to doing something that is a good use of my time [cracks another beer, starts Fallout 4].

Righto.

--
Adam

Sunday 28 February 2016

ColdFusion 2016: improvement to cfloop (of all things)

G'day:
I don't think I'll be able to spin this out very far, but it's another small feature of ColdFusion 2016 which is good, and seems to work properly. Well I say "good", and I call it a "feature", but it's really more "mundane" and just a case of finally implementing something almost correctly, instead of the initial attempt which was ballsed-up.

Back in ColdFusion 8, Adobe added the ability to <cfloop> to loop over an array directly, rather than need to use the <cfloop> tag's equivalent of an index for loop. So prior to ColdFusion 8, we needed to do this:


<cfset rainbow = ["Whero","Karaka","Kowhai","Kakariki","Kikorangi","Poropango","Papura"]>

<cfloop index="i" from="1" to="#arrayLen(rainbow)#">
    <cfoutput>#rainbow[i]#<br></cfoutput>
</cfloop>

This outputs the predictable:

Whero
Karaka
Kowhai
Kakariki
Kikorangi
Poropango
Papura


That's fairly innocuous, but iterating over an array in a view is sufficiently common a task that it made sense to fine-tune <cfloop> to accommodate it more semantically. So in ColdFusion 8 we got this instead:


<cfloop array="#rainbow#" index="colour">
    <cfoutput>#colour#<br></cfoutput>
</cfloop>

Well done, Adobe. This would have been so easy to get right, but they managed to balls it up. Here's a quick lesson in arrays:


rainbow = [];
rainbow[1] = "Whero";
rainbow[2] = "Karaka";
rainbow[3] = "Kowhai";
rainbow[4] = "Kakariki";
rainbow[5] = "Kikorangi";
rainbow[6] = "Poropango";
rainbow[7] = "Papura";

Arrays have two relevant components in this exercise: the element (which contains the value), and the index, which indicates the relative position of the element in the array. This isn't just terminology I'm making up... it's fundamental to how arrays work. And it's simple, and it's a CS 101 sort of thing (maybe CS 102?).

I missed this at the time it went into CFML as I did not bother with the CF8 pre-release, so it was out the door before I was aware of it. How everyone who did show up to the PR managed to not notice is beyond me. I imagine people simply didn't care, or were too busy playing with Flash Forms or whatever was the shiny gewgaw in that release. I mean it's trivial, but it's yet another example of Adobe rushing headlong on their mission of making CFML shit, instead of improving it.

Anyway, that was all a digressive framing exercise. The ColdFusion community finally managed to convince Adobe to set things straight (see 3321646, and 3341256), after only 3.5 years and two ColdFusion releases. We finally now have this:

<cfloop array="#rainbow#" index="i" item="colour">
    <cfoutput>#i#: #colour#<br></cfoutput>
</cfloop>

Notice how if I specify both an index and an item, the array loop actually exposes both the index (and it actually is the index), and the item for the element. We can't blame Adobe for using the term item here rather than element as - for once - they took advice to follow what Railo had already done. And item ain't so bad. And is actually correct-ish, which index never was.

If one specifies only the index in the loop, then it still receives the element value. For the sake of backwards compat. This was supposed to be deprecated, but it doesn't seem to be. I will follow this up (commented added to 3321646, to get it reopened).

And if one specifies only item, then it works as one would predict:

<cfloop array="#rainbow#" item="colour">
    <cfoutput>#colour#<br></cfoutput>
</cfloop>

Adobe have also extended this to list looping and file looping too:

<cfset week = "Rāhina,Rātū,Rāapa,Rāpare,Rāmere,Rāhoroi,Rātapu">

<cfoutput>
<cfloop list="#week#" index="day">
    #day#<br>
</cfloop>
<hr>

<cfloop list="#week#" index="i" item="day">
    #i#: #day#<br>
</cfloop>
<hr>

<cfloop list="#week#" item="day">
    #day#<br>
</cfloop>
<hr>
</cfoutput>

Rāhina
Rātū
Rāapa
Rāpare
Rāmere
Rāhoroi
Rātapu


1: Rāhina
2: Rātū
3: Rāapa
4: Rāpare
5: Rāmere
6: Rāhoroi
7: Rātapu


Rāhina
Rātū
Rāapa
Rāpare
Rāmere
Rāhoroi
Rātapu



<cfsetting enablecfoutputonly="true">

<cfoutput><pre></cfoutput>
<cfloop file="#getCurrentTemplatePath()#" index="line">
    <cfoutput>#encodeForHtml(line)#<br></cfoutput>
</cfloop>
<cfoutput></pre></cfoutput>
<cfoutput><hr></cfoutput>

<cfoutput><pre></cfoutput>
<cfloop file="#getCurrentTemplatePath()#" index="i" item="line">
    <cfoutput>#i#:    #encodeForHtml(line)#<br></cfoutput>
</cfloop>
<cfoutput></pre></cfoutput>
<cfoutput><hr></cfoutput>

<cfoutput><pre></cfoutput>
<cfloop file="#getCurrentTemplatePath()#" item="line">
    <cfoutput>#encodeForHtml(line)#<br></cfoutput>
</cfloop>
<cfoutput></pre></cfoutput>
<cfoutput><hr></cfoutput>

(I'll spare you the output: you get the idea).

Enigmatically, they did not extend this to structs. I'd expect this to work:

<cfset numbers = [
    one = "tahi",
    two = "rua",
    three = "toru",
    four = "wha",
    five = "rima",
    six = "ono",
    seven = "whitu",
    eight = "ware",
    nine = "iwa",
    ten = "tekau"
]>

<cfoutput>
<cfloop index="index" item="item" collection="#numbers#">
    #index# #item#<br>
</cfloop>
</cfoutput>

The analogue here is that when one specifies both index and item, then those receive the key and the value. But... no. We just get a compile error:

The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request

Attribute validation error for tag CFLOOP.

It has an invalid attribute combination: collection,index,item. Possible combinations are:





  • Required attributes: 'file,index'. Optional attributes: 'charset,from,to'.
  • Required attributes: 'file,index,item'. Optional attributes: 'charset,from,to'.
  • Required attributes: 'file,item'. Optional attributes: 'charset,from,to'.
  • Required attributes: 'index,item,list'. Optional attributes: 'delimiters'.
  • Required attributes: 'index,list'. Optional attributes: 'delimiters'.
  • Required attributes: 'item,list'. Optional attributes: 'delimiters'.
  • Required attributes: 'group'. Optional attributes: 'endrow,groupcasesensitive,startrow'.
  • Required attributes: 'group,query'. Optional attributes: 'endrow,groupcasesensitive,startrow'.
  • Required attributes: 'query'. Optional attributes: 'endrow,startrow'.
  • Required attributes: None. Optional attributes: None.
  • Required attributes: 'array,index'. Optional attributes: None.
  • Required attributes: 'array,index,item'. Optional attributes: None.
  • Required attributes: 'array,item'. Optional attributes: None. <l...

  • Heaven forbid Adobe do a thorough job here.

    Also note the error messages is actually truncated half-way through a tag, and before the various attribute combos even get around to mentioning structs. Sigh.

    Oh, and one cannot use this syntax to iterate over a query either.



    This all got me thinking, actually. Why do we have different constructs for looping over an array, query, list or struct? Or file for that matter. How come it's not just this:

    <cfset myCollection = getCollection()>
    
    <cfoutput>
    <cfloop collection="#myCollection#" key="key" value="value">
        #key#: #value#<br>
    </cfloop>
    </cfoutput>
    

    Where I'm using getCollection() to be purposely value. That could return anything iterable: an array, a list, a file object, a struct, a query. Anything that implements some notional "Iterable" interface really. But <cfloop> could have one uniform syntax for iterating over said collection. Would that not be better? (Raised as 4122711).

    But anyway. As far as they have implemented this feature: it works. It perhaps shoulda been extended to structs and queries too though, yeah? And perhaps as a unified syntax. Oh well.

    Righto.

    --
    Adam

    Monday 22 February 2016

    ColdFusion 2016: Unaspirational thinking with session/Redis support

    G'day:
    Disclsoure: this is a reproduction of an observation I made on the ColdFusion 2016 pre-release forums. As it is my own work, and is independent of anyone else's comment or input, I feel it's not breaking the NDA to repeat it.

    ColdFusion 2016 adds a new feature to be able to pass-off session storage to a Redis server. Details here: ColdFusion 2016 Features / External session storage. The allows one to specify in CFAdmin a Redis server to use for session management.

    On the face of it this is a good feature, but I had apprehensions about this since it was announced. To contextualise the first sentence, I had earlier speculated that the implementation would not be very well thought out.

    Right, so here we are, and my suspicions were correct.

    This implementation is somewhat lacking in aspiration.

    [redacted]Adobe have implemented half a solution here.

    What would have been a good approach would be to have provided an interface which one can implement a SessionManager connector. And then have this sort of thing in Application.cfc (or in the CF server config somewhere for instance-wide handling):

    this.sessionManager = new MySessionManager();
    

    This hands off all session management to the SessionManager.

    The interface specifies which methods are needed to use sessions via the abstracted manager, eg:
    • createSession()
    • setProperty()
    • getProperty()
    • hasProperty()
    • getProperties()
    • invalidateSession()
    • expireSession()
    • onSessionStart()
    • onSessionEnd()
    • etc, you get the idea

    I s'pose this interface could be fulfilled either by a Java class or a CFC.

    From there, Adobe could implement a redis-based SessionManager.

    Why? Because there’s more games in town than Redis. A lot more. We have a cluster of Memcached boxes which we could use for this, had Adobe not painted itself (and all of us) into a corner. Again. Even from the perspective of existing CF work... why Redis, when all the rest of the embedded nearline storage has been build on Ehcache? Or, hey... I might wanna put my sessions in a DB, or even have a null implementation.

    But the way Adobe have implemented this we're stuck with their one implementation. And at their mercy for how it’s implemented.

    Also I think - as a general thing - the primary configuration mechanism for a CF server/instance/application needs to move away from the CFAdmin interface. That approach to configuring application server software is a bit out of date. Config should be file based, IMO (and not WDDXed structs, but clear text, human- and code-maintainable config files).
    I removed one sentence from that as it alludes to something else on the PR which I am not in the position to discuss here, I have also corrected two typos I spotted, and added some formatting.

    A coupla notes on this:

    • as I mentioned: seemingly this can only be managed by CFAdmin. I think this should be settable at Application level.
    • As mentioned in the docs: "If you select Use J2EE session variables, you cannot store ColdFusion sessions in Redis". This is not very satisfactory. This was discussed in the pre-release, but I cannot go into that here. erhaps the relevant people from Adobe can reproduce their feedback on this.

    I've not much to add other than that... I have not experimented with or tested this feature, so I don't know how robust or production-ready it is.

    It's certainly not an enterprise solution to the functionality though.

    Righto.

    --
    Adam

    Friday 19 February 2016

    ColdFusion 2016: toJson() method

    G'day:
    This'll need to be super quick as I only have 15min of lunch break left.

    ColdFusion 2016 added a toJson() method to allmost of its inbuilt types. Here's the biz:

    a = ["tahi"];
    st = {two="rua"};
    ost = {three="toru"};
    q = queryNew("en,mi", "varchar,varchar",[["four","wha"]]);
    cfxml(variable="x"){
        writeOutput("<five><rima/></five>");
    }
    s = "ono";
    ts = now();
    d = 42 * 1;
    b = true == true;
    
    CLI.writeLn(a.toJson());
    CLI.writeLn(st.toJson());
    CLI.writeLn(ost.toJson());
    CLI.writeLn(q.toJson());
    CLI.writeLn(q.toJson(true));
    CLI.writeLn(x.toJson());
    CLI.writeLn(ts.toJson());
    //CLI.writeLn(d.toJson()); // not supported for numerics
    //CLI.writeLn(b.toJson()); // not supported for booleans
    CLI.writeLn(variables.toJson());
    


    And the output:


    C:\src\CF12\other\tojson>cf toJson.cfm
    ["tahi"]
    {"TWO":"rua"}
    {"THREE":"toru"}
    {"COLUMNS":["EN","MI"],"DATA":[["four","wha"]]}
    {"ROWCOUNT":1,"COLUMNS":["EN","MI"],"DATA":{"EN":["four"],"MI":["wha"]}}
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<five><rima/></five>"
    "ono"
    "February, 19 2016 13:48:38"
    {"OST":{"THREE":"toru"},"B":true,"ST":{"TWO":"rua"},"X":"<?xml version=\"1.0\" e
    ncoding=\"UTF-8\"?>\n<five><rima/></five>","Q":{"COLUMNS":["EN","MI"],"DATA":[["
    four","wha"]]},"D":42.0,"TS":"February, 19 2016 13:48:38","A":["tahi"]}
    C:\src\CF12\other\tojson>

    So all good for arrays, structs, ordered structs, queries (and both modes for queries), XML, strings, dates and scopes. But it fails on numerics and booleans. For the sake of completeness it should work on numerics and booleans too (esp if it works on strings!). I've raised 4119888.

    Anyway... well done: something that's a) useful; b) almost works 100%. And the non-100% it's quite edge-case-y anyone.

    Righto.

    --
    Adam

    ColdFusion 2016: good-bye Akamai

    G'day:
    Two snippets of info from King Anit on Slack this morning:
    Anit Kumar
    [09:34]
    Hi All,
    From now onward, ColdFusion and ColdFusion Builder installers are available without Akamai Download Manager. So, no more download related complaints :simple_smile:
    I've tested this and it's true. Well done.

    And also Adobe have put the Express versions of ColdFusion 2016 back up, and there are direct downloads for them (so circumventing even the Adobe webshite):

    So that's jolly good. Good work, Anit.

    In other Anit-related news, another of his colleagues - Rahul Upadhyay - from the ColdFusion Support Team has joined the CFML Slack channel too. Hopefully Rahul is as solid a bloke as Anit has proven to be.

    --
    Adam

    You need to consider the ColdFusion 2016 release as a public beta

    G'day:
    I've been looking at what Adobe has released as ColdFusion 2016 over the last few days, and I can only conclude that it's really only "first public beta" quality. I figure it's worth making this observation, and warning people to not treat it as production-ready, and - unless you're basically installing it for testing purposes - simply don't touch it yet. Wait for a coupla updates even before trying it.

    Adobe (and Macromedia before them) have always had public betas in the past, but for some reason they chose not to this time. If one looks at the duration of the pre-release cycle, ColdFusion 2016 was released very early compared to other releases. If one also bears in mind there's usually a small-audience alpha even before the wider-audience pre-release, and they didn't have that closed alpha round this time, it demonstrates it's been in a testable environment for an even shorter period than usual.

    Given that... what they have released is pretty much the quality one would expect perhaps half-way through the pre-release cycle, which makes it perhaps 2-3 iterations more untested than the public usually experiences.

    So in effect it's not even a beta. It's an alpha. It's OK for an alpha, but that doesn't make it production ready.

    We also have to bear in mind a few factors contributing to the quality of the product here:

    • the ColdFusion Team's understanding of CFML is poor (ie: that's as distinct from Java, which they might be quite good at; but they don't understand the language they are creating);
    • their attention to detail is notoriously bad;
    • their testing is superficial;
    • they generally ignore input from the community. This was especially bad this time round, as described by Ray in his recent blog article about the ColdFusion 2016 release;
    • and as stated, this was all just a rush job.
    If you bear all that in mind, do you really want to be investing your time trying to get your applications working on something that can only optimistically be referred to as beta? No.

    Also stop to think about what you're gaining here: there is bugger-all in ColdFusion 2016 anyhow, feature-wise. I guess they have fixed a bunch of bugs, but most of those ought to be back-ported into ColdFusion 10 and 11 (as appropriate) anyhow. ColdFusion 10 is supported until mid 2017, and ColdFusion 11 until Q2 2019 (ref: Adobe Support Lifecycle Policy).

    In addition to that there seems to be some question marks over licensing changes which might make ColdFusion 2016 substantially more expensive to run (or upgrade to) than ColdFusion 11, so that's something else to consider. I do not quite understand the details of this, but perhaps ask around.

    Just steer clear of ColdFusion 2016, at least for the time being, until they get their act together. If nothing else, don't reward them for doing substandard work.

    Righto.

    --
    Adam

    ColdFusion 2016: critical regression in scoping

    G'day:
    Thanks to Mingo Hagen for inspiring me today. He brought this to my attention on the CFML Slack channel (all the more reason to subscribe to that, btw... to catch stuff like this).

    ColdFusion 2016 has introduced a serious regression bug when it comes to unscoped references in functions.

    Update (2016-07-23)

    See Wil's comment below. He's confirmed this is fixed in ColdFusion 2016, Update 2. Cheers for letting us know about this, Wil.
    Here's the repro:

    function foo(bar) {
        bar = "test";
        try {
            writeOutput("arguments scope");
            writeDump(var=[arguments.bar]);
        } catch (any e){
            writeDump({message=e.message});
        }
        try {
            writeOutput("local scope");
            writeDump(var=[local.bar]);
        } catch (any e){
            writeDump({message=e.message});
        }
        try {
            writeOutput("no scope");
            writeDump(var=[bar]);
        } catch (any e){
            writeDump({message=e.message});
        }
        try {
            writeOutput("variables scope");
            writeDump(var=[variables.bar]);
        } catch (any e){
            writeDump({message=e.message});
        }
    }
    
    writeOutput("Test with no arg passed:<br>");
    foo();
    
    writeOutput("<hr>Test with arg value passed:<br>");
    foo("arg value");
    

    The conceit here is that we are assigning an un-scoped variable a value within the function.

    On previous versions of ColdFusion (and Lucee/Railo) we get this:



    So CF successfully notices that the unscoped bar is a reference to bar argument, and assigns it appropriately. And the behaviour is uniform irrespective of whether we pass a value for the bar argument or not. This is correct.

    On ColdFusion 2016, we get this instead:





    Here ColdFusion isn't bothering to do a scope look-up to see if the unscoped variable already exists in a scope, it just goes "unscoped = variables scope". Which, in a function, is wrong.

    This is a pretty serious bug. All the more reason to give ColdFusion 2016 a swerve at least for the time being (if not permanently, because it's just not worth the hassle given the lack of any real gain one would get from "upgrading" to it).

    Mingo's not raised a ticket for this yet, but I'll cross-ref once he has. It's 4119653.

    Righto.

    --
    Adam

    Thursday 18 February 2016

    ColdFusion 2016: the slipshoddiness from Adobe continues with ordered structs

    G'day:
    Hopefully "slipshoddiness" with keep Ron Stewart amused.

    However no-one should be amused by any of this ColdFusion 2016 bullsh!t, which continues to be beta-quality the more of it I look at.

    OK, so I wrote about the sorted/ordered structs coming to ColdFusion 2016 back when Adobe released that info to the public, a few months back: "ColdFusion 2016: ordered/sorted structs". Some of that info is out of date now, because [stuff that went on in the pre-release programme and I can't discuss], but the reasons were reasonably legit (-ish), and we all agreed to the scope reduction.

    One of the changes was that it became clear that there really isn't a difference between the notion of "ordered" and "sorted": they are basically synonyms. Originally the idea was that an ordered struct had its keys in the order they were originally added into the struct; but a sorted struct was one in which one could supply a callback to define a bespoke ordering. It was pointed out that "insertion order" and "alphabetic order" and "bespoke order" are all variation of the same concept, so it was decided to drop the disparate concepts, and just run with ordered structs to cover all bases. Well until they had trouble with the callback-ordered version, and dropped that from ColdFusion 2016. This is correct: the callback option did not ship. It'll come "later". Which might be ColdFusion 2018, I should think. Or never.

    So what's left: there's a new type of struct called an ordered struct. or as Adobe put it using their CFML-yoda-English "structOrdered" (FFS).

    There's two ways of creating a new ordered struct:

    orderedViaFunction = structNew("ordered");
    writeDump(var=orderedViaFunction, label=orderedViaFunction.getClass().getName());
    
    orderedViaLiteral = [=]; // or : for the assignment if you must
    writeDump(var=orderedViaLiteral, label=orderedViaFunction.getClass().getName());
    

    And these dump just like structs:

    D:\src\CF12\structs\ordered>cf creation.cfm
    coldfusion.runtime.StructOrdered - struct [empty]


    coldfusion.runtime.StructOrdered - struct [empty]


    D:\src\CF12\structs\ordered>

    I think perhaps it's a slight glitch there that dump doesn't identify them as ordered structs. I'll raise a ticket for that (4119477).

    Anyhow, and empty ordered struct is a bit of a cop-out. Here's one with something in it, demonstrating the different from a normal struct ("structNormal"?):

    normal = {
        first = 1,
        second = 2,
        third = 3,
        fourth = 4,
        fifth = 5
    };
    writeDump(normal);
    
    ordered = [
        first = 1,
        second = 2,
        third = 3,
        fourth = 4,
        fifth = 5
    ];
    writeDump(ordered);
    

    Result:

    D:\src\CF12\structs\ordered>cf literal.cfm
    struct

    FIFTH: 5
    FIRST: 1
    FOURTH: 4
    SECOND: 2
    THIRD: 3

    struct

    FIRST: 1
    SECOND: 2
    THIRD: 3
    FOURTH: 4
    FIFTH: 5

    D:\src\CF12\structs\ordered>

    So you see that the latter dump has indeed preserved its key ordering. Lovely.

    Oh... literal syntax there uses square brackets rather than curly braces.

    I tested a bunch of other things: each(), map(), filter(), toJson(), for-looping, keyArray() etc, and they all worked. But it's all boring, so I'll spare you.

    As with everything (it seems) in ColdFusion 2016, it's riddled with bugs though.

    First - I did not find this one, Aaron Neff did - the literal syntax is a bit fragile (heaven forbid literal syntax in ColdFusion is brittle... struct- and array-literal syntax in ColdFusion was almost unusable when it was added in ColdFusion 8; almost production ready for ColdFusion 9, and I think by ColdFusion 10 it'll be stable. So with this stuff... maybe by ColdFusion 2020. Or it'll probably be COLDFVSION MMXX or something daft by then. But anyway...

    ColdFusion 2016: half-arse`d-ness from Adobe on a potentially handy feature

    G'day:
    This'll be a lot quicker than I anticipated, given Adobe haven't actually bothered to implement most of the given feature.

    Back in the ColdFusion 10 era, I raised 3597432 ("replaceWithCallback()"). The idea here was to add an option to replace() to make it behave more like JavaScript's String.prototype.replace(), in that it can take a callback to perform the replacement operation. Quite often a replacement operation might be mroe than a simple swap-out for one value for another, beyond the scope that regex operations can facilitate it. So using a callback allows one to handle the replacement anyway one likes.

    Here's an example of the JavaScript version:

    source = "this is a string from which to exchange some matches using a callback to perform the exchange";
    match = new RegExp("exchange", "g");
    replacement = "replace";
    
    result = source.replace(match, function(match, index, source){
        return replacement;
    });
    
    console.log(result);
    

    Which yields:

    C:\src\CF12\strings\replaceWithCallback>node replace.js
    this is a string from which to replace some matches using a callback to perform the replace

    C:\src\CF12\strings\replaceWithCallback>

    Note how this just swaps out all the instances of "exchange" for "replace". This is a daft usage of a callback, but you get the general idea.

    So ColdFusion 2016 implements this:

    source = "this is a string from which to exchange some matches using a callback to perform the exchange";
    match = "exchange";
    replacement = "replace";
    
    result = source.replace(match, function(match, index, source){
        return replacement;
    }, "ALL");
    
    writeDump([
        source=source,
        match=match,
        replacement=replacement,
        result = result
    ]);
    

    Result:



    So that's cool. It works.

    But do you know what's not cool?

    They only implemented it on replace(). They did not implement it on replaceNoCase(). Nor reReplace(). nor reReplaceNoCase().

    Sigh. Useless. Just useless.

    So really... in that they only added it to one of four congruent functions, this represents making CFML (haha, I initially typoed that as "FML"... somewhat fittingly perhaps) just that little bit worse, rather than that little bit better.

    Righto.

    --
    Adam

    ColdFusion 2016: query iteration functions

    G'day:
    In ColdFusion 11, one of the best new features was the addition of collection iteration methods, ie stuff like Array.map(), Struct.each(), List.listReduce() etc. One glaring omission was the lack of these implemented for queries. I have no idea why they were omitted, but there you go.

    Anyway, one of the few decent language features in ColdFusion 2016 is that these have finally been implemented. They mostly work as one would expect, but I'll run through examples of each anyhow.

    each()

    each() simply iterates over the query, calling the callback for each row. The function singature for the callback is:

    void function(struct row, numeric index, query query)
    

    An example of this in action is:

    colours = queryNew("id,en,mi", "integer,varchar,varchar", [
        [1,"red","whero"],
        [2,"orange","kakariki"]
    ]);
    
    colours.each(function(colour, index, colours){
        writeDump(arguments);
    });
    

    And the results:



    Predictable and no surprises.

    map()

    map() is a bit more complicated. I'll start with an example to frame things:

    colours = queryNew("id,en,mi", "integer,varchar,varchar", [
        [1,"red","whero"],
        [2,"orange","karaka"],
        [3,"yellow","kowhai"],
        [4,"green","kakariki"],
        [5,"blue","kikorangi"],
        [6,"indigo","poropango"],
        [10,"violet","papura"]
    ]);
    
    maoriColours = colours.map(function(colour, index, colours){
        return {mi=colour.mi};
    }, queryNew("mi","varchar"));    
    
    writeDump(var=maoriColours);
    



    The function signature for the callback is the same as before except it returns a struct:

    struct function(struct row, numeric index, query query)
    

    Note map() takes a second argument, which is a second query. This query acts as a template for the remapped query returned by map(). The map callback returns a struct with keys that are a subset of the column names in that query. Note here my template query has a column mi, and so does the struct I'm returning from the callback. If I try to return a different column name, I get an error:



    It's important to note that that query is only used for the "schema" of the returned query, any rows in it are ignored. That whole second argument is optional, and if omitted the original query's schema is used instead.

    reduce()

    reduce() holds no surprises. Its callback signature is:

    any function(any carry, struct row, numeric index, query query)
    

    And an example is:

    week = queryNew("id,en,mi", "integer,varchar,varchar", [
        [1,"Monday","Rāhina"],
        [2,"Tuesday","Rātū"],
        [3,"Wednesday","Rāapa"],
        [4,"Thursday","Rāpare"],
        [5,"Friday","Rāmere"],
        [6,"Saturday","Rāhoroi"],
        [7,"Sunday","Rātapu"]
    ]);
    
    shortestMaoriDayName = week.reduce(function(shortest,number){
        if (shortest.len() == 0) return number.mi;
        return number.mi.len() < shortest.len() ? number.mi : shortest;
    }, "");
    
    writeOutput(shortestMaoriDayName);
    

    This simply returns:

    Rātū

    sort()


    The callback for this takes two elements to compare:

    comparison function(any row1, any row2)
    

    Where I say comparison as the return type here, I mean a numeric value that is <0, 0 or >0 indicating whether row1 is less then, equal to or greater than row2, by whatever yardstick is reflected by the callback. Same as any other sort handler. Example:

    colours = queryNew("id,en,mi", "integer,varchar,varchar", [
        [1,"red","whero"],
        [2,"orange","karaka"],
        [3,"yellow","kowhai"],
        [4,"green","kakariki"],
        [5,"blue","kikorangi"],
        [6,"indigo","poropango"],
        [10,"violet","papura"]
    ]);
    
    colours.sort(function(c1,c2){
        return compare(c1.mi, c2.mi);
    });
    
    writeDump(colours);
    

    Note that sort() desn't return the sorted query - it'd be a lot better if it did, TBH, I think I might raise a bug for this (4119099) - it sorts the query inline. This prevents this method being chained, which is disappointing. The output for this is predictable:



    (it's the Maori column that's sorted there... not so obvious given the ubiquity of Ks in Maori words).

    filter()


    Yay, here's the first definite bug. The filter() callback has this function signature:

    boolean function(struct row, numeric index, query query)
    

    And here's an example:
    colours = queryNew("id,en,mi", "integer,varchar,varchar", [
        [1,"red","whero"],
        [2,"orange","karaka"],
        [3,"yellow","kowhai"],
        [4,"green","kakariki"],
        [5,"blue","kikorangi"],
        [6,"indigo","poropango"],
        [10,"violet","papura"]
    ]);
    
    coloursWithoutK = colours.filter(function(colour, index, query){
    writeDump(arguments);abort;
        return !colour.mi.find('k');
    });
    
    writeDump(coloursWithoutK);
    

    This obviously (obviously!) returns a query with only the three rows which don't have the letter K in the Maori colours, right?



    No. It returns an array. How did this get through QA (NB: there's a good reason why this was not fixed in the pre-release, but I cannot disclose why. It's not a good reason though).

    So I'll raise a bug for that (4119103).

    That's about it. There's a fail in sort() in that it does not return the sorted query, leaving the original alone, and an outright bug in the implementation of filter().

    All in all: a good alpha-quality implementation of these functions though. And a good thing about ColdFusion 2016.

    Righto.

    --
    Adam

    Tuesday 16 February 2016

    ColdFusion 2016: trying to write about one thing; end up writing about another

    G'day:
    I was gonna do an article about ColdFusion 2016's new query iteration functions, but my fondness for using Maori as my sample data language has pointed me in the direction of a possible bug in ColdFusion 2016's CLI.

    I've distilled it down to this:

    CLI.writeln("kuputuhi tauira ki pūāhua nako");
    

    If I run that via the ColdFusion 2016 CLI, I get this:


    D:\src\CF12\cli\utf8>cf
    CLI.writeln("kuputuhi tauira ki pūāhua nako");
    ^Z
    kuputuhi tauira ki puahua nako
    D:\src\CF12\cli\utf8>

    Hmmm... where gone the diacritic marks?

    Initially I put this down to a tweak I've made to the CLI batch file which allows me to put code straight into STDIN instead of a file and run it, so I ran the code old-school:

    D:\src\CF12\cli\utf8>cf cfmlExample.cfm
    kuputuhi tauira ki p┼½─?hua nako
    D:\src\CF12\cli\utf8>

    Yikes! Worse!

    I figured there was a chance that Windows wasn't handling encoding in its own CLI box that well, so decided to see what PHP made of this:

    <?php
    echo "kuputuhi tauira ki pūāhua nako";
    

    And this yields:

    D:\src\CF12\cli\utf8>php phpExample.php
    kuputuhi tauira ki pūāhua nako
    D:\src\CF12\cli\utf8>

    OK, so ColdFusion is doing no different from PHP here when running a file. On a whim I decided to try Ruby too, and this fared better:

    puts "kuputuhi tauira ki pūāhua nako"
    


    D:\src\CF12\cli\utf8>ruby rubyExample.rb
    kuputuhi tauira ki pūāhua nako

    D:\src\CF12\cli\utf8>


    So it wasn't like it was impossible to display the right characters in a Windows CLI box, but I decided to google anyhow.

    I found a PHP answer which said I needed to change the code page in the CLI box to support UTF-8, by doing this:


    D:\src\CF12\cli\utf8>chcp 65001
    Active code page: 65001

    D:\src\CF12\cli\utf8>


    This is a new one on me, but it seemed to sort PHP's issues out:


    D:\src\CF12\cli\utf8>php phpExample.php
    kuputuhi tauira ki pūāhua nako
    D:\src\CF12\cli\utf8>


    But it didn't really help ColdFusion:


    D:\src\CF12\cli\utf8>cf cfmlExample.cfm
    kuputuhi tauira ki pū�?hua nako
    D:\src\CF12\cli\utf8>

    On a further whim, I decided to extend my test bed to Python:

    print("kuputuhi tauira ki pūāhua nako")
    


    Without the chcp call, I got this:

    D:\src\CF12\cli\utf8>py pythonExample.py
    kuputuhi tauira ki pūāhua nako

    D:\src\CF12\cli\utf8>


    Which is exactly the same as ColdFusion's output. But once I make the chcp call:


    D:\src\CF12\cli\utf8>chcp 65001
    Active code page: 65001

    D:\src\CF12\cli\utf8>py pythonExample.py
    kuputuhi tauira ki pūāhua nako

    D:\src\CF12\cli\utf8>

    As you can see: it's fine.

    So my conclusion here is that it's completely legit than ColdFusion might not be able to render UTF-8-encoded characters without the code page being actively changed, but even after that something is not right with the CLI. I supsect that when they load the file in, they don't give any thought to encoding at all, so the source code actually gets "corrupted" before it's run. I have to admit that my understanding of encoding is OK, but not fantastic, but when one throws Windows CLI code pages into the mix too... I dunno what my expectations ought to be. But it ain't working, that's fer sure.

    Could someone please try this on *nix and see what they get when using a more robust shell?

    Now it's too late for me to look at query iterations functions, so that might be a job for before work tomorrow.

    Righto.

    --
    Adam

    ColdFusion 2016: floor()

    G'day:
    OK, so there's a coupla other uninteresting features in ColdFusion 2016 which I'll look at. The first one is in response to a ticket John Whish raised a while back: Deprecate `int` in favour of `floor`.

    SSIA, really, but not the grounds for doing so. The int() function doesn't really do what it's supposed to, as I wittered on about here: CFML: how should int() behave? int() should just return the integer part of a number (hint: it's in the name), but instead it performs a rounding exercise instead. This means with negative numbers, it gets the answer wrong.

    Still: the horse has bolted and it's not like it can be fixed or dropped now. Speaking of fixing it, there is a function fix() which does what int() was supposed to do.

    So what's this floor() function? It does exactly the same as int() currently does, but it's got a more sensible name when taken in conjunction with another already-existing ceiling(). The idea is that we abandon int() as being faulty, and promote floor() and ceiling() for the rounding down/up intergerising functions, and use fix() to just get the integer part. Basically: just forget about int() being a thing, and move on.

    So this ain't much of an enhancement, it just does some language tidy-up.

    Here, btw, is some code demonstrating what I'm on about:

    <cfset numbers = [-3.6,-3.5,-3.4,-2.6,-2.5,-2.4,-0.5,0,0.4,0.5,0.6,1.5,1.6,2]>
    <cfset handlers = [int,floor,ceiling,fix,round]>
    <cfoutput>
    <table border="1">
        <thead>
            <tr>
                <th>x</th>
                <cfloop array="#handlers#" item="handler">
                    <th>#handler.getName()#</th>
                </cfloop>
            </tr>
        </thead>
        <tbody>
            <cfloop array="#numbers#" item="x">
                <tr>
                    <td>#x#</td>
                    <cfloop array="#handlers#" item="handler">
                        <td>#handler(x)#</td>
                    </cfloop>
                </tr>
            </cfloop>
        </tbody>
    </table>
    </cfoutput>
    

    Here's a rare instance of me using tags, eh? Well it's mostly output, and I needed a table to present it, so fair cop.

    This outputs:

    x int floor ceiling fix round
    -3.6 -4 -4 -3 -3 -4
    -3.5 -4 -4 -3 -3 -3
    -3.4 -4 -4 -3 -3 -3
    -2.6 -3 -3 -2 -2 -3
    -2.5 -3 -3 -2 -2 -2
    -2.4 -3 -3 -2 -2 -2
    -0.5 -1 -1 0 0 0
    0 0 0 0 0 0
    0.4 0 0 1 0 0
    0.5 0 0 1 0 1
    0.6 0 0 1 0 1
    1.5 1 1 2 1 2
    1.6 1 1 2 1 2
    2 2 2 2 2 2


    All we see here is that int() (and now floor()) round to the next lowest integer, which might be a different integer from the integer part of the original number if the number is negative. ceiling() does the opposite: rounds to the next highest integer. fix() just returns the integer part of the number, and for good measure I've chucked round() in their too to show rounding to the nearest integer, which is yet another variation.

    One thing Adobe have not done to finish this work is to mark int() as deprecated. That's kinda essential to this, as otherwise floor() is just adding clutter to the language, rather than being a step to tidy it up. I'll remind Adobe about this bit. Again.

    So not very interesting, but a quick one to write up whilst waiting on a call.

    Righto.

    --
    Adam

    ColdFusion 201610.5.1: first attempt at installing...

    G'day:
    So I decided to bite the bullet, install that stupid Akamai download manager (in 2016, seriously?) and grabbed the ColdFusion 201610.5.1 trial.

    Most of the way through the install process, I get this:



    Just for the sake of people googling, I'll put that here again in text:

    The Program can't start because MSVCR110.dll is missing from your computer. Try reinstalling the program to fix this problem.

    I guess this is something to do with a C++ runtime I don't have installed, or something. I mean I do have the 2005, 2008, 2010, 2012, 2013 and 2015 ones installed. But not this one.

    For the record, I'm doing this install on a Windows 10 Home 64-bit laptop.

    One would think if an installer needed a dependency... the installer would know how to get hold of the dependency?

    Oh well. I'll work out WTF, and report back.

    Update:

    Yeah, apparently I didn't have the 2011 redistributable, which is solved by installing the 2012 one. Which I already had installed. But, hey... I'll play their silly games and reinstalled it, and now ColdFusion 2016's installer has completed no worries.

    I remain underwhelmed by ColdFusion 201610.5.1, needless to say.

    Righto.

    --
    Adam

    ColdFusion 2016: Adobe finally abandons CFML

    G'day:
    I should have suffixed that with "for all intents and purposes".

    Today ColdFusion 2016 was released. My executive summary of this release is that it's by far the worst ColdFusion release since CFMX6.0. 6.0 was basically released as alpha quality, and not usable for I think three additional beta patches, finally being usable at 6.1.

    ColdFusion 2016 continues Adobe's increasingly prevalent tendency to aim to appeal more to PHB types, but this time finally decides to completely marginalise the actual CFML developer. If ColdFusion 11's only addition to CFML had been <cfclient>... it'd still be a better ColdFusion release - in terms of CFML - than CF2016 is. That's how crap CF2016 is.

    In this light, I think it's clear that Adobe have pretty much given up on the CFML community, instead thinking their "community" is a bunch of purchase-order-signing IT managers.

    ColdFusion 2016 includes a bunch of non-CFML features, which are varying degrees of relevance and interest to the CFML developer, but there are only... 2 (f***ing two!!)... enhancements to the CFML language. We've been waiting two years for more stuff to go into the CFML language, and that's what we get. This is appallingly cynical from Adobe.

    So what are the CFML features? Let me quote from the release docs:

    Language enhancements

    For safe navigation, “?.” operator has been introduced. Collections support for “ordered” and “sorted” has been introduced. Refer this section to know more about all the language enhancements.

    That's it. Two things.

    I've already written about both of the features they list above:


    Both of these have changed since I wrote about them:
    • the scope of sorted/ordered structs has been pegged back a bit, and there's now only ordered structs, as Adobe realised "ordered" and "sorted" are synonymous terms, and they only really needed the one concept. The implementation they have shipped is a bare minimum to not take the piss, but it's not as good as the alpha version I wrote up. I'll leave it to you to examine the final implementation.
    • the implementation of the safe-navigation operator has been tweaked to also do a null-safety-check on the last right-hand-side operand (read the linked-to article for explanation of this), but it has been released in an incomplete state as it only supports dot notation, not bracket notation.
    They've actually forgotten a coupla small things: there are now iteration methods for queries (.map().reduce() etc), and I heard someone mention there's also a valueArray() method now... I've not tested that.

    I've had a quick look at the query iteration methods, but do't have anything to show for it yet. I'll get onto that next.

    And actually there is another feature: arrays are now "passed by reference" rather than "passed by value", which is long overdue, and more a design bugfix than a feature IMO. And is that really a CFML feature? I guess it's code-centric.

    To be fair there's also some tweaks to PDF functionality, but as the whole concept of PDFs makes me...

    ...zzzzzzzzzzzzzzzzzzz...

    ... sorry, dunno what happened: dropped off there for a second. Um... right... PDFs... yeah, one can do stuff with redaction and comments and metadata, but I have no idea what and intend to keep it that way.

    The other actually good developer-centric feature of ColdFusion 2016 is the CLI, which is a welcome addition, and has been implemented well. But it's not a CFML enhancement. I wrote up the alpha implementation a while back: "ColdFusion 2016: the long-awaited CLI". It's not moved much since then, so I won't go into it more.

    But what about all those tickets Adobe marked "to fix" shortly before and during the pre-release? Nothing, as far as I can tell. I know goalposts change during development of new software, but I really do think Adobe changed the status on those tickets purely to get people off their back, and they never really had any intention of fixing them in ColdFusion 2016. I'm sure someone will come back saying "they just said 'to fix', they never said when". That's just disingenuous. But I'm sure that was the particular combination of smoke and mirrors they were going to fall back on if anyone called them on it.

    What other stuff is there?

    NTLM authorisation has finally been added to I think all features which should have had it from the outset. This is not a new feature IMO, but basically bringing beta-quality features up to release quality. It makes them usable beyond general proof of concept.

    There's a half-baked, feature which allows one to hive session storage off to a redis data store which isn't bad conceptually, but I don't think the realisation is that good. Still: this is an administrative feature, not a language feature.

    The marquee features of ColdFusion 2016 are ColdFusion-administrative sort of stuff, indeed the main feature, the API Manager is a completely separate app, and I'm not sure why it's been shipped as part of ColdFusion. Note it's an API Manager, not a ColdFusion API Manager. SO it's like a separate Adobe product they bunged in with ColdFusion 2016 simply cos they got the same team developing it. Well: developing it at the expense of actually working on ColdFusion itself, it seems.

    ColdFusion Builder has had a code security scanner built into it, but that's CFB not ColdFusion, so who cares about that? It requires using CFB to use it, so that pretty much invalidates it as far as being relevant goes, I think.

    One caution I have here is that the pre-release cycle seemed very short this time around. It's usually about a year, I think; this time it was more like six months. I cannot go into detail about the inner workings of the PR due to an enduring NDA, but I will let the reduced duration speak for itself. As I would always advise: do not upgrade your environments yet. Perhaps put a ColdFusion 2016 server into your test lab and run your tests against it. But do not make plans to go live with it yet. I would usually recommend waiting until the .0.1 or .0.2 release of any new ColdFusion version before running with it, but if one takes the duration of the prerelease, I think one ought to consider this a public beta rather than the release version, so perhaps hold off longer even than usual.

    What an abject disappoint.

    As a footnote to this, Ray's written an article about his thoughts on ColdFusion 2016, as well as pushed the boat out a bit and made some (supposedly) NDA-friendly observations about the ColdFusion 2016 Pre-release programme. You've probably already read his stuff before landing here, but if not: Adobe ColdFusion 2016 Released.

    I'll give those query iteration methods a whirl and report back...

    --
    Adam

    Tuesday 26 January 2016

    ColdFusion: a list of tickets I'm awaiting an update on

    G'day:
    This is just gonna be a list of ColdFusion tickets I've raised, which are currently marked "to fix" but have no indication of in which release they're gonna be fixed. As the ColdFusion 2016 programme has been under way for about six months now, it's a bit odd that Adobe haven't been more firm about what's going on with them. I'm gonna compile a list here, and put it on the ColdFusion Team's radar.

    It's gonna be pretty dry reading. It's gonna be literally a list of links. You should probably just close the browser window now. Unless you're from the Adobe ColdFusion Team, I mean. IN which case you should open every single one of them and follow it up, pls.

    Hey... I did tell you it was just gonna be a list of links.

    Righto.

    --
    Adam

    Friday 8 January 2016

    Adobe opening up ColdFusion 2016 prerelease to more punters

    G'day:
    I can't really say too much about this (NDA, etc), but the Adobe ColdFusion Team have posted this on their blog "Pre-release for API Management capabilities in ColdFusion Raijin".

    So if you're interested in testing their new API Manager (which I am not, I hasten to add), then you can go give them a hand. Adobe really could use more tester-eyes on this thing, so if you're so inclined to pitch in, you'd be helping both Adobe and the subsequent user community of the API Manager.

    Righto.

    --
    Adam


    Friday 18 December 2015

    Survey results: singular or plural for datetime component setters?

    G'day:
    Yesterday I asked you whether for methods for setting the discrete components of a datetime whether you prefer singular - eg: setMinute() - or plural - setMinutes(). The reason for this is that Adobe are adding those methods in ColdFusion 2016, and there seems to be some flip-flopping on whether each one should be singular or plural. Details here:  3374275: Add granularity to createDateTime().

    I've had 50 responses now, and the trend is fairly clear, so I'll tabulate the results:

    ComponentSingularPlural
    Year96%4%
    Month96%4%
    Day96%4%
    Hour80%20%
    Minute76%24%
    Second72%28%
    Millisecond72%28%

    So that's reasonably clear: most people prefer singular. That said, a decent percentage of people prefer singular for the date components and but plural for the time components.

    Two people went plural for everything; 35 went singular for everything. One person went singular for everything except for the minute component (slightly odd choice, that one, I reckon).

    There were a few comments too:

    I would go for singular even though some sound better as plurals. Though in the end as long as it's consistently one or the other and not a horrible mix I would be OK with it.
    This is my opinion too. It's not an exercise in grammatical accuracy or even to do with how human language would do it. They're methods of an object.

    Arse Arse Arse
    Yes. Yes you are. At least you spelt it right though.

    you're setting an hour, not a range of hours
    This came out a bit, and not something that occurred to me previously, but they're correct.

    As far as getting or setting, I think the plural form is more conceptually consistent. Though both can be equally argued for. Second refers to the value place, but Seconds refers to values in that place. I believe both Java and Javascript use setSeconds(), which is probably more comparable to ColdFusion, but other languages use setSecond() and set('second'), and SQL uses dateAdd('seconds'...), so there doesn't seem to be a whole lot of consistency across languages. In the end, I hate date manipulation anyway, so whatever method they choose will work for me. Unless they go with something like changeTime('addSecond(s)'). :-)
    I'd like it if you could tease out why plural is more conceptually consistent. I don't get that.

    plural makes no sense to me here? You are setting the "hour of the day" not the "hours of the day"


    I avoid plurals on my methods and DB field names. I have exactly 2 exceptions to this rule: Comments Details IMHO, these two by their nature are not singular. I also thing of these as being free-form text.


    Consistency above all else. As indicated by my responses above, my preference is singular. This is in part because some just don't work (for me, mentally) as plurals. Consider: setYears()? Ew. Combine that logical preference for _some_ of them to be singular, with the desire for _all_ of them to be consistent, and you arrive at: All of them should be singular.


    Either way, just please be consistent!
    Good pragmatism.

    use singular or plural to match JS implementation
    Why JS? I don't think the JS implementation here is very sensible, and I was left wondering why they made the decision that they did.

    I prefer all singular. Especially since year(), month(), day(), hour(), minute(), second() exist in the language. I think it'd be odd to keep those as-is and introduce .setHours(), .setMinutes(), setSeconds(). I'd rather introduce millisecond()/.setMillisecond(), even if I don't typically say "the millisecond", and retain consistency. On a different note, I am happy to see Adobe presented their proposal so that we can offer feedback. Very nice there. Thanks Adam for doing this survey!, -Aaron
    Cheers dude.

    Just follow what JavaScript has already done!


    You are setting a single value of a property not several values although that value could be more than 1.


    I would match the javascript date object setters: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date they use setDate() instead of setDay() for the date part of the month.
    Again... I think JS ballsed this up, so not a good precedent to follow. There is also not intrinsic connection between CFML and JS to warrant following their precedent in the first place.

    If you go look at the current date/time functions on cfdocs.org, you see MOST of the function names are singular (daysinMonth, daysinyear only plural), so it seems to me that Singular should be used to keep in sync with the rest of the date/time functions.
    Good call. I did not check that. So internal consistency would be to be singular. The existing procedural "getters" are all singular, for example: year()... through to second().

    You are setting a single value, therefore, it should be singular. setMillisecond() = set the millisecond value to
    Correct.

    Initially, I favored the plural for the Minutes, Seconds, and Milliseconds. But after thinking about it more, I think that the singular makes the most sense, and will help to keep the purpose clear. Otherwise, I could see someone saying those functions should take ranges.
    Thanks for giving it some thought, and sufficiently so for you to actually change your position on the matter. Nice one.

    Unsure as to why anyone would want some plural and some not. Let's pick a sensible rule and stick with it throughout. Signular all the way, as the method is referring to the property, not multiple things.


    Date/Time is a singular entity If you were setting a range/duration, then this should plural


    Adam loves the wallabies
    Specifically their services to the All Blacks 2015 World Cup campaign. Cheers for that, Andy.


    So there's a suggested case for following JavaScript's Date methods, which would be:

    • setFullYear() (setYear() is deprecated)
    • setMonth()
    • setDate()
    • setHours()
    • setMinutes()
    • setSeconds()
    • setMilliseconds()  

    What a mess. There's four different approaches in the first four methods. Screw that. This is also ignoring the fact that those ones don't match the existing getter functions CFML already has. I think this is a pretty invalid position to take, to be honest.

    The people advocating for this probably just meant in the context of plurals for hours, minutes, seconds, but I wanted to demonstrate that I think not a lot of thought went into how JavaScript did this, so I don't think there's a good reason to follow them.

    Other people reckon using plurals for some but not others based on how one would use them in English: it's quite likely to refer to the year of the date, but the minutes of the hour. But the hour of the day. English is not a very sensible language, and not everyone speaks English so I think following English's weirdo rules (even though these words are in English) is about as helpful as following JavaScript.

    So at the beginning of this I was fairly certain sticking with singular would be the better approach, and the more I think about it now and look at what other people say, I am even more adamant. I am going to advocate on 3374275 that they stick with singular. Which, indeed, is what they've already done and they were going to change it to plural for hours, minutes and seconds, so this saves them some work.

    Cheers everyone.

    And thanks, Andy, for reminding me how the All Blacks smashed the Wallabies in the Rugby World Cup final.

    Righto.

    --
    Adam

    Wednesday 16 December 2015

    Survey: singular or plural for datetime component setters?

    G'day:
    ColdFusion is adding date/time component setters methods to CFML, eg: myDate.setYear() etc. The ticket covering this is here: 3374275: Add granularity to createDateTime().

    The methods could be implemented with a singular term for the date component, or plural. EG: setHour() or setHours(). There's currently some disagreement over the approach to take here.

    I thought I'd put it to the CFML community, so have concocted a brief survey to ask the question: Date component setter methods for CFML. It basically asks what your preference is for each method, and there's a box for other observations.

    Please go fill it out if you can be arsed.

    Cheers.

    --
    Adam

    Monday 14 December 2015

    ColdFusion: more on this encodeForHtml() crap

    G'day:
    This is a follow-up to last week's ColdFusion: please help me discourage Adobe from releasing bad code article; my inspiration drawing from Brad and Sean's comment on same. I found myself writing too much in reply, so have extracted it into an article instead.

    First Brad:
    [...] the word "encode" is noise in the method if the class is already called "Encode". I kind of like "Encode.forHTML()" to cut down on verboseness. I'm not too much of a fan of Ruby's uber-succinct shortcuts [methods like to_s() instead of toString() ] but I think there's something to be said for trimming down on unnecessary fluff typing.
    I initially started with the same thought (see my comment on the ticket):
    So they should be in something like:

    String Encode.forHtml(String stringToEncode)
    Sorry, was being hasty before. That class / method name example I gave was rubbish: Encode is a verb, so makes no sense as a class name; and the method suggestion had similar grammatical shortcomings. Something more like Encoder or StringEncoder or something for the class, and the methods themselves should be the same as their procedural counterparts, so:

    static String StringEncoder.encodeForHtml(String stringToEncode)
    The name of the method needs to stand on its own two feet (methods have two feet, just like you and I, apparently ;-). "forHtml()" does not adequately describe what the method does. This is a bit semantic given it'll generally be referenced in context with its class, but I still think a method name should describe what it does. "forHtml()" sounds like one is coming in half-way through the description.

    Of course at that point, the only difference (to the average programmer) between

    Encode.forHTML()

    and

    EncodeForHTML()

    is one character and I think the latter is easier to remember and easier to type since it reads the same but isn't peppered with punctuation.
    Well I'd be advocating StringEncode.encodeForHtml(), so that makes even more typing still. I think an appropriate level of brevity is a factor in language design, but not the chief one. And I programming is not an exercise in typing: it's an exercise in thinking. I am perpetually bemused by these people who think programming is some sort of typing race. In the bigger scheme of things, typing a few extra characters each time one wants to encode something? So what?

    Also in equivocating over Encode.forHTML() and EncodeForHTML() you're arguing a case for something I'm not contesting. Or even talking about. This conversation is not about whether we should have a headless function or an OO method: that horse has already bolted.

    I'm kind of torn here, because I like the highly-academic idea that a language does things "properly" and according to best practices (or at least the precedent set by other languages at the time) but I also like the idea that CF (and many other dynamic, loosely typed languages) bend rules here and there with an end result of making things as simple and easy for the developer as possible in order to get code out quickly and efficiently. (Push complexity on the compiler, not the programmer) At the end of the day, I'm not quite sure what real-world difference there is between a collection of related functions that differentiate themselves by starting with "Encode." and a collection of related functions that differentiate themselves by starting with "Encode".
    This is - again - misdirected equivocation. This is not part of the discussion. The ticket is specifically about the member-function implementation of the encoding functions.

    Wednesday 9 December 2015

    ColdFusion: so there won't be a ColdFusion 12?

    G'day:
    Yes, this is obviously a case of two things:

    1. Betteridge's law of headlines...
    2. ... being used for click baiting (or indeed in this case, click bating perhaps ;-).
    There will definitely be a "next" version of ColdFusion, but it seems Adobe might  skipping forward 2005 major version numbers, and going with ColdFusion 2016 instead.

    Well if this is anything to go by, anyhow:


    This is on ticket 4097705, which is something about <cfpdfparam> or something. I did not read the detail as I don't care about PDFs. Or their params.

    So... ColdFusion 2016? Yeah, why not.

    Best I go re-tag all my "ColdFusion 12" articles as "ColdFusion 2016".

    --
    Adam