Showing posts with label Rob Glover. Show all posts
Showing posts with label Rob Glover. Show all posts

Thursday 17 October 2013

CFML: listEach() on CFLib

G'day:
This one cropped up because of something Rob Glover mentioned in passing on Twitter, which I gave a fairly perfunctory response to.

Scott more helpfully pointed out that converting the list to an array and looping that is a good approach. I agree.

However this got me thinking: a list-iterating UDF would be handy, and CFLib doesn't seem to have one, so I knocked one together:

public array function listEach(required string list, required function callback, string delimiters=","){
    var arr = listToArray(list, delimiters);
    var arrLen = arrayLen(arr);
    var result = [];
    for (var index=1; index <= arrLen; index++){
        arrayAppend(result, callBack(argumentCollection=arguments, index=index, element=arr[index], length=arrLen));
    }
    return result;
}

There's nothing exciting here, it just takes a list, loops over it, and passes each element to a callback to process it. It'll return an updated array if that's helpful. I chose an array instead of a list because it's more useful, and allows non-simple values to be returned.

This follows the precedent set in my recent arraySetEach() UDF, passing all the passed-in args to the callback, plus the position the iterating index is at, and the value, and in this case the length of the list being processed.

It can be used both to create a new, transformed list:

numbers = "tahi|rua|toru|wha";

function _ucase(){
    return ucase(element);
}

upperCasedNumbers = listEach(numbers, _ucase, "|");
writeOutput(serializeJson(upperCasedNumbers));

Output:

["TAHI","RUA","TORU","WHA"]

Or just as a looping construct. Here we turn the list into an <ol>:

function toOL(){
    if (index == 1){
        writeOutput("<ol>");
    }
    writeOutput('<li id="index_#index#">#element#</li>');
    if (index == length){
        writeOutput("</ol>");
    }
}


listEach(numbers, toOL, "|");

Which outputs mark-up:

<ol>
    <li id="index_1">tahi</li>
    <li id="index_2">rua</li>
    <li id="index_3">toru</li>
    <li id="index_4">wha</li>
</ol>

So that was two examples: using it to create an updated data structure, or simply as a looping construct. Actually the toOL() UDF would probably be better done in tags, given we're outputting mark-up:

<cffunction name="toOL">
    <cfif index EQ 1>
        <ul>
    </cfif>
    <cfoutput><li id="index_#index#">#element#</li></cfoutput>
    <cfif index EQ length>
        </ul>
    </cfif>
</cffunction>

For the UDF I'll submit to CFLib (as above) the code is ColdFusion 10 / Railo 4.x only, but Rob was using CF8. However with a few cosmetic changes, this all works fine on CF8 too:
function listEach(list, callback, delimiters){
    var arr        = false;
    var arrLen    = false;
    var result    = [];
    var index    = 0; 

    if (!structKeyExists(arguments, "delimiters")){
        delimiters = ",";
    }

    arr = listToArray(list, delimiters);
    arrLen = arrayLen(arr);

    for (index=1; index <= arrLen; index++){
        arrayAppend(result, callBack(argumentCollection=arguments, index=index, element=arr[index], length=arrLen));
    }
    return result;
}

I just needed to change the function definition slightly, do my VARs at the top, and scope my arguments specifically in some places in the calling code:

function toOL(){
    if (arguments.index == 1){
        writeOutput("<ol>");
    }
    writeOutput('<li id="index_#arguments.index#">#arguments.element#</li>');
    if (arguments.index == arguments.length){
        writeOutput("</ol>");
    }
}

And that was it.

It's important to remember that people get confused when talking about closures and callbacks, and often use the word interchangably. And closure is only a facet of function expressions, and only available from CF10 / Railo 4.x. However being able to use callbacks has existed in CFML since CF5, and before Railo even existed.

None of this is terribly exciting, but maybe someone will find it helpful. Shrug.

Righto.

--
Adam

Wednesday 14 August 2013

Moan moan moan bloody moan

