Showing posts with label CFML. Show all posts
Showing posts with label CFML. Show all posts

Saturday 7 January 2017

Survey: CFML usage and migration strategies

G'day:
This comes off the back of a few unrelated discussions I've had recently about people having moved on from CFML, and there being a reasonable desire to encourage and facilitate other people to do so too. There's a feeling that a lot of CFML devs might move away from CFML if they were helped to, but lack the confidence to make the leap themselves, or know where to start.

One suggestion floated was to start a project similar to ColdFusion UI the Right Way, but basically covering any part of CFML. Someone's suggested they might help giving examples of how to do stuff in Go; and I'd definitely help out giving examples in PHP (which might encourage people to stick with CFML, I dunno! ;-)

The motivation for this is that I think persisting with CFML is - for a lot of people in a lot of situations - career "assisted dying". I think for most CFML devs it's deleterious to stick with it. I'd like to help show these devs that other languages aren't scary, and indeed a bunch of them are similar in ease of use as CFML claims it is.

I've created a survey on freeonlinesurveys.com. I used to use Survey Monkey for this, but they now limit their free surveys to ten questions, and this one has 15. I've not tried freeonlinesurveys.com (either as a provider or a consumer), so I hope this works out. The UI for creating the surveys sure was easy to use though.

Anyway, the survey is here: "CFML usage and migration strategies". Go fill  it in if you can be arsed. To let you know what yer getting into, the survey is 15 free-flow questions, as follows. I've included my own answer for each question:
  1. Provide a brief comment about youself (don't worry about your CFML usage or dev work just yet: this is just about you). Don't worry if you'd rather not give too much detail, that's cool.
    This is my own answer: Adam Cameron. I'm London-based and (breaking my own rule slightly here) have been a dev for 16 years and in the IT industry for... blimey... 23 years.
  2. How did you come to be a developer, and are you primarily a developer or is it an adjunct to another role (like a sysop or designer or something like that)?
    I did programming at polytech, and loved it. I then took the first job that presented itself to me, which was as a sysop & desktop support bod on a NetWare network. I started using CFML there just before I went abroad for a few years. On return I got offered a job as a CFML dev with no professional programming experience at all.
  3. Summarise your CFML usage timeline (just timeline for this one). Include things like what year you started, when you moved on, if you have. (or what's the time timetable for moving on if it's just planned). Mention versions in this one.
    I started in 2001, and stuck with it until 2014. I shifted to PHP because the company I work for did. During that time I used all versions of CF from 4.5->9 professionally, but also have good knowledge of 10-12 as well, as I've been involved heavily in the CFML community and testing of ColdFusion's CFML implementation. I've never used any other CFML platform other than ColdFusion, other than experimentally & community participation.
  4. During that time was it your primary or sole dev language, do you think? Or was it always an adjunct to some other language? How did it fit within the mise en scene of your daily usage? For the purposes of this answer, let's consider "a wee bit of client-side JS and a bunch of HTML & CSS" as a given. Only mention those if they represent a significant part of your work.
    Yup. I did purely CFML from 2001-2014.
  5. Did you work on just in-house code bases for your employer, or did you also work on third party code bases - like open source projects - too.
    Just in house, and for client applications when I worked in a studio.
  6. If you're still primarily a CFML developer... why? That's not a loaded question, and I'm not suggesting that you're wrong for being where you are. It's just good framing information. Don't answer this if you've moved on from CFML: the next question is for you.
    I've moved on.
  7. If you've moved on from CFML: why? Did you just change jobs? Did other languages you were using just seem more appealing? Over time did you find yourself using CFML less and less? Did you actively change because of career-longevity considerations? What language(s) did you move to? Stuff like that.
    I was offered a job doing CFML, with no professional programming expertise at all, and it was a good way of getting away from doing network / hardware / desktop suppport work, which I didn't enjoy. That was my only reason for even starting with CFML: it was easy to pick up. I stuck with it cos I am lazy, and couldn't be arsed changing, and it paid the bills. By the time I decided I'd had enough I was tithed to my employer, so had to stick with them. We started a move to C# but that didn't take as we got bought out by a PHP shop. So now I do PHP. It was a good opportunity to shift languages, even if PHP would not have been my choice.

    My chief concern with changing languages is having to take a pay cut from "senior" dev to being a newbie in another language. I cannot sustain a pay cut.
  8. Do (/did) you use primarily or solely ColdFusion; or Lucee or Railo; or some variation of BlueDragon? In what proportions? If you migrated from one to another: why?
    ColdFusion. I never used any of the others professionally, but have messed around with all of them a fair bit.
  9. Do (/did) you participate in any CFML-based open source projects? To what degree (like you are the owner or primary committer of the project; or just a single commit; or raised some bugs but never actually coded anything; or wrote some docs, or whatever)?
    I raise bugs when I find them, and have helped a bit with cfdocs.org and luceedocs too. But never submitted a pull req.
  10. And what about in other languages?
    I've raised a bug in PHP 7 which got fixed. I have not directly contributed to any side projects though.
  11. What is or was - for you - the best feature of CFML which has you going "yeah, that's pretty cool actually". List more than one if you like. Importantly: had you compared similar functionality to how it's done in other languages, or was it just based on liking the CFML feature?
    That it's loosely and dynamically typed,which cuts down on pointless boilerplate code like one sees in Java. I didn't really compare to other languages too much.
  12. Are there any CFML features that would have fallen into that category for you when you were doing CFML, but ended up not being as cool as you thought when you looked at other languages?
    <cfquery> isn't really any easier to use than how it's done in any other language. That's a myth.
  13. What is it about CFML (or the underlying ColdFusion / Lucee / etc platform) you like the least? Explain why, if poss. Again, list as many or as few as you like. If this/these contributed to you moving on (if you have, I mean), mention that too.
    The vendor attitude. Adobe is slack at their job with ColdFusion, because they have too "enterprise" an approach to something that should be dynamic and agile. The community is very small and shrinking. Community expertise seems to migrate away, rather than steward the community (most of the community stalwarts still around aren't even CFML devs any more!)
  14. If there was a project similar to "ColdFusion UI the Right Way", but aimed at any part of CFML (like how to make a DB query in PHP instead of CFML for example), would you be keen to help on it? Would it be of interest to you to be a "user" of it?
    Well yeah. It was partly my idea.
  15. What else do you think might be useful to share?
    nothing really
So you could be writing a lot of stuff there: that's excellent if you do. Or you could write as little as you like: also cool, just slightly less so.

One of my Twitter contacts said that if I furnished the questions, they'd blog their thoughts. That's another thing you could do instead of doing the survey if you like: just let me know about it, as I don't follow any technical blogs these days (CFML or otherwise). Or you could reply in a comment here (although for me the survey would be easier to deal with, to be honest). Just whatever.

If you could also circulate word about this to your CFML contacts, that'd be cool too. I dunno how much notice the CFML community takes of me any more. Cheers.

That URL again: https://freeonlinesurveys.com/s/AuQ2WWKy#/0.

Or just RT it:



Righto.

--
Adam

Friday 16 December 2016

cf(it's a~)live.net

G'day:
I received some excellent news from Russ Michaels this morning:

cflive.net is now back, thanks to hostek who donated me a server for cflive and cfmldeveloper, which will also be back soon.

Cool! I missed cflive.net. It might not be as shiny and fully-featured as trycf.com, but it has the one benefit of just getting on with it. It saves yer code and runs it. trycf.com monkeys with both the code and the output thereof, which I've never liked, and never felt was that reliable for anything other than superficial demonstrations. I have always felt happy testing issues on cflive.net, whereas I find trycf.com to be a bit of an "unreliable narrator".

The one feature I wish cflive.net would pinch from trycf.com is the ability to save code with a shareable link.

Anyway... Both services are valuable - almost indispensable - resources for the CFML community, and I'm glad cflive.net is back.

Good stuff, Russ; and thanks, Hostek, on behalf of the CFML community.

Righto.

--
Adam

Thursday 10 November 2016

PHP & CFML: I get thrown by how static variables work

G'day:
I like it when I demonstrate to myself I'm a bit thick. Today was one of those days. I've been working with PHP for a coupla years now, and pretty much have ported my OO knowledge over to it. Coming from a CFML background one thing I'm not so familiar with is static variables and methods. I "get" it, that's cool, but clearly I'm still mid-way up the learning curve, and some idiosyncrasies still elude me. I found one today.

I was doing some testing, and the situation I was in was that my test classes needed some subclassing... don't ask... it was legit but I cannot go into details and it'd bore you rigid anyhow. In my base class I had a static variable which I needed to have a different value for in the subclass (it was actually a sub-sub-class. Dumb tests). So I set it to a new value in my test method, then the test method called a method from the base class, and the test went splat cos it was still using the base class value. This confused the shit out of me at first. Here's a simplified example:

class GP {

    public static $test = "GP";
    
    public function fromGP(){
        return self::$test;
    }
    
    public function get(){
        return self::$test;
    }

}

class P extends GP {

    public static $test = "P";
    
    public function fromP(){
        return self::$test;
    }

}

class C extends P {

    public static $test = "C";
    
    public function fromC(){
        return self::$test;
    }

}

$c = new C();

echo "fromC: " . $c->fromC() . PHP_EOL;
echo "fromP: " . $c->fromP() . PHP_EOL;
echo "fromGP: " . $c->fromGP() . PHP_EOL;
echo "get: " . $c->get() . PHP_EOL;

So:

  • I've got a grandparent class (GP) which sets a static variable, and has two methods to get its value.
  • And a Parent class (P) that extends GP, with its own value for the static variable, and another different method to get its value.
  • And a Child class (C) which does the same thing, but to the Parent.
  • In my test rig I create a C instance, and call each of the methods.
Now cos I'm thick, I expected this output:

fromC: C
fromP: C
fromGP: C
get: C

Because C overrides the value for the static test variable, so the inherited methods should all use that value.

But what I did get is this:

fromC: C
fromP: P
fromGP: GP
get: GP

It took me a while to click. I changed my variable to be not static, and got the results I expected. Changed it back: back to the "wrong" (where "wrong" is "not what I wanted") answer. Then it dawned on me I guess. Static variables are bound to the class. I already knew this, but the ramifications didn't hit me initially. $c might be an object that inherits from P and GP, and obviously object variables will also follow inheritance rules, but static variables relate to the class, not the object. So in the class GP, in which the method fromGP is defined... self::$test refers to the static $test variable in that class. So: the value is GP. and on from there for fromP and down to fromC too (and get).

It made sense once I engaged my brain.

When I got home from work I decided to "ask" Java what it thought about this, and knocked together an analogous solution. Please pardon my shit Java skills.

package me.adamcameron.test;

public class GP {

    public static String test = "GP";
    
    public String fromGP(){
        return test;
    }
    
    public String get(){
        return test;
    }

}

package me.adamcameron.test;

public class P extends GP {

    public static String test = "P";
    
    public String fromP(){
        return test;
    }

}

package me.adamcameron.test;

public class C extends P {

    public static String test = "C";
    
    public String fromC(){
        return test;
    }

}

package me.adamcameron.test;

public class Test {

    public static void main(String[] args){
        C c = new C();
        
        System.out.println("fromC: " + c.fromC());
        System.out.println("fromP: " + c.fromP());
        System.out.println("fromGP: " + c.fromGP());
        System.out.println("get: " + c.get());
        
    }

}

And that - predictably - confirmed PHP's behaviour (not that I doubted PHP. Much):

>java me.adamcameron.test.Test
fromC: C
fromP: P
fromGP: GP
get: GP

Coolio. I'm also pleased I managed to write 20-odd lines of Java and get it right firstsecond time (it was a typo! ;-)

Last but not least, I remember that Lucee's flavour of CFML introduced static variables in Lucee 5, so I decided to see what it did:

component {

    static {
        test = "GP";
    }
    
    public function fromGP(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}

component extends=GP {

    static {
        test = "P";
    }
    
    public function fromP(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}

component extends=P {

    static {
        test = "C";
    }
    
    public function fromC(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}


<cfscript>
o = new C();

writeOutput("fromC: #o.fromC()#<br>");
writeOutput("fromP: #o.fromP()#<br>");
writeOutput("fromGP: #o.fromGP()#<br>");
writeOutput("get: #o.get()#<br>");
</cfscript>

When I ran this...

fromC: C
fromP: C
fromGP: C
get: C


Hrm. So Lucee does what I originally wanted, but I now reckon this is not what it should be doing. I best go ask someone from LAS what the story is there.

Anyway that was that. Just a small lesson learned for me today.

Oh, btw... the solution was to just make the variable not static. Then me code worked fine. There really was no need for it to be static in the first place, on reflection.

Update:
It's important to read Kalle's comment (below) regarding static:: in PHP.

Righto.

--
Adam

Sunday 25 September 2016

CFML: "It's alive!". Really now?

G'day:
Yesterday I was "helping" someone on the CFML Slack channel with some challenges they had with CFC serialisation. The conversation turned to ColdFusion's custom serialisers (see an earlier article: "ColdFusion 11: custom serialisers. More questions than answers"), and how pants I think the implementation & entire undertaking of that part of CF11 was. I'll just quote myself (full transcript of entire discussion here: Gist / adamcameron / slack.txt:

I did try to position the whole undertaking as "here be dragons"

On the face of it, it's a cool idea

But - as per most less-than-obvious things the Adobe CF Team tries to do - they completely cocked it up, having no real understanding of how ppl use CFML

[...]

What concerns me about Adobe's implementation is it seems to be very "not OO" which worries me when it's actually implemented by ppl who are supposedly Java devs.

[...]

I floated it "at the appropriate time" (nod to not speaking about the pre-release programme, but... well... f*** them), but the relevant parties just either didn't care or didn't get it or refused to get it.

It's the perpetual symptom that Adobe never consult the community before they blindly go and implement ill-conceived and poorly realised shite.

They ought to feel free to only consult with their major-client "IT stakeholders" for features like the API Manager and to a degree `<cfclient>` (hohoho), but when it comes to nuts 'n' bolts language implementation... they need to ask the ppl on the ground.

But based on that recent survey Adobe did... there won't be any more focus on the CFML language from them. They're only interested in buzzword-completion/box-ticking IT-Manager-pleasing platform features

In response to that someone observed they admired my sticking power with CFML (thanks for saying that), but my reaction was that Adobe finally "broke" me with CF2016 being such a waste of time for the CFML dev, as they did hardly anything in the language, instead of rolling something that's really nothing to do with ColdFusion: the API Manager into CF2016 and making it the marquee "feature".

At the same time Lucee 5 lost objective direction in that they indulged Micha's whim to re-write the thing to be OSGi capable (or whatever the correct term is), and mess around with the .lucee lang thing which ended up being both stillborn and evidence that LAS is out of their depth when they're not just copying what Adobe does. Harsh, sorry LAS, but that's my opinion.

I have given up on both the vendors when it comes to CFML, and personally I've moved from CFML to PHP now, but I'm still there in the CFML community helping people where I can. So - as far as the community goes - I'm suffering from "just when I thought I was out... they pull me back in", as it were. Apologies to Al Pacino there. But theatrics aside, that's my choice, so no worries.

Patrick came back with a reasonable riposte to my comments about being let down by the vendors:

Yeah, those sorts of public "divorces" (Railo-->Lucee) are never pretty, but it was really necessary. Kudos to the core of the Railo peeps who held it together. LAS is quite strong now, and I believe we're the future of CFML as a platform. I'm especially excited to see what the community can do with the extensible framework in place now. What say we all co-create a really vibrant ecosystem of products? With Adam as our cheerleader (I don't see it as cynical, mate!), I have no doubt we'll get it done.

Good on him, but I disagreed with one thing he said, and this lead to a bit of a diatribe from me:

You can't just say "What say we all co-create a really vibrant ecosystem of products?" and have it happen. The CFML community have demonstrated that they're - for the most part - takers rather than givers. Even with ppl asking for help from the community I see a sense of (misplaced) entitlement from them.

That said there's a minority of good worker bees, and a bunch of people who are good people but they're still just in CFML 9-5 (which is fine), but will not grow or strengthen the community.

Most of the stalwarts of the community are companies who have in the past invested a lot of effort into CFML (not the community necessarily, but for themselves), and it'd be a large $$$ cost for them to move. That's going to be why some of them continue to persist with CFML, and also encourage Lucee[:] as LAS is more likely to work with those small to mid-level companies to provide what they want out of CFML. Same as with Adobe and larger corporates.

The likes of Daemon and MSO.Net and Ortus and Pixl8 aren't going away when it comes to CFML due to how they've stacked their egg baskets, and I reckon they're more likely to stick with CFML than [it is that] LAS will [continue to] exist. Although one of them might consider "buying" Micha to perpetuate CFML for them if LAS founders. At which point Lucee will become kinda like OpenBD is for Alan Williamson's outfit

Sean was reading this and "emoting" with thumbs-ups in agreement and the like, then we got to chatting about OSS & CFML and that sort of thing. I'll spare you that.

But I stand by what I say above. I've been in the CFML community since 2001, and been active in the community all that time, and have been watching the ebb and flow of the CFML tide, be it ColdFusion or BlueDragon or Railo or Lucee. So I think my opinion is possibly an informed one. And I always offer it for the sake of informing, rather than posturing. People are quite welcome to disagree with me and to put a case forward as to why I'm mistaken. I like finding out I'm wrong, and I like debating (or arguing ;-) a topic with people. Provided they actually make a case.

Geoff Bowers (of afore-mentioned Daemon fame: he's the boss; and I think he's El Presidente of LAS) joined the conversation and suggested we should not be having such conversations, as they hurt the CFML community. And Geoff's position is that "The CFML Community" is a meaningful entity.

I disagree.

I disagree with a few points or inferences here:

  • Any conversation by experienced members of a given community will possibly have merit, even if they suggest that the best course of action for the community members is to prepare themselves for the time when "the community" doesn't really mean anything any more.
  • Even if the points being discussed have no merit, I disagree with the notion that the discussion should not be had because one member of the community doesn't like the sound of it, or it doesn't match their agenda.
  • I don't actually think "The CFML Community" is a thing in and of itself which automatically deserves preservation. This might seem odd, but I'm interested in the welfare of the members of the community, not some completely intangible label "The CFML Community". That's meaningless. I will help the individual people in the CFML community whenever I can (or, realistically, when I feel like it, but that's quite often ;-), and I think helpful advice to people who rely on CFML for their crust these days is: plan an exit strategy. It's convenient for Geoff to deny this (I'll get to that), but CFML as a language has had its day. It had a unique selling point a decade ago: it made making dynamic websites quite easy compared to where Perl or PHP or JSPs were at the time. It does not have that USP any more. CFML does a bunch of things really well, but not that much better than any number of more popular (and easy!) languages do these days. The thing is the other languages bring their own participatory communities with them, and with those communities come rich OSS efforts which ensure whatever one wants to do that might be off-piste for a language: there'll be a third-party module for that.


But I digress: no, I don't think "The CFML Community" is worth protecting. The community members are, and my best (-quality and -intentioned) advice is to start planning an exit strategy. You'd have to be an arsehole to place the preservation of "The CFML Community" ahead of the welfare of its members.

CFML has precisely one remaining relevant purpose: to keep CFML devs in work if they can't do anything else. CFML is not going places. It's staying where it is. But the ice floe where it's positioned itself is melting away, growing smaller and smaller, and nothing's gonna reverse this.

Geoff disagrees. That's cool, but other than "I disagree" and a few half-hearted ad hominems ("none taken", btw), he didn't actually refute a single thing I said. I'd be dead f***in' keen to hear what he has to say though, to demonstrate how CFML is still alive in any meaningful way though. And how it's a good prospect for uptake, and how CFMLers shouldn't be planning an exit strategy.

My cynicism suggests to me that Geoff is mostly concerned about "The CFML Community" because it's commercially prudent for him to do so: he's built a company and a livelihood on CFML. So it's beneficial to him for there to be "The CFML Community" out there, as they're potential revenue opportunities. That's completely fine and I see where he's coming from. But it seems disingenuous to me for him to be suggesting myself or others don't have the community's best interests at heart when we offer advice that is not convenient for his business model.

Another issue I'll touch on in this increasingly rambling diatribe is Geoff's assertion that my observations have an actual deleterious impact on the community. This is seemingly based on the notion that any negative comment is intrinsically deleterious, and for "names" such as myself to me making these negative comments is more deleterious still. I guess cos ppl might be inclined to listen to my advice. Heaven f***in' forbid.

He's probably right. But let's get things in perspective here. Things that are deleterious to "The CFML Community":

  1. ColdFusion being a paid-for product in 2016;
  2. Adobe's lack of interest or lack of ability to keep the language contemporary;
  3. every other bloody language being more contemporary and compelling;
  4. and free.
  5. The apathy of most CFML dev resulting in an under-represented community to engender momentum; 
  6. Adobe's lack of ability to market ColdFusion;
  7. Railo v Lucee schism;
  8. Lucee 5 taking an inordinate amount of time to see light of day, for what seems like developer-self-indulgence;
  9. LAS dithering about the place and seeming very incoherent when it comes to what they're doing;
  10. [daylight]
  11. [more frickin' daylight]
  12. Me saying stuff Geoff doesn't like.
One could quibble about some of those items there, but it's the scale of things that is significant. Almost all of CFML users don't know who the f*** I am. A decent subset of those that do can think for themselves and will take what I say with a grain of salt, or already be (dis-)agreeing with me. And the others could do worse than consider what I have to say, and weigh it up for themselves.

But no CFMLer who cannot form their own opinions will have any idea who I am, and will never see any of my written opinions.

I hasten to add that the list above is "things that are deleterious to the CFML community". I see Lucee as a net force for good in the community. But if we're assessing what is unhelpful in the community, then they're a more significant player in all this than I am. And even Lucee's influence one way or the other is completely inconsequential compared to Adobe's.

But go on then, Geoff and others who think CFML is alive and well and is something that should be suggested for new adopters. Tell me why it is. Tell us... "The CFML Community"... why that's the case. Don't just call me an uppity dick or a big meany or whatever the f*** personal attack is easier for you to make. Do something.

Why is CFML still relevant?
Why should new people adopt it?
Why should people stick with it instead of perhaps protecting their future by examining other options?
Why should people not at least suggest that might be something to do?

Go on then...

--
Adam

Sunday 28 August 2016

Blimey, a CFML-centric article

G'day:
But it's nothing that interesting. I submitted my answer ("Friday puzzle: my PHP answer") for the Friday Code Puzzle yesterday, deciding to use PHP, given it's what I do for a crust these days, and I could best visualise the solution in PHP.

But the puzzle was from a CFML chat group, so I figured I might blow the dust of my CFML skills and see how closely a CFML answer would approximate my PHP one. The answer is "quite closely!". Here's a CFML transliteration of the PHP answer:

component {

    parents = {};

    function init() {
        parents[0]["children"] = [];

        return this;
    }

    static function loadFromCsv(filePath) {
        var dataFile = fileOpen(filePath, "read");

        var tree = new Tree();
        while(!fileIsEof(dataFile)){
            var line = fileReadLine(dataFile);
            tree.addNode(
                nodeText = line.listLast(),
                id = line.listFirst(),
                parent = line.listGetAt(2, ",", true)
            );
        }

        return tree;
    }

    private function addNode(nodeText, id, parent) {
        parent = parent == "" ? 0 : parent;

        parents[id].nodeText = nodeText;
        parents[parent].children = parents[parent].children ?: [];
        parents[parent].children.append(parents[id]);
    }

    function serializeJson() {
        return serializeJson(parents[0]["children"]);
    }
}

Note that this is using Lucee 5's CFML vernacular, and this will not run on ColdFusion for a coupla reasons:

  • ColdFusion still doesn't support static methods;
  • It has a new bug with the ?: operator which breaks some of my shortcuts there.

I did not write the full test suite, but I wrote a small test rig and ran all the test variations manually. Here's the trickiest one:

dataFile = expandPath("../../test/testfiles/notOrdered/data.csv");

tree = Tree::loadFromCsv(dataFile);
treeAsJson = tree.serializeJson();

jsonAsArray = deserializeJson(treeAsJson);
writeDump(jsonAsArray);

And it yields the goods:




For completeness, here's the variation I had to do to make this work on ColdFusion:

static function loadFromCsv(filePath) {
    var dataFile = fileOpen(filePath, "read");

    var tree = new Tree();
    while(!fileIsEof(dataFile)){
        var line = fileReadLine(dataFile);
        tree.addNode(
            nodeText = line.listLast(),
            id = line.listFirst(),
            parent = line.listGetAt(2, ",", true)
        );
    }
    return this;
}

private function addNode(nodeText, id, parent) {
    parent = parent == "" ? 0 : parent;

    parents[id] = parents.keyExists(id) ? parents[id] : {};
    parents[id].nodeText = nodeText;
    
    parents[parent] = parents.keyExists(parent) ? parents[parent] : {};
    parents[parent].children = parents[parent].keyExists("children") ? parents[parent].children : [];
    parents[parent].children.append(parents[id]);
}

  • ColdFusion does not yet support static methods, so I cannot use a static factory method to create the tree, I need to instantiate an empty tree and then get it to load itself. Not ideal, but not the end of the world either.
  • Because of various bugs in ColdFusion's implementation of implicitly creating "deep" data structures, and also with the ?: operator, I had to be more explicit with stepping through creation of intermediary structs, and also use keyExists instead of ?:

This isn't bad, and I don't fault ColdFusion for its lack of static yet, but I'm annoyed by finding yet more bugs in ColdFusion when all I want is to run some code.

Back to the Lucee version: I think it demonstrates CFML syntax is a bit tidier than PHP's, as it's not quite so in love with the punctuation, and punctuation is just noise to achieving good Clean Code. But in this example it's much of a muchness, innit?

Anyway, I have little else to add other than that. The approach is identical to the PHP one I did: I just wanted to compare the two languages.

Job done.

Righto.

--
Adam

Tuesday 8 March 2016

PHP's equivalent to <cfsavecontent>

G'day:
I almost wrote an article about this six or so months ago as it's a half-way interesting topic. And now I realise I should have because someone asked the very same question on Stack Overflow ("php equivalent for coldfusion cfsavecontent"). The accepted my answer, so I'm gonna do another sneaky cop-out and replicate my answer as an article here as well. Hey... I wrote it, I should be allowed to do what I like.

For the uninitiated, <cfsavecontent> (or savecontent()) allows one to enclose some code in a block, and any output generated within the block is assigned to a variable instead of being output. This is handy for capturing large strings which need logic to build: much nicer than just string concatenation.

In tag-based code, <cfsavecontent> is pretty slick:

<cfsavecontent variable="content">
    Some text<br>
    <cfif randRange(0,1)>
        <cfset result = "value if true">
    <cfelse>
        <cfset result = "and if it's false">
    </cfif>
    <cfoutput>#result#</cfoutput><br>
    Message from include is:
    <cfinclude template="./inc.cfm">
</cfsavecontent>

<cfoutput>#content#</cfoutput>

And inc.cfm is:

<cfset greeting = "G'day">
<cfoutput>#greeting#</cfoutput><br>

This outputs:

Some text
and if it's false
Message from include is: G'day


That's pretty straight forward. The script version is a bit sh!t:

savecontent variable="content" {
    // stuff goes here
}

Honestly Adobe, WTH were you thinking? Passing the variable name as a string? How was this not just an assignment, like anyone with a glimmer of language design skills might do? And, yes, I have mentioned this before: How about this for savecontent?

Anyway, that's CFML. In PHP one doesn't have tags, but equally one doesn't have weirdo assign-by-string-name constructs either. The equivalent PHP code is:

ob_start();
echo "Some text" . PHP_EOL;
if (rand(0,1)){
    $result = "value if true";
}else{
    $result = "and if it's false";
}
echo $result . PHP_EOL;
echo "Message from include is: ";
include __DIR__ . "\inc.php";

echo ob_get_clean();

And inc.php:

$greeting = "G'day";
echo $greeting . PHP_EOL;

(I'm running this from the shell, hence PHP_EOL instead of <br> for the line breaks).

That's pretty straight forward. What I actually like about this is there's no requirement to have a block structure at all: one simply says "start output buffering", and then later one does something with what's been buffered: in this case returning it, so I can output it.

There's a whole bunch of other functionality in the Output Buffering "package", and now it's piqued my interest so I will go have a look at it.

That's it. self-plagiarism complete.

Righto.

--
Adam

Saturday 5 March 2016

CFML: go have a look at a Code Review

G'day:
I've already put more effort into this than a Saturday morning warrants, so I'll keep this short and an exercise in copy and past.

Perennial CFMLer James Mohler has posted some code on Code Review:  "Filtering the attributes for a custom tag". I'll just reproduce my bit of the review, with his code by way of framing.

Go have a look, and assess our code as an exercise.

James's functions:

Original:

string function passThrough(required struct attr)   output="false"  {

    local.result = "";

    for(local.myKey in arguments.attr)    {
        if (variables.myKey.left(5) == "data-" || variables.myKey.left(2) == "on" || variables.myKey.left(3) == "ng-")  {

            local.result &= ' #local.myKey.lcase()#="#arguments.attr[local.myKey].encodeForHTMLAttribute()#"';
        } // end if 
    }   // end for

    return local.result;
}

His reworking:

string function passThrough(required struct attr)   output="false"  {

    arguments.attr.filter(
        function(key, value) { 
            return (key.left(5) == "data-" || key.left(2) == "on" || key.left(3) == "ng-");
        }
    ).each(
        function(key, value)    {
            local.result &= ' #key.lcase()#="#value.encodeForHTMLAttribute()#"';
        }
    );  

    return local.result;
}

Now for my code review (this is a straight copy and paste from my answer):

OK, now that I've had coffee, here's my refactoring:

function extractCodeCentricAttributesToMarkupSafeAttributes(attributes){
    var relevantAttributePattern = "^(?:data-|ng-|on)(?=\S)";

    return attributes.filter(function(attribute){
        return attribute.reFindNoCase(relevantAttributePattern);
    }).reduce(function(attributeString, attributeName, attributeValue){
        return attributeString& ' #attributeName#="#attributeValue#"';
    }, "");
}


Notes on my implementation:
  • I could not get TestBox working on my ColdFusion 2016 install (since fixed), so I needed to use CF11 for this, hence using the function version of encodeForHTMLAttribute(). The method version is new to 2016.
  • we could probably argue over the best pattern to use for the regex all day. I specifically wanted to take a different approach to Dom's one, for the sake of comparison. I'm not suggesting mine is better. Just different. The key point we both demonstrate is don't use a raw regex pattern, always give it a meaningful name.
  • looking at the "single-expression-solution" I have here, the code is quite dense, and I can't help but think Dom's approach to separating them out has merit.

Code review notes:
  • your original function doesn't work. It specifies variables.myKey when it should be local.myKey. It's clear you're not testing your original code, let alone using TDD during the refactoring process. You must have test coverage before refactoring.
  • the function name is unhelpfully non-descriptive, as demonstrated by Dom not getting what it was doing. I don't think my function name is ideal, but it's an improvement. I guess if we knew *why
  • you were doing this, the function name could be improved to reflect that.
  • lose the scoping. It's clutter.
  • lose the comments. They're clutter.
  • don't abbrev. variable names. It makes the code slightly hard to follow.
  • don't have compound if conditions like that. It makes the code hard to read. Even if the condition couldn't be simplified back to one function call and you still needed multiple subconditions: extract them out into meaningful variable names, eg: isDataAttribute || isOnAttribute || isNgAttribute
  • key and value are unhelpful argument names
  • slightly controversial: but unless it's an API intended to be used by third-parties: lose the type checking. It's clutter in one's own code.
  • there's no need for the output modifier for the function in CFScript.
  • don't quote boolean values. It's just false not "false".

Unit tests for this:

component extends="testbox.system.BaseSpec" {

    function beforeAll(){
        include "original.cfm";
        include "refactored.cfm";
        return this;
    }

    function run(testResults, testBox){
        describe("Testing for regressions", function(){
            it("works with an empty struct", function(){
                var testStruct = {};
                var resultFromOriginal = passThrough(testStruct);
                var resultFromRefactored = extractCodeCentricAttributesToMarkupSafeAttributes(testStruct);
                expect(resultFromRefactored).toBe(resultFromOriginal);
            });
            it("works with an irrelevant attribute", function(){
                var testStruct = {notRelevant=3};
                var resultFromOriginal = passThrough(testStruct);
                var resultFromRefactored = extractCodeCentricAttributesToMarkupSafeAttributes(testStruct);
                expect(resultFromRefactored).toBe(resultFromOriginal);
            });
            it("works with each of the relevant attributes", function(){
                var relevantAttributes = ["data-relevant", "onRelevant", "ng-relevant"];
                for (relevantAttribute in relevantAttributes) {
                    var testStruct = {"#relevantAttribute#"=5};
                    var resultFromOriginal = passThrough(testStruct);
                    var resultFromRefactored = extractCodeCentricAttributesToMarkupSafeAttributes(testStruct);
                    expect(resultFromRefactored).toBe(resultFromOriginal);
                }
            });
            it("works with a mix of attribute relevance", function(){
                var testStruct = {notRelevant=7, onRelevant=11};
                var resultFromOriginal = passThrough(testStruct);
                var resultFromRefactored = extractCodeCentricAttributesToMarkupSafeAttributes(testStruct);
                expect(resultFromRefactored).toBe(resultFromOriginal);
            });
            it("works with multiple relevant attributes", function(){
                var testStruct = {"data-relevant"=13, onRelevant=17, "ng-relevant"=19};
                var resultFromOriginal = passThrough(testStruct);
                var resultFromRefactored = extractCodeCentricAttributesToMarkupSafeAttributes(testStruct);
                expect(resultFromRefactored).toBe(resultFromOriginal);
            });
        });

    }

}


Use them. NB: the includes in beforeAll() simply contain each version of the function.

One thing I didn't put as I don't think it's relevant to this code review is that one should be careful with inline function expressions. They're not themselves testable, so if they get more than half a dozen statements or have any branching or conditional logic: extract them into their own functions, and test them separately.

Oh, and in case yer wondering... yes I did write the tests before I wrote any of my own code. And I found a coupla bugs with my planned solution (and assumptions about the requirement) in doing so. Always TDD. Always.

Anyway, all of this is getting in the way of my Saturday.

Righto.

--
Adam

Tuesday 16 February 2016

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

Thursday 28 January 2016

TeraTech's State of the CF Union Survey 2016

G'day:
We're trying to drum up more submissions to TeraTech's State of the CF Union Survey 2016. That's a link to their blog article about it, and the survey itself is here.

As well as making sure you fill it in yourself, please do the rounds of your colleagues and contacts who don't usually participate in the community, and... well... get them to pitch in.

Cheers.

--
Adam

Tuesday 19 January 2016

Floating point arithmetic with decimals

G'day:
As a human... what is the value of z, after you process this pseudocode with your wetware:

x = 17.76
y = 100
z = x * y

Hopefully you'd say "1776". It was not a trick question.

And that's an integer, right? Correct.

CFML

Now... try this CFML code:

x = 17.76;
y  = 100;
z = x*y;

writeOutput(z);

1776 So far so good.

But what about this:

writeOutput(isValid("integer", z));

You might think "YES" (or true if yer on Lucee), however it's "NO".

And this is where young players fall into the trap. They get all annoyed with isValid() getting it wrong, etc. Which, to be fair, is a reasonable assumption with isValid(), but it's not correct in this instance. It's the young player who is mistaken.

If we now do this:

writeOutput(z.getClass().getName());

We get: java.lang.Double

OK, but 1776 can be a Double, sure. But CFML should still consider a Double 1776 as a valid integer, as it should be able to be treated like one. So why doesn't it? What if we circumvent CFML, and go straight to Java:

writeOutput(z.toString());

1776.0000000000002

Boom. Floating point arithmetic inaccuracy.

Never ever ever forget, everyone... when you multiply floating point numbers with decimals... you will get "unexpected" (but you should pretty much expect it!) floating point accuracy issues. This is for the perennial reason that what's easy for us to express in decimal is actually quite hard for a computer to translate into binary accurately.

Aside: we were chatting about all this on the CFML Slack channel this morning, and one person asked "OK, so how come 17.75 x 100 works and 17.76 x 100 does not?". This is because a computer can represent 0.75 in binary exactly (2-1 + 2-2), whereas 0.76 can only be approximated, hence causing the "issue".

The problem really is that CFML should simply output 1776.0000000000002 when we ask it, and it should not try to be clever and hide this stuff. Because it's significant information. Then when the young player output the value, they'd go "oh yeah, better round that" or whatever they need to do before proceeding. CFML is not helping here.

This is pretty ubiquitous in programming. Let's have a trawl through the various languages I can write the simplest of code in:

JavaScript


x = 17.76;
y = 100;
z = x * y

console.log(z);


>node jsVersion.js
1776.0000000000002

>

JS just does what it's told. Unsurprisingly.

Groovy


x = 17.76
y = 100
z = x * y
println "x * y: " + z

println "x: " + x.getClass().getName()
println "y: " + y.getClass().getName()
println "z: " + z.getClass().getName()
println "z: " + z.toString()


>groovy32 groovyVersion.groovy

x * y: 1776.00
x: java.math.BigDecimal
y: java.lang.Integer
z: java.math.BigDecimal
z: 1776.00
>


This is interesting. Whilst Groovy keeps the result as a float (specifically a BigDecimal) - which is correct - it truncates it to the total number of decimal places expressed in its factors. That's how I was taught to do it in Physics at school, so I like this. This second example makes it more clear:

x = 3.30
y = 7.70
z = x * y
println "x * y: " + z

println "x: " + x.getClass().getName()
println "y: " + y.getClass().getName()
println "z: " + z.getClass().getName()
println "z: " + z.toString()


>groovy32 more.groovy
x * y: 25.4100
x: java.math.BigDecimal
y: java.math.BigDecimal
z: java.math.BigDecimal
z: 25.4100
>

In 3.30 and 7.70 there are four decimal places expressed (ie: two for each factor), so Groovy maintains that accuracy. Nice!


Java


import java.math.BigDecimal;

class JavaVersion {

    public static void main(String[] args){
        double x = 17.76;
        int y = 100;
        System.out.println(x*y);
        
        BigDecimal x2 = new BigDecimal(17.76);
        BigDecimal y2 = new BigDecimal(100);
        System.out.println(x2.multiply(y2));
        
    }
}

Here I added a different variation because I was trying to see why the Groovy code behaved the way it did, but it didn't answer my question. I suspected that perhaps it was a BigDecimal thing how it decided on the accuracy of the result, but it wasn't:


>java JavaVersion
1776.0000000000002
1776.000000000000156319401867222040891647338867187500

>

This is a good demonstration of how a simply base-10 decimal fraction is actually an irrational number in binary.

Monday 18 January 2016

hackread.com: putting the "hack" back into journalism

G'day:
I can't post this in response to the article in question as the website requires a Facebook login, one of which I do not have. Anyway, there's this exercise in journalistic cheapshottery (yes, I meant that to be an O not an I) "Program Languages That Generate Most Software Security Bugs". Specifically:

Similar findings from OWASP test results show that ColdFusion, PHP, and Classic ASP, in that order, are the worst languages when it comes to software security.

I'd usually leave it to Brad "CFDoberman" Wood to refute, but I'm undergoing a "slow start Monday" today, so I'll rise to it.

My response was gonna be as follows:

I'm not gonna dispute the raw stats, but I will dispute the analysis and the technical competence of the author, in the context of at least "ColdFusion" (the language is CFML, btw, not ColdFusion. That's like describing .Net as a language. Oh... you do that too. They're not languages).

But anyway, the chief failing on your part here is that CFML is implemented basically as a tag lib atop of Java (that's understating things somewhat, but for these purposes it's appropriate). It's a JVM language. Therefore it's incorrect to say that Java's security features are not available to a CFML application. They intrinsically *are* available. It's all baked in.

As for SQL injection prevention... the tools are there as well to prevent against them... one just needs to use them. Same with XSS (it's a single checkbox config option on the ColdFusion management server).

The problem is not the language or the platform: it's the developers. Most CFML developers are bloody useless. This stems from the fact CFML is very easy to program in, meaning the small niche of developers it attracts often times are just not very good at their jobs, because they don't need to be to be productive. I guess this is the language's "fault": it's too easy to use.

I'm far less au fait with PHP, but I know that SQL injection here is also the fault of the developer, not the language. The tools are all there... ppl are just bad at their jobs.

I can't speak for ASP.

On another tangent... taking cheap shots at ColdFusion as a source of security dismay seems a bit odd to me anyhow. It's such a niche product these days... what percentage of WWW volume does it account for? Even if 100% of sites running CF were vulnerable (which, obviously, they're not)... that would be probably equivalent risk as 1% of PHP sites, given the increased market slice of PHP over CFML. So where does the real-world risk lie then?

All in all, lazy uneducated journalism here. But I imagine your remit is more to get eyes on the page than to do well-considered analysis, or to understand what you're talking about.

Cheers

I really shouldn't pay attention to this crap, as I don't want to encourage people going to the page, but... so be it.

If someone has a Facebook login (someone must), you could do me a favour and post this response in the comments (please attribute it to me, and cross ref back to this article)?

Righto. Back to things that are actually a good use of my time.

--
Adam

Sunday 17 January 2016

Stepping looping

G'day:
Yeah, this is an odd thing to write about, and there's no trick to it: I really do mean this sort of thing:

for i=1 to 10 step 2

I got to thinking about this because of a comment from Chris Dawes on my article Lucee 5 beta: <cfloop> tweak, in which he sung the praises of the CFML tag equivalent of that general construct:

<cfloop index="i" from="1" to="10" step="2">

This is fine for a view, but I'd steer clear of it in my business logic. But what would I use instead? The obvious answer is this:

for (i=1; i <= 10; i+=2)

But these days I try to eschew generic looping like that, in favour of a more purposeful iteration exercise using iteration methods. If I was iterating a collection to remap data, I'd use map(); if I was doing so to translate the collection data to another data structure or object, I'd reduce(); if I was getting rid of stuff I'd filter(), and - if the language allowed it - if I was checking to see if one or any of the collection elements fulfilled some criteria, I'd use some() or every(). It's really seldom that one wants to loop "for the hell of it", if one thinks about it. It's an exercise in processing data or checking data.

Obviously in a view one is likely to be just outputting data from the data collection, so using a general loop is as good as anything. Also if one is skipping records... a stepped general purpose loop is perhaps better than using like an array-based loop and conditionally continuing the loop or some nonsense like that. All good.

But this got me thinking... let's say I was:

  1. writing business logic;
  2. wanting to step over array elements;
  3. still wanted to use array iteration methods.

This is more a mental exercise than a programming one, but it just happened to pique my interest.

CFML

In CFML this is dead easy. Here's an example:

numbers = ["tahi", "rua", "toru", "wha", "rima", "ono", "whitu", "waru", "iwa", "tekau"];

oddNumbers = numbers.filter(function(_,i){return i mod 2;});

writeDump(oddNumbers);

Here I'm using ColdFusion 12's CLI, and the results are as follows:

C:\src\cfml\languageComparison\steppedLoop>cf cfmlVersion.cfm
array

1) tahi
2) toru
3) rima
4) whitu
5) iwa

C:\src\cfml\languageComparison\steppedLoop>

filter() allows us to create an array of every second element simply by doing the usual mod operation. This gives us a new array.

If we wanted to actually render the elements as a string, we'd just chain a reduce() call onto that:

oddNumbersAsString = numbers.filter(function(_,i){return i mod 2;}).reduce(function(combined, oddNumber){
    return "#combined##oddNumber##chr(10)#";
}, "");
CLI.writeLn(oddNumbersAsString);


C:\src\cfml\languageComparison\steppedLoop>cf cfmlVersion.cfm
tahi
toru
rima
whitu
iwa

C:\src\cfml\languageComparison\steppedLoop>

Or, hey: not get so dogmatic about using collection iteration methods, and just leverage a list method:

oddNumbersAsString = numbers.filter(function(_,i){return i mod 2;}).toList(chr(10));

(yields the same output)

I turned my mind to other languages to compare.

JavaScript

Same as CFML really. Just some "spelling" differences:

oddNumbersAsString = ["tahi", "rua", "toru", "wha", "rima", "ono", "whitu", "waru", "iwa", "tekau"]
    .filter(function(_,i){return (i+1) % 2;})
    .reduce(function(combined, oddNumber){
        return combined + oddNumber + String.fromCharCode(10);
    }, "");

console.log(oddNumbersAsString);

Output:


C:\src\cfml\languageComparison\steppedLoop>node jsVersion.js
tahi
toru
rima
whitu
iwa


C:\src\cfml\languageComparison\steppedLoop>

PHP

PHP is a bit disappointing here for a coupla reasons. Firstly PHP is still at its roots still procedural, so the code is intrinsically more clunky, plus it's collection iteration methodsfunctions aren't implemented that thoughtfully, so I have to jump through some intermediary hoops:
$numbers = ["tahi", "rua", "toru", "wha", "rima", "ono", "whitu", "waru", "iwa", "tekau"];

$indexedNumbers = array_map(null, range(0, count($numbers)-1), $numbers);
$indexedOddNumbers = array_filter(
    $indexedNumbers,
    function($number){
        return ($number[0]+1) % 2;
    }
);
$reindexedIndexedOddNumbers = array_values($indexedOddNumbers);

$oddNumbers = array_map(
    function($number){
        return $number[1];
    },
    $reindexedIndexedOddNumbers
);

$oddNumbersAsString = array_reduce(
    $oddNumbers,
    function($combined, $oddNumber){
        return $combined . $oddNumber . PHP_EOL;
    },
    ""
);
echo $oddNumbersAsString;

The issues here are:

  • we want to filter on the array index position, but PHP doesn't think to pass this to the array_filter()'s callback, so I need to push an index into the data using array_map() and range(). One good thing here is that array_map(), if not given a callback then it just maps all the arrays it's passed together into sub arrays. Which suits my purposes here.
  • Then I can use array_filter() to do the actual filtering, but array_filter() is a bit sh!t for a second time because it leaves a sparse array after it filters. So after I filter I don't have a new array with elements 0-4, I have one with elements 0,2,4,6,8 (!!!!). How bloody stupid is that? I guess this is down to PHP's array implementation being quite rubbish and basically being "kinda an array, kinda a struct, kinda - as a result - neither".
  • I need to fix this by creating a new array with just the values.
  • As I said PHP is procedural, so I can't chain stuff. I could nest it, but that makes for awful, inside-out code. So I use individual statements and intermediary variables.
  • Having filtered I still need to get my data elements back to "normal" by getting rid of the index I embedded in them. Another call to array_map() to undo what I did before.
  • And finally I can reduce things back down to a string.
Way too much horsing around there. Note: I know this is - as a result - a contrived example. There's no way a sane person would do this sort of thing. In PHP, one's better off eschewing the array iteration methods and just using general forEach() loops. Old school, but they work.

Tuesday 12 January 2016

PHP & CFML: xpath with empty name spaces

G'day:
My mate who sits next to me at work, Amar, was trying to extract some info from an XML document, and we stumbled over the xpath syntax when there was a namespace defined, but no prefix was given. I'm completely unused to using xpath in PHP (I've had to query something once, I think), but had done a fair bit back in my CFML days.

Here's the XML in question (well: it's not the same XML, but it's equivalent):

<Response xmlns="http://example.com/ns/">
   <user>
      <dateOfBirth>1947-01-08</dateOfBirth>
      <firstName>Ziggy</firstName>
      <lastName>Stardust</lastName>
      <gender>?</gender>
   </user>
</Response>

See we've got a namespace declaration but no bloody prefix defined. Grumble.

On CF9 namespaces could be kinda ignored: just not specifying the namespace at all:

raw  = '<Response xmlns="http://example.com/ns/">
   <user>
      <dateOfBirth>1947-01-08</dateOfBirth>
      <email>sailor@example.com</email>
      <firstName>Ziggy</firstName>
      <lastName>Stardust</lastName>
      <gender>?</gender>
   </user>
</Response>';
xml = xmlParse(raw);

usingEmptyNamespace = xmlSearch(xml, "/:Response/:user/:lastName");
writeDump(usingEmptyNamespace);


Via cflive.net this yields:


Cool. However at some point - it might have been CF10, but I don't know - taking this approach stopped working because ColdFusion changed its XML parsing engine and apparently empty namespaces like that aren't legal.

The solution I had discovered (via googling and Stack Overflow) was to use the local-name() xpath function:

usingLocalName = xml.search("/*[local-name()='Response']/*[local-name()='user']/*[local-name()='firstName']");
writeDump(usingLocalName);

And this yields (I'm using ColdFusion 2016's CLI now), hence the format change:

>cf xpath.cfm
array

1) [xml element]
        XmlName:        firstName
        XmlNsPrefix:
        XmlNsURI:       http://example.com/ns/
        XmlText:        Ziggy
        XmlComment:
        XmlAttributes:  [struct]
        XmlChildren:

>

Now I switch to PHP, and have to make this lot work. Firstly the empty path version simply didn't work:

<?php

$raw = '<Response xmlns="http://example.com/ns/">
   <user>
      <dateOfBirth>1947-01-08</dateOfBirth>
      <firstName>Ziggy</firstName>
      <lastName>Stardust</lastName>
      <gender>?</gender>
   </user>
</Response>
';

$xml = new SimpleXMLElement($raw);

$usingEmptyNamespace = $xml->xpath("/:Response/:user/:gender");
var_dump($usingEmptyNamespace);


>php xpath.php
PHP Warning:  SimpleXMLElement::xpath(): Invalid expression in xpath.php on line 15

Warning: SimpleXMLElement::xpath(): Invalid expression in xpath.php on line 15
bool(false)

>

Using the local-name() approach worked fine in PHP:

$usingLocalName = $xml->xpath("/*[local-name()='Response']/*[local-name()='user']/*[local-name()='firstName']");
var_dump($usingLocalName);



>php xpath.php
array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (1) {
    [0]=>
    string(5) "Ziggy"
  }
}