G'day:
Another point for Rob Glover... he reminded me about one of my pet hates yesterday... CFML code in which the dev has closed the tags. I wrote an article about closing CFML tags ages ago. But what else annoys me in my CFML world?

Here's a list of CFML- or dev-oriented pete peeves of mine. In no particular order. And why? Just for the hell of it, on a slow news day.

The idea that pointless closing  CFML tags is "a thing"

'nuff said.

Comments that simply state what the next line of code is doing

eg:

// create the object
theObject = createObject("TheComponent");

Seriously? F*** off if you do that. If a person is looking at that file, they understand CFML (or at least they should, and comments are not the way to solve that if it's not the case!), and reading the code is a fine way of identifying what the code does.

This is made worse by poorly maintained code in which the comments reflect the state of the code as it was a year earlier, but not how it is now.

Use comments to explain what's not already on the screen, and not readily inferrable from what's on the screen: why you're doing something, how an algorithm works, or an explanation of why unlikely-looking code needs to be the way it is (eg: some third-party service wanting dates as strings, or something).

Copying and pasting of code instead of refactoring

If you want the same functionality somewhere else in your code base: refactor, don't copy and paste (or as I term it in this context: "cu*t and paste". Because if you do it: you are one). I don't know how many times I've needed to fix something only to find out that the same bit of logic exists in several places in the code base.

Using tags in logic-only files

Tags are for interacting with mark-up. They are clumsy and clunky-looking when used in logic-only files. The only time they should be used in these cases is when Adobe have dropped the ball and there is no script-compatible implementation of the given functionality.

Not RTFMing (or should that be "RingTFM"?)

Asking questions (especially in a written forum) before even bothering to just look up the answer in the docs. Or via Google. It's just laziness, and people like that do not deserve the help they are soliciting.

CFML's "Wizard" tags

Like anything to do with the UI (<cfform>, <cfpod> etc), and including <cfinsert> and <cfupdate>. Have some pride: do your work properly.

Adobe's attitude to their clients

Their support of and participation in their developer community is appalling.

Not understanding when to use #

Thanks to David Nash for this one. Its not complicated (like when to use an apostrophe), yet so many people simply do'nt bother to learn the languages' syntax properly. NB: sic.

Not having a basic understanding of computing

Not understanding why floating point numbers are inaccurate, why data types have size limitations; or how Javascript runs on a client machine and ColdFusion runs on a server and the two don't interact directly, etc. I don't expect people to be able to write a C compiler (cue Sean to say something along the lines that he has ;-), or know the intricacies of the architecture of a CPU, but an understanding of the basics of the tool set one uses daily for one's career.

Being partisan about technology

Because I use Railo doesn't mean ColdFusion is automatically the villain (it's not for those reasons), or if I like CFML then PHP automatically sux, etc. Or I like Apple products therefore Microsoft products are automatically bad, etc. I know we work in IT, but not everything is binary. And you sound stupid if you hold opinions like that.

That's a random ten. There'll be others, and things that annoy me more, but happen less frequently so have not sprung to mind in the last 15min since I decided to write this.

I'm sure you'll disagree with at least some of these, or have some other ones of your own. Go on then...

--
Adam

Tuesday 30 July 2013

CFML: Reload Coldspring on each request when in a dev environment

G'day:
This is based on another question Rob asked about Coldspring a few days back:

Is there a way to make #Coldspring reload the CFCs on each requestion, for dev purposes? #coldfusion

I've had the same question myself, but never had to act on it so never bothered to check.

At the time I responded that I was pretty sure there was no "mode" in Coldspring to get it to change its load behaviour, and indeed one would usually only load beans when the application starts anyhow, so by the time a subsequent request comes around, everything's loaded.

Some people have code in their onRequestStart() which looks for a "reload" (or similar) URL param, and does an app-restart if it's found. This is an OK approach, but for some reason it's never sat that well with me. My feeling is that one's environment doesn't change from request to request, so it shouldn't be a request-centric process. It also leaves functionality intended for the dev environment accessible in the live environment, which isn't ideal IMO. It's not a major concern and is easily-enough worked around, but it got me thinking about alternative approaches. Not necessarily better ones, just an alternative.