>

However I was certain PHP would do things better than that, and doing some reading, I see they have a way of resolving the lack of defined namespace, using the registerXPathNamespace() method:

$xml->registerXPathNamespace('db', 'http://example.com/ns/');
$usingRegisteredNamespace = $xml->xpath("/db:Response/db:user/db:lastName");
var_dump($usingRegisteredNamespace);

This allows me to specify a prefix to make the xpath string legit. Nice one!

>php xpath.php
array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (1) {
    [0]=>
    string(8) "Stardust"
  }
}

>

One last thing I tried on a whim in CFML which worked was that it seems one can specify a wildcard namespace:

usingWildcardNamespace = xml.search("/*:Response/*:user/*:firstName");
writeDump(usingWildcardNamespace);


>cf xpath.cfm
array

1) [xml element]
        XmlName:        firstName
        XmlNsPrefix:
        XmlNsURI:       http://example.com/ns/
        XmlText:        Ziggy
        XmlComment:
        XmlAttributes:  [struct]
        XmlChildren:

>

This did not work on PHP. I dunno enough about XML engines and parsing and searching to make a comment on the whys and wherefores of what one's expectations ought to be when it comes to this sort of stuff, but it's good to know about registerXPathNamespace(), and also good to know about the wildcard stuff with CFML.

Not very incisive or groundbreaking stuff, but it's just what I had to work out today.

Righto.

--
Adam

Saturday 9 January 2016

Reflection on CFML tags and functions

G'day:
And I don't mean that in the philosophical or introspective sense. I mean actual reflection: being able to programmatically address the CFML language itself.

This line of thinking has come from the ColdFusion 2016 pre-release programme, and as such I cannot link to or discuss what's being said there. I do think there are some public links to bits and pieces of the topic somewhere, but I'm buggered if I can find them at the moment. Anyway: everything I say here is my own "work", and is original to this article, so I feel fine opening it for public discussion.

CFML has a function getFunctionList()which returns a liststruct with a key for each function name in CFML. I'm not sure who thought that was a clever implementation, but they should perhaps not be able to make any unsupervised decisions in future.

If returns this sort of thing:


Mind blowing.

Oddly, there's also a way of accessing information about those awful "service" CFCs ColdFusion has shipped with for a while (you know: Query.cfc, Http.cfc etc, in the customtags/com/adobe/coldfusion directory). For that, one looks directly in the server scope:


And as they're just CFCs, one can also get - slightly more useful - info about them with getMetadata():


For CFML tags, one needs to look in cfusion/wwwroot/WEB-INF/cftags/META-INF/taglib.cftld (which despite its name is XML):

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