Here's an approach which is based on the environment the code is running in, with the environment settings being dictated by an environment-specific config file.

In source control there's a bunch of environment config files, eg:

/config/
    environment-config-DEV.json
    environment-config-LIVE.json

Update:
As per a couple of the comments, I had been slightly too abstract for my own good in the code example, so I have modified it a bit. The intent was not to suggest one would always want to run Coldspring in a dev environment, just that if one wanted to whilst one was working on something that needed reloading every request, then it can be achieved using an environment-specific configuration. Hopefully my tweaking of the code has made this more clear. Or less unclear.

And when deploying, the deployment script takes the file appropriate for the deployment environment and deploys it as environment-config.json. In this case, a very pared down config file might be:

{
    "coldspringLoadMode"    : "REQUEST"
}


From there, the Application.cfc file reads environment-config.json (which is now environment-specific), and acts accordingly. EG:

// Application.cfc
component {

    this.name = "coldspringTest01";
    this.mappings = {
        "/coldspring" = expandPath("../coldspring"),
        "/api" = expandPath("../api")
    };

    public void function onApplicationStart(){

        application.environmentConfig = deserializeJson(fileread(expandPath("../conf/environment-config.json")));

        if (application.environmentConfig.coldspringLoadMode == "APPLICATION"){
            initColdspring();
        }

        writeOutput("Application initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
    }

    public void function onRequestStart(){
        if (application.environmentConfig.coldspringLoadMode == "REQUEST"){
            initColdspring();
        }
    }


    private void function initColdspring(){
        var properties = initColdspringProperties();

        application.beanFactory = createObject("coldspring.beans.DefaultXmlBeanFactory").init(defaultProperties=properties);
        application.beanFactory.loadBeansFromXmlFile("../conf/beans.xml", true);
        writeOutput("Coldspring initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
    }

    private struct function initColdspringProperties(){
        return {timestamp=now()};

    }

}

So Application.cfc doesn't need to have logic to decide whether it's in LIVE or DEV mode, it just loads the config file, and then uses the environment-specific settings.

From a Coldspring perspective, we then have conditional logic as to where we do its initialisation. To facilitate this we factor out the Coldspring initialisation into its own method, and then conditionally call that method onApplicationStart() (when "live"),or onRequestStart() (if "dev").

NB: the Coldspring / bean config itself is the same as in the article I knocked-out this morning.

As a test rig, I have this code:

// test.cfm
writeOutput("test.cfm initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
o1 = application.beanFactory.getBean("C");
writeOutput("o1.timestamp = #timeFormat(o1.timestamp, "HH:MM:SS.LLL")#<br>");

Which just demonstrates when o1 was initialised. On dev, the output from the first two requests look like this:

First request:
Application initialised @ 13:39:33.257
Coldspring initialised @ 13:39:33.275
test.cfm initialised @ 13:39:33.275
o1.timestamp = 13:39:33.258

Second request:
Coldspring initialised @ 13:40:31.352
test.cfm initialised @ 13:40:31.353
o1.timestamp = 13:40:31.345

So Coldspring is being init-ed  each request, and the C instance that gets assigned to o1 is - accordingly - too.

In the live environment, Coldspring is only loaded when the application starts, so o1's C instance is only created once, too:

First request:
Coldspring initialised @ 13:42:41.695
Application initialised @ 13:42:41.695
test.cfm initialised @ 13:42:41.696
o1.timestamp = 13:42:41.686

Second request:
test.cfm initialised @ 13:43:05.785
o1.timestamp = 13:42:41.686

That's it, really. I'm not saying this is a better approach to having URL-based stuff, but it's a case of different horses for different courses. Each approach has its pros and cons. And, indeed, both can be used in conjunction with each other.

I've got a meeting to go to...

--
Adam

Installing Coldspring 1.2

G'day:
This article is partly a piss-take, and partly actual guidance. Mostly the latter. Thanks to Rob Glover (again!) for the inspiration for the article (both the piss-take and the actual meat of the article).

Whilst on the train this morning, I read this Twitter update from Rob:

Amazing. NOWHERE can I find INSTALLATION INSTRUCTIONS for #coldspring. Just lots on how it works. #coldfusion
That is quite amazing, but not in the way Rob means. Because a quick google of "coldspring install" returns a bunch of results, and just browsing through the first few:

  1. ColdSpring - Home
    Well... yeah, OK: this is a fail. I've done superficial clicking around, and didn't see any installation instructions within the first ten clicks of hitting the home page.
  2. [PDF] Reference Guide - ColdSpring
    The third item is the installation instructions.
  3. How to install the ColdSpring framework - Hosting Forum - Hostek ...
    The raison d'être of the document is that its instructions of how to install Coldspring.
  4. Mach II and Coldspring for n00bs | Ollie Jones, software hints and tips
    Includes instructions for installing Coldspring
  5. ColdSpring - Railo
    Is not actually about installing Coldspring.
  6. A Beginner's Guide to the ColdSpring Framework for ColdFusion ...
    Unfortunately - given the title - doesn't actually include how to install it.
From here, the docs are referring to Coldspring installation / config issues, or tangential issues people have had, so not really relevant. Still: in the first six Google results, five are about Coldspring installation, and three of them have instructions.

So what's amazing about this is that Rob couldn't find any of this stuff.



But just for the sake of completeness, here are some instructions for installing Coldspring.

Update:
As Mark Mandel has pointed out to me, I only cover 1.2 here, and there is also 2.0 in the works (the status of it is TBC... I don't actually know). I'll find out if 2.0 works differently, and write a companion article if required.
  1. Download Coldspring from the Coldspring website. At time of writing, the current stable version proclaims to be 1.2, and there's an alpha of 2.0 available too. There's a download link for 1.2 (zip) on the homepage.
  2. Unzip the file into a non web browsable directory. Do not put it in your web root (this conflicts with most of the instructions you will see). After unzipping, you should have this sort of directory structure:

    /[parent directory]/
        /[coldspring directory]/ (can be called anything, but "coldspring" makes sense)
            /beans/
                    DefaultXmlBeanFactory.cfc
    
    There will be a lot of other files in there, but that gives you an idea of what the directory structure is like.
  3. In you application's Application.cfc, create a ColdFusion mapping to the [coldspring directory], eg:
    this.mappings = {
        "/coldspring" = expandPath("../coldspring")
    };
    In this case, my web root is adjacent to the coldspring dir, eg:
    /[parent directory]/
        /coldspring/
        /webroot/
            Application.cfc
    
  4. as an alternative to creating the mapping in Application.cfc, one could instead create it in CFAdmin, but I think doing it in the application is preferable.
That is it as far as installation goes.

Note that a lot Coldspring installation instructions will by default suggest just dumping the Coldspring directory in the web root, because this is "easier" because it doesn't require the mapping. This is really poor advice for a few reasons:
  • It's not exactly difficult to create the mapping.
  • The Coldspring files are not intended to be web browsable, so why would the instructions suggest putting them in the web root? That's just wrong.
  • Having files not intended to be web browsed in a web-browsable directory is a potential vector for site hacking. 
In fact most framework installation instructions prefer the lazy / wrong / insecure installation approach to the equally-easy / good / secure approach. So when you read framework installation instructions which advocate putting the framework in the web root as a default practice, screw your nose up in disgust, and don't do that. Generally a framework will be happy to be installed anywhere, and all it needs is a CF mapping with a known name (eg: /coldspring in this case).



So the install for Coldspring is easy. Here's a sample bare-bones application building on that, showing some of the config.

// Application.cfc
component {

    this.name = "coldspringTest12";
    this.mappings = {
        "/coldspring" = expandPath("../coldspring"),
        "/api" = expandPath("../api")
    };

    public void function onApplicationStart(){
        var timestamp = now();

        writeOutput("application initialised @ #timeFormat(timestamp, "HH:MM:SS.LLL")#<br>");

        application.beanFactory = createObject("coldspring.beans.DefaultXmlBeanFactory").init(defaultProperties = {timestamp=timestamp});
        application.beanFactory.loadBeansFromXmlFile("../conf/beans.xml", true);
    }

}

Here we have our /coldpsring mapping, and the bootstrap code to get Coldspring loaded, and my beans loaded too.


I'm passing a bean config parameter too.

Here's my bean definition XML file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="C" class="api.C">
        <constructor-arg name="timestamp">
            <value>${TIMESTAMP}</value>
        </constructor-arg>
    </bean>
</beans>

Note that the /config directory is also not in the web root. It's adjacent to the /coldspring and /webroot and (see below) /api directories.

And I obviously have this C.cfc file too:

// C.cfc
component {

    public C function init(required date timestamp){
        this.timestamp = arguments.timestamp;

        writeOutput("C initialised @ #timeFormat(timestamp, "HH:MM:SS.LLL")#<br>");

        return this;
    }

}

Finally, I have a file which uses Coldspring to fetch my bean:

// test.cfm
writeOutput("test.cfm initialised @ #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
o1 = application.beanFactory.getBean("C");
writeOutput("o1.timestamp = #timeFormat(o1.timestamp, "HH:MM:SS.LLL")#<br>");
sleep(5000);

writeOutput("current time #timeFormat(now(), "HH:MM:SS.LLL")#<br>");
o2 = application.beanFactory.getBean("C");
writeOutput("o2.timestamp = #timeFormat(o2.timestamp, "HH:MM:SS.LLL")#<br>");

And the output from this is as follows:

application initialised @ 08:38:21.029
test.cfm initialised @ 08:38:21.053
o1.timestamp = 08:38:21.029
current time 08:38:26.066
o2.timestamp = 08:38:21.029

What the timestamps show is that the C bean is created once, during application start-up, and then calls to getBean() simply return that bean. Note that I pause for 5sec in test.cfm, but the timestamp o2 has is the original timestamp from app start-up, not the current timestamp from when o2 was created.


There's obviously a bunch more config options for Coldspring, but this is not an attempt to replicate the docs, it's just a top level summary of how to install it, and a bare bones example app showing the minimum config.

Hopefully this will help people trying to find Coldspring installation docs online.

I've got another article to come on Coldspring: also based on a question Rob asked the other day. Stay-tuned.

--
Adam

Tuesday 23 July 2013

ColdFusion: I've come to a conclusion

G'day:
This is something that I've been mulling over / worrying about / contemplating for a coupla months now. I find myself poised over the keyboard frequently about to write this increasingly often. And Rob Glover is to thank for making the comment on Twitter that made me think "actually, yeah..."

This is my conclusion. I now believe Adobe is doing more harm to ColdFusion and CFML in general than they are good. And I think it's time - for the benefit of CFML as a language - for them to pull out. There's a few reasons for this.

Firstly, whilst they are "mothership", the language is just not going to go anywhere. As far as I can tell, no-one involved in the Adobe ColdFusion Team actually uses CFML as a matter of course, and I think the entire business unit is completely detached from the community. Worse, what they are doing on a day-to-day basis is Java. Whilst Java is a great language, it's not even remotely the same in intent as CFML is, and - worse - Java is a behemothemic (I made that word up) old juggernaut which works in a completely different way than the spritely languages that compete in the space CFML is struggling to be relevant in. Adobe don't (seem to ~) know how CFML is used, what the target market is, and what they should do with it to keep it relevant.

Secondly... $$$. I guess this makes sense. Adobe are not in the business of making a great language, they're in the business of making money for their shareholders. To do this, they need revenue, and to have revenue, they need to sell licences. Because they're in the software selling business. Unfortunately the people they market CF / CFML to are the people who pay for the licences, and all they need to do is to generate enough smoke, and angle the mirrors in the right way, and the licence-purchasing decision makers will re-up their licences. Especially if - and I think this is where the bulk of Adobe's ColdFusion revenue comes from - the person signing the cheques (dear god, I actually typed in "checks" then, initally) is doing so on behalf of some US government agency, and there's very little oversight on the sort of money they're paying; and what's more important to them is continuity, not innovation.

Thirdly. And this is how I came to this conclusion, remember, so this is the third reason that influenced me to think the way I do: Railo. I've been using Railo since it came on the scene, but only in the capacity of reading the mailing list and testing stuff out. I have never had any of my apps running in production on Railo. This is not slight on Railo, it's just that I have always worked for ColdFusion shops; licences already purchased. But over that time I've seen Railo arrive as the third possibly-viable CFML solution (after ColdFusion and BlueDragon... the New Atlanta one, not the Alan Williamson one, which came along around about the same time as Railo?), and then surpass BD (closed or open) becoming the second player in the CFML market, to now pretty much being the leader in the CFML community. I don't mean in revenue, or in "stature", but in importance. Basically I've gone from not giving a shit what Railo might have to say about CFML, to not giving a shit what Adobe might have to say about it (I never gave a shit about BD, for varying reasons). This is partly because Adobe hardly ever say or do anything sensible / useful about CFML other than when there's a release cycle on, and more recently because what Railo is saying about CFML and the CFML community just makes sense. Whereas everything out of the Adobe stable these days is going more and more heavily on the marketing smoke and mirrors, and is almost entirely without substance.

Fourthly I've been looking at other languages recently. I'm a slack-arse when it comes to forwarding my career prospects: I'm good at CFML and that finds me work. Well has done so far. I'm lucky in that regard (although I will never become a rich person with my work ethic, I at least have a place to hang-out and write code every day ;-). So I've pretty much focused on CFML and enough JS to get by over the last decade. However now that I'm looking, what I do see out there is every other relevant language just does stuff better than CFML does these days. It's a complete frickin' myth that CFML is still this quickest-to-market / rapid-development language. It might be so in the context of "my simple website", like what CFML used to excel at ten years ago, but in the context of doing more than "making it easy to generate HTML", even my superficial knowledge of competing languages reveals that CFML is basically irrelevant in that space now. Now I'm not saying some stuff on a line-of-code-by-line-of-code basis is simply easier to do in CFML, but I really think ColdFusion is lacking the bigger picture here. Plus it just moves too damned slowly to keep up. THat and when they do decide to do new stuff, it's crap like <cflclient> (ColdFusion 11's apparent marquee feature) which seems to be a wizard for writing JavaScript for mobile apps. In CFML. What? Are you lunatics, Adobe?

Update: <cfclient>? Wah?
Someone asked what I'm on about with this mention of <cfclient>. It was mentioned on a slide in a presentation I saw at cf.Objective() of new features coming to ColdFusion 11. There was also discussion of the CF -> Mobile functionality in the same presentation, and again at Scotch on the Rocks. I do not claim any specific knowledge about the functionality other than what's in the public domain via those sources.

The shining light here for CFML though, is Railo. They listen to their developers / their community. They do Railo (and accordingly CFML ~) consultancy, so they know what people on the ground are actually doing, and they enhance then language in ways that will benefit professional CFML developers. They seem to look at what other languages are doing, and consider those features for Railo / Railo's CFML. They keep the language alive and moving. They seem to give a shit about CFML (the Adobe ColdFusion Team seem to be interested in CFML because it pays their salary, and not really beyond that. Well that's my impression).

So here's what I think. Adobe? Give up on ColdFusion and CFML. You're not helping.

And Railo? Screw what Adobe might have done / might be doing with CFML. Take the language as your own (that sounds more melodramatic than I intend it to, but you get the idea).

What do you think?

--
Adam