Tuesday 1 December 2015

ColdFusion: I learn something about query of query

G'day:
Just a quick one (I'm supposed to be doing Clojure this morning, not CFML). Here's somethng I did not know about QoQ in CFML. Well: in ColdFusion's implementation of QoQ. It's LIKE statement supports (very limited) regex patterns in its value.

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"]
]);

coloursWithOorU = queryExecute(
    "SELECT * FROM colours WHERE mi LIKE :pattern",
    {pattern={value="%[ou]%"}},
    {dbtype="query"}
);

writeDump(var=coloursWithOorU, format="text", metainfo=false);

And the result:

query

 
[Record # 1] 
en: red 
id: 1 
mi: whero
 
[Record # 2] 
en: yellow 
id: 3 
mi: kowhai
 
[Record # 3] 
en: blue 
id: 5 
mi: kikorangi
 
[Record # 4] 
en: indigo 
id: 6 
mi: poropango
 
[Record # 5] 
en: violet 
id: 10 
mi: papura

Cool. Note this does not work on Lucee.

I dunno what the grammar of the patterns are, but it's not simply standard CFML regex patterning. For example Initially I tried to have a pattern which would match words of six letters or more (ie: .{6}), but that didn't work. I was gonna say "it'd be really grand if Adobe actually documented this stuff", but actually they have! It's right there on the "Query of Queries user guide" page. OK, so the grammar is very limited. Just to what I've shown, basically: single character classes. It doesn't even support repetition modifiers. So it's a bit disappointing that the grammar is so limited, but it's handy nevertheless.

Thanks to Tim Brenner on the CFML Slack Channel for bringing this to my attention!

--
Adam

Sunday 1 November 2015

1000

G'day:
Blimey... the CFML Slack channel now has 1000 members! That's not bad going for an initiative only started a few months ago.

I doubt anyone from the CFML community reading this blog hasn't already signed up, but if not: join in:




Or use the form here.

--
Adam

Thursday 29 October 2015

CFML community working well together

G'day:
I've not much to say here, but I'm just really pleased how the CFML community - especially Dom Watson and Sean Corfield - have been chipping away and planning out and discussing a ColdFusion enhancement request Alex Skinner raised last night (Allow additional attributes to be added to CFPARAM tags).

I put my usual cynical / jaded oar in (that said, I stand by what I say!), but Dom and Alex have been having a lot of discussion today (not just on the bug tracker, but on the CFML Slack channel too), and it's great work.

Well done, community.

--
Adam


Friday 23 October 2015

ColdFusion 2016: it goes to show you should raise feature requests

G'day:
I'm still under NDA with the ColdFusion 2016 Prerelease Programme, so I need to be circumspect about what I say about CF2016, but once someone from Adobe has talked about something in public, I reckon I am allowed to at least refer back to that, and draw on public information to write some stuff.

Adobe showcased some ColdFusion 2016 features at CFCamp yesterday. It sounds like it was a good presentation, and was more dev-centric than has been the case in the past. Nice one, Rakshith.

A while back I raised this ticket on the bugbase: "?.: safe navigation operator". I gleaned this idea from trawling around other languages to see what they have on offer which might complement CFML. I wrote the results of this in my article "Thinking about operators in CFML".

Well I'm quite pleased that the safe-navigation operator has made its way into ColdFusion 2016.

Yay! <- that's me being pleased. It doesn't happen often, so remember what it looks like for future ref.

So what's this "safe navigation" thing?

It's an operator which takes two operands, and follows the following rules:
  1. if the first operand is null, it returns null;
  2. if the first operand is not null, it applies the second operand to it using the . operator.
Wah?

OK, say we have this:

// safeNavigationExample.cfm
function getMaoriNumberById(id){
    var numbers = ["tahi", "rua", "toru", "wha"];
    if (id <= numbers.len()){
        return numbers[id];
    }
}

fiveInMaoriInUpperCase = getMaoriNumberById(5).ucase();

We would get this:

Value must be initialized before use.

Its possible that a method called on a Java object created by CreateObject returned null.
The error occurred in
safeNavigationExample.cfm: line 10
8 : }
9 : 
10 : fiveInMaoriInUpperCase = getMaoriNumberById(5).ucase();
11 : </cfscript>

To counter this, we'd need to write this lot:

fiveInMaori = getMaoriNumberById(5);
if (!isNull(fiveInMaori)){
fiveInMaoriInUpperCase = fiveInMaori.ucase();
}else{
    fiveInMaoriInUpperCase = javaCast("null", "");
}

That's quite a mouthful. And this is only with one chained operation. What if it was this:

result = new SomeObject().method1().method2().method3().method4();

And at any point in that, the result of one of the methods might be null? We'd need this:

someObject = SomeObject();
resultOfMethod1 = someObject.method1();
if (!isNull(resultOfMethod1)){
    resultOfMethod2 = resultOfMethod1.method2();
    if (!isNull(resultOfMethod2)){
        resultOfMethod3 = resultOfMethod2.method3();
        if (!isNull(resultOfMethod3)){
            result = resultOfMethod3.method4();
        }
    }
}
if (!structKeyExists(variables, "result")){
    result = javaCast("null", "");
}

Yikes.

So this is where the safe navigation operator comes in:

result = new SomeObject().method1()?.method2()?.method3()?.method4();

Much better.

Wednesday 14 October 2015

CFML: operator overloading

G'day:
A while back I posted my "What I'd like to see in ColdFusion 12 (redux, as is happens)" article, and I provided links to most of the tickets I raised for features to facilitate discussion and input from you lot.

One feature I mentioned in passing but forgot to add the link to was operator overloading. I did actually create the ticket, but I forgot to link to it. The ticket is 4013841.

Now I know a full-fledged operator overloading system would be quite an undertaking, and there's also been some industry push-back on the validity of having too comprehensive a system as it would compromise code clarity. EG: we know what 2 * 3 means, but what does Apple * Banana mean? Even if the object types of the operands could meaningfully be multiplied, it doesn't make for clear code.

However this does not mean there aren't really good situations in which this sort of control might be useful.

Here's some examples I provided with the ticket:

// Person.cfc
component {
    
    property dateOfBirth;

    function compare(p1, p2){
        return p1.dateOfBirth.compare(p2.dateOfBirth);
    }
}

Would allow:

adam = new Person(createDate(1970,2,17));
zachary = new Person(createDate(2011,3,24));
if (adam > zachary){
    writeOutput("Yay! Dad finally wins at something. Being old. Grumble.");
}

Or another example is best demonstrated by PHP's ArrayAccess interface, which allows the definition of a few methods (offsetExists(), offsetSet(), offsetGet(), offsetUnset()) which then allows one to treat an object like an array, by overloading the behaviour of the [] syntax (not a true operator in this case). C# also allows for this sort of thing too (then again, C# allows for almost full operator overloading, as far as I recollect).

Or not even really operator overloading, but just extending built-in CFML processing by permitting a class to implement a toString() method which is then used whenever an object reference is used where a String is expected, eg:

// Person.cfc
component {
    
    property dateOfBirth

    function toString(){
        return dateOfBirth.dateFormat("yyyy-mm-dd");
    }
}


writeOutput(zachary); // 2011-03-24

This is all useful, clear stuff. However Adobe have just fed-back with a fairly unthinking response:

Operatore overloading is known to have its own issues because of which it is not supported in many languages.

This is pretty much the boilerplate response when a naesayer encounters a discussion about operator overloading. It kinda suggests to me that not a great deal of thought went into the reaction, but rather "he said operator overloading: use that stock response".

I did go on to point this out:

Well: out of the top ten programming languages (http://spectrum.ieee.org/)

  • Java - no
  • C - no
  • C++ - yes
  • Python - yes
  • C# - yes
  • R - could not determine
  • PHP - limited
  • JavaScript - very limited
  • Ruby - yes
  • Matlab - yes


So more than 50% offer at least some operator overloading.

Another situation relevant language that supports this is Groovy (which is like what CFML could have become had it been stewarded properly).

[After some more investigation of languages that popped into my mind...] Rust does; Go doesn't. Swift does. Scala does. Erlang doesn't seem to (but could not find a definitive source), F# does. Clojure seems to allow it. Haskell seems to. Even blimin' Perl does!

[note: those are all the languages I checked. I did not cherry-pick which ones to check to fortify my position]

I think your observation is based on the position that Java took when it decided not to do operator overloading, which is rather out of date now. Things have moved on.

There are caveats to be considered when providing for operator overloading, but I don't think you are correct in writing them off as "known issues", and I think the knowledge you are basing this on is perhaps out of date, or just mired in "how Java decided to do things". CFML is a more dynamic and forward-thinking language than Java, so you should not let your Java background hold us back.
I think Adobe needs to spend some time thinking about what they can do to make the CFML language better - especially as they've spent so much time holding it back whilst other languages have passed it by. Using a stock answer of "operator overloading bad" it a bit leaden, IMO.

But maybe I should not have used the term "operator overloading" as this was only a part of what I was thinking of at the time. And, TBH, I kinda knew it would be a risky suggestion and likely to be dismissed in exactly the way it was.

What do you think? Ought I have taken a more piecemeal approach and suggested individual pieces of functionality which draw from this concept? I thought of that, but was slightly wary that the whole concept needs some thought, rather than just blting bits and pieces on. Dilemma.

Thoughts?

--
Adam