G'day:
I had had this article in draft for over a year, but never got around to writing it. Then Matt Bourke offered an idle challenge to me to write four blog articles before year-end (it's not 8pm in the UK yet, so plenty of time), and I found myself trawling through my draft-backlog for inspiration. I came across this article (well: an earlier version of it), but decided "nah, screw that... I missed the boat on that one, Railo's already done it".
But now I read on Twitter that Steven Neiland is suggesting an enhancement to it... and it's something I had a discussion about the last time I floated this idea for CFML (back in 2010: "<cfretry> / retry")...
Tuesday 31 December 2013
3/4: The dumb things ColdFusion does
G'day:
Admission:
This is the article I alluded to a coupla weeks ago when I said this:
OOOPS. Got about 30sec away from pressing "publish" on a blog article... only to realise I was wrong in what I was saying. Dammit.
— Adam Cameron (@dacCfml) December 13, 2013
I promised Gavin I'd still release the article anyhow, so here it is. I have made it clear where I go wrong, so people don't get the idea I'm actually right in what I say here.
- Adam 31/12/2013
(2/4) ColdFusion: var
hoisting can make a difference
G'day:
Remember a while back I observed that - like JavaScript - ColdFusion hoists its VAR declarations: "ColdFusion hoists VAR declarations". At the time I could not see how this could cause problems in CFML. Well I've noted a case where it does cause problems, in that it reveals a bug in ColdFusion 9 & 10.
I came across this ticket on the bug tracker: "cfloop array and wrong iterator", which details how ColdFusion 9 (and, as it turns out: 10) gets confused whilst array-looping in an edge-case situation. Check this out:
Remember a while back I observed that - like JavaScript - ColdFusion hoists its VAR declarations: "ColdFusion hoists VAR declarations". At the time I could not see how this could cause problems in CFML. Well I've noted a case where it does cause problems, in that it reveals a bug in ColdFusion 9 & 10.
I came across this ticket on the bug tracker: "cfloop array and wrong iterator", which details how ColdFusion 9 (and, as it turns out: 10) gets confused whilst array-looping in an edge-case situation. Check this out:
(1/4): it's not all about ColdFusion 10 bugs. CF 9.x has outstanding bugs too...
G'day:
First the "1/4" thing. Matt Bourke has just challenged me to release another four blog articles today. Let's see if I can do it. I make no apologies for the quality (or length) of the writing today. I'm not in the pub for one thing ;-)
This one is really short, but was underway before Matt challenged me.
First the "1/4" thing. Matt Bourke has just challenged me to release another four blog articles today. Let's see if I can do it. I make no apologies for the quality (or length) of the writing today. I'm not in the pub for one thing ;-)
This one is really short, but was underway before Matt challenged me.
Monday 30 December 2013
Unit Testing / TDD: not testing stuff
G'day:
It's about bloody time I got back to this series on TDD and unit testing. I've already got the next few articles semi-planned in my head: topic, if not content. I have to say I am not in the most inspired writing mood today, and the words aren't exactly flowing from fingers through keyboard to screen - this is the third attempt at this paragraph - but we'll see how I go.
To find inspiration and to free up the fingers a bit, I'm at my local pub in Merlin Park in Galway, which - today - has shown its true colours as a football pub (to you Americans, that's "soccer". To me, it's "shite"). I've tried to like football, but it all just seems too effeminate to me. The place is chock-full of colours-wearing lads yelling at the screen. Either for or against one of Chelsea or Liverpool (the former lead 2-1 at half time). It's not conducive to writing, but the Guinness next to me will be.
I'll not list the previous articles in the series as it will take up too much room. They're all tagged with "TDD" and "unit testing" though.
On with the show...
It's about bloody time I got back to this series on TDD and unit testing. I've already got the next few articles semi-planned in my head: topic, if not content. I have to say I am not in the most inspired writing mood today, and the words aren't exactly flowing from fingers through keyboard to screen - this is the third attempt at this paragraph - but we'll see how I go.
To find inspiration and to free up the fingers a bit, I'm at my local pub in Merlin Park in Galway, which - today - has shown its true colours as a football pub (to you Americans, that's "soccer". To me, it's "shite"). I've tried to like football, but it all just seems too effeminate to me. The place is chock-full of colours-wearing lads yelling at the screen. Either for or against one of Chelsea or Liverpool (the former lead 2-1 at half time). It's not conducive to writing, but the Guinness next to me will be.
I'll not list the previous articles in the series as it will take up too much room. They're all tagged with "TDD" and "unit testing" though.
On with the show...
Friday 27 December 2013
Glitch in ColdFusion's CFML parser
G'day:
This is not what I was meaning to write about today, but hey.
I'm on the second leg of my trip to Galway: Central Line › Piccadilly Line (which today is taking on some of the District Line stations too, just - I think - to make my journey even more shit) › sit around at LHR drinking Guinness (or waiting in the security queue, depending on how busy it is... it'll be busy, I reckon) › fly to Shannon › bus to Galway › another bus to the bit of Galway I actually want to go to. This'll all take about 7h. Bleah. I'm catching a bus today because the rental car rates were insane this weekend. Instead of the usual £50 for two days, Hertz wanted close to £300 for three days. I cannot afford that. So I'm busing (€20) to Galway, and catching a cab (€100) back on Mon. Anyway, the point being I've got a lot of time sitting around, so I'm "investigating".
Right... I don't want this to become to blogs what CFHour is to podcasts (50% waffle before it gets going), so... erm... CFML stuff...
This is not what I was meaning to write about today, but hey.
I'm on the second leg of my trip to Galway: Central Line › Piccadilly Line (which today is taking on some of the District Line stations too, just - I think - to make my journey even more shit) › sit around at LHR drinking Guinness (or waiting in the security queue, depending on how busy it is... it'll be busy, I reckon) › fly to Shannon › bus to Galway › another bus to the bit of Galway I actually want to go to. This'll all take about 7h. Bleah. I'm catching a bus today because the rental car rates were insane this weekend. Instead of the usual £50 for two days, Hertz wanted close to £300 for three days. I cannot afford that. So I'm busing (€20) to Galway, and catching a cab (€100) back on Mon. Anyway, the point being I've got a lot of time sitting around, so I'm "investigating".
Right... I don't want this to become to blogs what CFHour is to podcasts (50% waffle before it gets going), so... erm... CFML stuff...
Thursday 26 December 2013
PDFs and Japanese and weirdness and bugs
G'day:
Here's one I was looking at on Stack Overflow today. It involves generating a PDF with Japanese text in it. I did not solve the issue. This doesn't surprise me too much, because I have never once generated a PDF in CFML in a production environment. I've just never had the need to. My entire exposure to <cfdocument> is in situations like this: investigation and "helping" other people.
Here's one I was looking at on Stack Overflow today. It involves generating a PDF with Japanese text in it. I did not solve the issue. This doesn't surprise me too much, because I have never once generated a PDF in CFML in a production environment. I've just never had the need to. My entire exposure to <cfdocument> is in situations like this: investigation and "helping" other people.
Identifying when a regex pattern matches at position zero in a string
G'day:
Sean asked on Twitter today for input into how to best handle a shortfall in some TestBox functionality. The TestBox Jira ticket is this one (it also contains basically what I'm about to say here!): "toThrow() cannot match empty message & cannot match detail". The reason for the first stated problem is because of the way CFML's reFind() function works.
Consider this code:
The gist of this is that the reFind() call checks to see whether URL.input is a string which comprises (in its entirety) zero or more digits.
Here are the results with a few test inputs:
So this shows that it only lets solely-numeric values past: values like 1b3 return a zero match. OK, so far so good.
But hang on. The pattern was for zero or more digits. So zero digits is also legit here. Let's try giving an empty string (which is indeed zero digits) as the input:
This is correct. The match of zero digits occurs at position zero in that empty string. That's a match. However... reFind() returns 0 if no match was found. As it turns out, this was not sensible behaviour, because it means that there's no way to distinguish between no match, and a match at position zero. Oops.
Sean asked on Twitter today for input into how to best handle a shortfall in some TestBox functionality. The TestBox Jira ticket is this one (it also contains basically what I'm about to say here!): "toThrow() cannot match empty message & cannot match detail". The reason for the first stated problem is because of the way CFML's reFind() function works.
Consider this code:
// reFind.cfm
param name="URL.input" default="";
pattern = "^\d*$";
match = refind(pattern, URL.input);
writeDump(var=[{input=URL.input},{match=match}]);
The gist of this is that the reFind() call checks to see whether URL.input is a string which comprises (in its entirety) zero or more digits.
Here are the results with a few test inputs:
array | |||||
---|---|---|---|---|---|
1 |
| ||||
2 |
|
array | |||||
---|---|---|---|---|---|
1 |
| ||||
2 |
|
array | |||||
---|---|---|---|---|---|
1 |
| ||||
2 |
|
array | |||||
---|---|---|---|---|---|
1 |
| ||||
2 |
|
So this shows that it only lets solely-numeric values past: values like 1b3 return a zero match. OK, so far so good.
But hang on. The pattern was for zero or more digits. So zero digits is also legit here. Let's try giving an empty string (which is indeed zero digits) as the input:
array | |||||
---|---|---|---|---|---|
1 |
| ||||
2 |
|
This is correct. The match of zero digits occurs at position zero in that empty string. That's a match. However... reFind() returns 0 if no match was found. As it turns out, this was not sensible behaviour, because it means that there's no way to distinguish between no match, and a match at position zero. Oops.
Wednesday 25 December 2013
File looping strategies: it doesn't seem to make a difference
G'day:
This article is more "for the sake of completeness" rather than being interesting. You'll perhaps recall I posted another boring article the other day: "So what did I do today? (Nothing interesting. Here are the details)", and I made a Peter Jackson allusion as to the merits of my writing volume (ie: sometimes more is not better). Sticking with that theme, here's the culmination of that line of investigation, which pretty much turned up nothing interesting.
A week or so ago, someone (I can't recall who... if it was you, lemme know and I'll credit you appropriately) on Twitter made the interesting observation that using <cfloop> over a file is about - I think - 10% more performant than using fileOpen() / fileReadLine() in a while loop. I find that slightly interesting as I like it when tags out perform script. Not because I prefer tags (very very far from it), but because stuff like this keeps us on our toes when it comes to exactly what's performant in CFML and what isn't. Another example of this was my investigation in to exactly how much slower is evaluate() compared to [some approach not using evaluate()] that I did a few months back: '"
This article is more "for the sake of completeness" rather than being interesting. You'll perhaps recall I posted another boring article the other day: "So what did I do today? (Nothing interesting. Here are the details)", and I made a Peter Jackson allusion as to the merits of my writing volume (ie: sometimes more is not better). Sticking with that theme, here's the culmination of that line of investigation, which pretty much turned up nothing interesting.
A week or so ago, someone (I can't recall who... if it was you, lemme know and I'll credit you appropriately) on Twitter made the interesting observation that using <cfloop> over a file is about - I think - 10% more performant than using fileOpen() / fileReadLine() in a while loop. I find that slightly interesting as I like it when tags out perform script. Not because I prefer tags (very very far from it), but because stuff like this keeps us on our toes when it comes to exactly what's performant in CFML and what isn't. Another example of this was my investigation in to exactly how much slower is evaluate() compared to [some approach not using evaluate()] that I did a few months back: '"
evalulate()
is really slow". Is it now?'.Monday 23 December 2013
Being loosely typed is often a good thing but...
G'day:
Here's the upshoot from something that bubbled up on Twitter. I'll spare you the replicated timeline this time, and just cut to the chase...
Here's the upshoot from something that bubbled up on Twitter. I'll spare you the replicated timeline this time, and just cut to the chase...
CFML/TestBox: running on actual proper tests
G'day:
Various people seem to be trying out TestBox on their old MXUnit unit tests today, and reporting back on Twitter. I'm working through our ones, and have a few findings. I have more that 140 chars of information, so I'll quickly blog instead.
I can't go into too many details regarding our tests, but we have a few thousand of them spread over a number of applications.
Running through our main tranche of... ooh... 2793 (!!) tests, I had the following issues:
One really annoying side effect of this is that TestBox doesn't preserve the context of where the error happened, it just reports it as happening in the bowels of TestBox itself, which is not correct. It should bubble back the original exception. It was basically impossible for me to bandaid some of the errors I was seeing because TestBox wasn't telling me where they were happening!
I'm outa lunchtime, so I will need to run the other tests later, and see if I come up with anything else. But until we get the issues above sorted out, TestBox is not a possibility for us. I'll continue to use it at home, though.
--
Adam
Various people seem to be trying out TestBox on their old MXUnit unit tests today, and reporting back on Twitter. I'm working through our ones, and have a few findings. I have more that 140 chars of information, so I'll quickly blog instead.
I can't go into too many details regarding our tests, but we have a few thousand of them spread over a number of applications.
Running through our main tranche of... ooh... 2793 (!!) tests, I had the following issues:
assertIsQuery() is missing
This is a standard MXUnit assertion (assertIsQuery()), missing from TestBox. Lack of this probably breaks about 200 of our unit testsassertIsEmpty() is missing
So is this (assertIsEmpty()). We use this less often, but it probably breaks 50-odd tests.mxunit:expectedException missing
It was pointed out to me the other day ("I should pay attention to what my readers tell me! expectException()") that MXUnit has a better approach to dealing with this - and one that seems is indeed implemented in TestBox - expectException(). However we have a lot of tests, and a bunch of them still use the above annotation. Perhaps 50 tests "fail" due to this not being implemented.makePublic() doesn't seem to work
I didn't investigate this thoroughly, but it seems TestBox has implemented makePublic(), but it doesn't actually work. I say this because I get no error on code calling makePublic(), but none of the methods made public actually are public, as a result. A few hundred failures.mock() not implemented
MXUnit has built-in mocking. So does TestBox. However each are different, and it doesn't look like MXUnit's version has been implemented. We only use this once in our tests (we generally use MockBox).If beforeTests() errors, testing halts
This in itself is completely the same as MXUnit, but it basically discounts any chance of us using TestBox as it stands, because given addAssertDecorator() is also not implemented, this prevents me from actually doing a test run. Because the whole thing falls over as soon as we try to bring in our custom assertions. That aside, lack of being able to call in custom assertions also kills about another 500 of our tests. Fixing beforeTests() would be really nice, but if addAssertDecorator() was implemented, that'd get us moving forward.One really annoying side effect of this is that TestBox doesn't preserve the context of where the error happened, it just reports it as happening in the bowels of TestBox itself, which is not correct. It should bubble back the original exception. It was basically impossible for me to bandaid some of the errors I was seeing because TestBox wasn't telling me where they were happening!
The method init was not found in component TestCase.cfc
This is a very edge-case one. We have a helper CFC which is not run as a test case, but we want to run some of TestCase's methods. So we instantiate an instance of TestCase of our own. In MXUnit, TestCase.cfc has an init() method. In TestBox, it does not, so it errors. This is one of those crazy edge-cases one only ever discovers testing on real world code.Others
I was getting a few other test failures too, in tests I know work fine. I wasn't too sure if those were a byproduct of all the stuff above, so I did not investigate. Once I have a stable system, I will look at those ones more closely.Conclusion
So those results weren't great. About a quarter of our tests would not run without being monkeyed with, and we cannot do a test run at all. Still: this is because of a few minor glitches we use in key areas of our tests.I'm outa lunchtime, so I will need to run the other tests later, and see if I come up with anything else. But until we get the issues above sorted out, TestBox is not a possibility for us. I'll continue to use it at home, though.
--
Adam
Saturday 21 December 2013
So what did I do today? (Nothing interesting. Here are the details)
G'day:
I faffed around on the computer - with ColdFusion and Railo and TestBox - all day today, and have concluded I have nothing interesting to show for it. Still... this is a log of what I've been up to with CFML, so you're gonna hear all about it anyhow. And what's "better"... I'm gonna spin it out to be two articles. I am the Peter Jackson of CFML blogs!
OK, so I got wind of something to investigate the other day, and to do the metrics, I needed to time some stuff. Normally I'd just use a getTickCount() before and after the code, but I thought I might need something more comprehensive this time, so I figured a wee stopwatch UDF was in order. I've also just installed TestBox and am in the process of seeing if I can migrate away from MXUnit, so decided to do another TDD exercise with it. Note: there is no great exposition in this article, or really much new about TDD etc. It's simply what I did today.
Note: I abandoned TesxtBox's MXUnit compatibility mode today, because of a show-stopper bug: "ADDASSERTDECORATOR not implemented". This is the function that allows the importing of custom assertions, which I am using here. On the basis of that, I decided to just go with TestBox syntax instead.
Let's have a quick look at the function, and the tests.
//makeStopwatch.cfm
struct function makeStopwatch(){
var timeline = [];
var lap = function(string message=""){
var ticksNow = getTickCount();
var lapCount = arrayLen(timeline);
var lap = {
currentClock = ticksNow,
lapDuration = lapCount > 0 ? ticksNow - timeLine[lapCount].currentClock : 0,
totalDuration = lapCount > 0 ? ticksNow - timeLine[1].currentClock : 0,
message = message
};
arrayAppend(timeline, lap);
return lap;
};
return {
start = function(string message="start"){
return lap(message);
},
lap = function(string message="lap"){
return lap(message);
},
stop = function(string message="stop"){
return lap(message);
},
getTimeline = function(){
return timeLine;
}
};
};
Not much interesting here. I enjoyed finding another reason / excuse to use function expressions and a wee bit of closure around the timeline which "sticks" with the start() / lap() / stop() / getTimeline() functions.
What this function does is to log a struct at start, each lap, and another on stop. Here's it in action:
// useStopwatch.cfm
include "makeStopwatch.cfm";
stopwatch = makeStopwatch();
stopwatch.start("Begin timing");
sleep(500);
stopwatch.lap();
sleep(1500);
secondLap = stopwatch.lap("after another 1500ms");
writeDump(var=secondLap, label="secondLap");
sleep(2000);
stopwatch.start("Stop timing");
writeDump(var=stopWatch.getTimeline());
Output:
secondLap - struct | |
---|---|
CURRENTCLOCK | 1387658080067 |
LAPDURATION | 1500 |
MESSAGE | after another 1500ms |
TOTALDURATION | 2000 |
array | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
1 |
| ||||||||||
2 |
| ||||||||||
3 |
| ||||||||||
4 |
|
This is the bumpf I usually need when I'm timing stuff. Well: some or all of it. It'll be quite handy, I reckon.
One interesting facet here is that initially I thought I'd need three separate functions for the start() / lap() / stop() functions, I was doing TDD (seriously, I knew you'd wonder, so everything I write on this blog uses full TDD now), and having knocked out the first few tests to verify the method signatures for the returned functions, it occurred to me that stop() was actually redundant. It doesn't do anything that lap() wouldn't already do. I mean all this "stopwatch" does is take time marks... there's no clock starting or stopping really (getTickCount() keeps on ticking, we simply start paying attention and then stop paying attention at some point).
So, anyway, I decided before I starting messing around with a redesign, I'd get the test coverage done, get it working, and then refactor. This is something one of my colleagues (Brian, I mean you) has been drumming into me recently: don't get halfway through something, decide to do it differently, start again, refactor, waste time, and not have anything to show for it if I get interrupted (or, in a work situation, we get to the end of the sprint and need to release). So I banged out the rest of the tests, got everything working, and looked at my code some more.
Here are the tests:
CFML: Follow-up to last night's article about a TestBox "bug". PEBCAK strikes again!
G'day:
Time for me to fall on my sword.
Yesterday I started looking at TestBox ("Unit Testing / TDD - switching off MXUnit, switching on TestBox"), and whilst messing around, I came across a coupla bugs (some legit, I hasten to add), one of which Luis asked me to clarify on the ColdBox mailing list. Which I did: "[Testbox v1.0.0] "Variable ISEXPECTEDEXCEPTION is undefined" on erroring test".
The gist of this is as follows:
This is a distillation of my situation, not actual code. I am testing TestBox's MXUnit compatibility "mode", so here's a test that checks that beforeTests() and setup() run, plus how it deals with an erroring test, and a failing test.
When I was running this on ColdFusion, I got this output:
Note how the erroring and failing tests were reporting as erroring, but with an error internal to TestBox, not with the expected output MXUnit would give. So basically when a test failed or errored, TestBox itself was failing.
At this point... can you see what I've done wrong here?
Here's the thing. For TestBox test CFCs, there is no need to extend anything. I guess TestBox just uses mix-ins to insert all its bits and pieces. So my CFC doesn't extend anything.
However with MXUnit, one does need to extend mxunit.framework.TestCase.
Obviously on all my "normal" MXUnit test CFCs I do have the correct inheritance in place. But this particular test CFC started out with me testing TestBox tests, not MXUnit tests, so I neglected to put the inheritance in when I switched over to running it with MXUnit compatibility "mode". My bad.
Once I sorted that out, my tests run fine:
I fed this back to Luis, so that's case closed on that one. I also editorialised a bit, saying this:
This has always annoyed me about MXUnit. And it'll annoy me about TestBox. It doesn't annoy me much, but that MXUnit always relied on a file-naming scheme to identify test CFCs always seemed a bit amateurish to me. [gallic shrug]... this is a very minor gripe, and I do not forget that MXUnit has still been one of the most useful CFML tools out there, and TestBox is almost certainly gonna be an appropriate successor.
And Luis... very awesome you followed up my mailing list question so promptly. Much appreciated. I need to venture out into an uncharacteristically windy London for coffee and food, but I shall be writing more tests today. Let's see how it goes...
--
Adam
Time for me to fall on my sword.
Yesterday I started looking at TestBox ("Unit Testing / TDD - switching off MXUnit, switching on TestBox"), and whilst messing around, I came across a coupla bugs (some legit, I hasten to add), one of which Luis asked me to clarify on the ColdBox mailing list. Which I did: "[Testbox v1.0.0] "Variable ISEXPECTEDEXCEPTION is undefined" on erroring test".
The gist of this is as follows:
// Tests.cfc
component {
public void function beforeTests(){
variables.beforeTestsRun = true;
}
public void function setup(){
variables.setupRun = true;
}
public void function testInitFunctions(){
assert(structKeyExists(variables, "beforeTestsRun"));
assert(structKeyExists(variables, "setupRun"));
}
public void function testThatErrors(){
throw(type="TestException", message="This is a test exception", detail="Note that it is NOT being caught / tested for. This is by design");
}
public void function testThatFails(){
fail("This test should fail");
}
}
This is a distillation of my situation, not actual code. I am testing TestBox's MXUnit compatibility "mode", so here's a test that checks that beforeTests() and setup() run, plus how it deals with an erroring test, and a failing test.
When I was running this on ColdFusion, I got this output:
TestBox v1.0.0.00062
Global Stats (86 ms)
[ Bundles/Suites/Specs: 1/1/3 ] [ Pass: 1 ] [ Failures: 0 ] [ Errors: 2 ] [ Skipped: 0 ] [ Reset ]shared.git.blogExamples.unittests.testbox.Tests (18 ms)
[ Suites/Specs: 1/3 ] [ Pass: 1 ] [ Failures: 0 ] [ Errors: 2 ] [ Skipped: 0 ] [ Reset ]- +shared.git.blogExamples.unittests.testbox.Tests (18 ms)
- testThatErrors (10 ms) - Variable ISEXPECTEDEXCEPTION is undefined.
- testThatFails (4 ms) - Variable ISEXPECTEDEXCEPTION is undefined.
At this point... can you see what I've done wrong here?
Here's the thing. For TestBox test CFCs, there is no need to extend anything. I guess TestBox just uses mix-ins to insert all its bits and pieces. So my CFC doesn't extend anything.
However with MXUnit, one does need to extend mxunit.framework.TestCase.
Obviously on all my "normal" MXUnit test CFCs I do have the correct inheritance in place. But this particular test CFC started out with me testing TestBox tests, not MXUnit tests, so I neglected to put the inheritance in when I switched over to running it with MXUnit compatibility "mode". My bad.
Once I sorted that out, my tests run fine:
TestBox v1.0.0.00062
Global Stats (82 ms)
[ Bundles/Suites/Specs: 1/1/3 ] [ Pass: 1 ] [ Failures: 1 ] [ Errors: 1 ] [ Skipped: 0 ] [ Reset ]shared.git.blogExamples.unittests.testbox.Tests (15 ms)
[ Suites/Specs: 1/3 ] [ Pass: 1 ] [ Failures: 1 ] [ Errors: 1 ] [ Skipped: 0 ] [ Reset ]- +shared.git.blogExamples.unittests.testbox.Tests (15 ms)
- testThatErrors (8 ms) - This is a test exception
- testThatFails (4 ms) - This test should fail
I fed this back to Luis, so that's case closed on that one. I also editorialised a bit, saying this:
It's a pity your test case CFCs don't need to be of a certain type, as that would have clarified that error from the outset. It would also help mitigate a failing MXUnit always had in my opinion: that it determines whether a CFC is a test CFC by its NAME rather than its TYPE (eg: it's a subclass of TestCase). This name-based approach isn't great OO, especially if the capability to do it "properly" is right there.
This has always annoyed me about MXUnit. And it'll annoy me about TestBox. It doesn't annoy me much, but that MXUnit always relied on a file-naming scheme to identify test CFCs always seemed a bit amateurish to me. [gallic shrug]... this is a very minor gripe, and I do not forget that MXUnit has still been one of the most useful CFML tools out there, and TestBox is almost certainly gonna be an appropriate successor.
And Luis... very awesome you followed up my mailing list question so promptly. Much appreciated. I need to venture out into an uncharacteristically windy London for coffee and food, but I shall be writing more tests today. Let's see how it goes...
--
Adam
Friday 20 December 2013
CFML: Using TestBox to find bugs... but not in a way I expected
G'day:
I've been off work today because I need to use up my annual leave before the end of the year, and have basically been parked in front of the computer all day. A bit of a busman's holiday, really.
So I started using TestBox today ("Unit Testing / TDD - switching off MXUnit, switching on TestBox"), and quickly found (what seems to be ~) a bug once I finished the article and started writing the code for my next article.
The details for the TestBox bug are on the the ColdBox Google group: "[Testbox v1.0.0] "Variable ISEXPECTEDEXCEPTION is undefined" on erroring test". I've got no idea what's going on: as far as I can tell their code is fine, but I've not had a chance to investigate, because since I raised that, I've been trying to work out why the hell my code doesn't run on Railo.
However I've just isolated this "bug" in Railo, and here's the details.
I've been off work today because I need to use up my annual leave before the end of the year, and have basically been parked in front of the computer all day. A bit of a busman's holiday, really.
So I started using TestBox today ("Unit Testing / TDD - switching off MXUnit, switching on TestBox"), and quickly found (what seems to be ~) a bug once I finished the article and started writing the code for my next article.
The details for the TestBox bug are on the the ColdBox Google group: "[Testbox v1.0.0] "Variable ISEXPECTEDEXCEPTION is undefined" on erroring test". I've got no idea what's going on: as far as I can tell their code is fine, but I've not had a chance to investigate, because since I raised that, I've been trying to work out why the hell my code doesn't run on Railo.
However I've just isolated this "bug" in Railo, and here's the details.
Unit Testing / TDD - switching off MXUnit, switching on TestBox
G'day:
This article is more an infrastructure discussion, rather than examining more actual testing stuff. The ever-growing *Box empire has recently borged into yet another part of the CFML community: testing. They're released another box... TestBox. TestBox is interesting to me as it has a different approach to testing than MXUnit has... rather than xUnit style assertion-based testing, instead favouring a BDD approach. I've not done a lick of BDD, but people keep banging on about it, so I shall be looking at it soon. -ish. First I need to switch to TestBox.
One appealing thing I had heard about TestBox is that it's backwards compatible with MXUnit, so this should mean that I can just do the switch and continue with my current approach to testing, and ease my way into BDD as I learn more about it. So the first thing I decided to examine is how well this stands up, and how many changes I need to make to my existing tests to get them to run. Realistically, nothing is every completely backwards compatible... not even say between different versions of the same software (ColdFusion 9 to ColdFusion 10), let along a second system emulating another system (eg: Railo and ColdFusion). This is fine. I don't expect this migration to be seamless.
Here's what I worked through this morning to get up and running (spoilers: kinda running) on TestBox.
I preface this with the fact that I have always found Ortus's documentation to be a bit impenetrable (there's too much of it, it waffles too much), so I was hesitant about how long this would all take.
There's a caveat with this though (and this is where I had problems), is that TestBox does have some web assets which need to be web browseable, so it does actually need a web mapping, not just a CF mapping. They do caveat this further down the page.
The first pitfall I had was which directory they're actually talking about. The zipfile has this baseline structure:
So I homed this lot in my CF root (not web root, CF root) as /frameworks/testbox/1.0.0/, and added a /testbox CF mapping to that location.
WARNING (if you're following along and doing this at the same time): this is not the correct thing to do. Keep reading...
I then had a look around for which directory I needed to add a web server virtual directory for, and found web-servable assets in the following locations:
/apidocs/
/browser/
/runner/
/samples/
/testbox/system/testing/reports/assets/
(I searched for images, JS, CSS, HTML and index.cfm files; not perfect, but will give me an idea).
OK, so I figured he apidocs and samples are separate from the TestBox app, but that still leaves three disconnected (and laterally displaced) directories which need to be web browseable. This ain't great. So basically it looks like I need to make the entire /testbox dir web browseable. That's a bit shit, and a bit how we might have set up our CFML-driven websites... ten years ago. Oh well.
Basically there's a file server.xml located at <ColdFusion_Home>/runtime/conf/server.xml, where <ColdFusion_Home> is the cfusion dir in your ColdFusion install directory. For me the conf dir is at: C:\apps\adobe\ColdFusion\10\cfusion\runtime\conf.
In there there's an XML note like this:
It's commented out by default. All the instructions one needs are in the file itself, but basically it's uncomment it, put actual paths in, and add an aliases attribute:
I restarted CF and browsed to http://localhost:8500/testbox, and I got the files in my C:\webroots\frameworks\testbox\1.0.0 directory listing, so that worked. Good to know. I'll now forget about server.xml and aliases and stuff as I won't need to do it again for another six months. Shrug.
This article is more an infrastructure discussion, rather than examining more actual testing stuff. The ever-growing *Box empire has recently borged into yet another part of the CFML community: testing. They're released another box... TestBox. TestBox is interesting to me as it has a different approach to testing than MXUnit has... rather than xUnit style assertion-based testing, instead favouring a BDD approach. I've not done a lick of BDD, but people keep banging on about it, so I shall be looking at it soon. -ish. First I need to switch to TestBox.
One appealing thing I had heard about TestBox is that it's backwards compatible with MXUnit, so this should mean that I can just do the switch and continue with my current approach to testing, and ease my way into BDD as I learn more about it. So the first thing I decided to examine is how well this stands up, and how many changes I need to make to my existing tests to get them to run. Realistically, nothing is every completely backwards compatible... not even say between different versions of the same software (ColdFusion 9 to ColdFusion 10), let along a second system emulating another system (eg: Railo and ColdFusion). This is fine. I don't expect this migration to be seamless.
Here's what I worked through this morning to get up and running (spoilers: kinda running) on TestBox.
I preface this with the fact that I have always found Ortus's documentation to be a bit impenetrable (there's too much of it, it waffles too much), so I was hesitant about how long this would all take.
Locating, downloading and installing
Finding it
I googled "testbox", and the first link was the ColdBox Platform Wiki - TestBox. This is promising. Within a paragraph (and a to-the-point paragraph which just intros the product, so maybe the docs have got some improved focus: cool) there were download links. TestBox requires ColdFusion 10 / Railo 4.1, btw. I presume it uses closure or something? I'm not sure. But that's cool, I use CF10 and Railo [latest] for my work for this blog. It does preclude me from really giving it a test our with our 3000 unit tests at work though (which is a shame), because we're still on CF9 and will be for the foreseeable future.Installing it
The installation instructions threw me a bit. The default suggestion is to put the testbox dir into the web root, but that's poor advice: only files specifically intended to be web browseable should ever be in your web root. Fortunately the also mention one can stick 'em anywhere, and map them in with a /testbox mapping. I wish this was their default suggestion. In fact I wish it was their only suggestion. They should not encourage poor practice.There's a caveat with this though (and this is where I had problems), is that TestBox does have some web assets which need to be web browseable, so it does actually need a web mapping, not just a CF mapping. They do caveat this further down the page.
The first pitfall I had was which directory they're actually talking about. The zipfile has this baseline structure:
/testbox_1.0.0/
testbox-1.0.0.00062-201312171237
apidocs/
browser/
runner/
runner-template/
samples/
testbox/
license.txt
mockbox.txt
testbox.txt
So I homed this lot in my CF root (not web root, CF root) as /frameworks/testbox/1.0.0/, and added a /testbox CF mapping to that location.
WARNING (if you're following along and doing this at the same time): this is not the correct thing to do. Keep reading...
I then had a look around for which directory I needed to add a web server virtual directory for, and found web-servable assets in the following locations:
/apidocs/
/browser/
/runner/
/samples/
/testbox/system/testing/reports/assets/
(I searched for images, JS, CSS, HTML and index.cfm files; not perfect, but will give me an idea).
OK, so I figured he apidocs and samples are separate from the TestBox app, but that still leaves three disconnected (and laterally displaced) directories which need to be web browseable. This ain't great. So basically it looks like I need to make the entire /testbox dir web browseable. That's a bit shit, and a bit how we might have set up our CFML-driven websites... ten years ago. Oh well.
Configuring Tomcat
Here's a challenge (cue: Sean to get grumpy). I have no idea how to set up a virtual directory on Tomcat's built-in web server. Fortunately that's what Google is for, so I googled "tomcat web server virtual directories", and the very first link is a ColdFusion-10-specific document: "Getting Started with Tomcat in ColdFusion 10". I shuddered slightly that this is just in the ColdFusion Blog, rather than in the CF docs where it belongs, but it'll do. Fortunately the info in there is accurate, which is good.Basically there's a file server.xml located at <ColdFusion_Home>/runtime/conf/server.xml, where <ColdFusion_Home> is the cfusion dir in your ColdFusion install directory. For me the conf dir is at: C:\apps\adobe\ColdFusion\10\cfusion\runtime\conf.
In there there's an XML note like this:
<Context
path = "/"
docBase = "<cf_home>\wwwroot"
WorkDir = "<cf_home>\runtime\conf\Catalina\localhost\tmp"
>
</Context>
It's commented out by default. All the instructions one needs are in the file itself, but basically it's uncomment it, put actual paths in, and add an aliases attribute:
<Context
path = "/"
docBase = "C:\apps\adobe\ColdFusion\10\cfusion\wwwroot"
WorkDir = "C:\apps\adobe\ColdFusion\10\cfusion\runtime\conf\Catalina\localhost\tmp"
aliases = "/testbox=C:\webroots\frameworks\testbox\1.0.0"
>
</Context>
I restarted CF and browsed to http://localhost:8500/testbox, and I got the files in my C:\webroots\frameworks\testbox\1.0.0 directory listing, so that worked. Good to know. I'll now forget about server.xml and aliases and stuff as I won't need to do it again for another six months. Shrug.
ColdFusion config
I put a mapping to the same place in my test app's Application.cfc:// Application.cfc
component {
this.mappings = {
"/cflib" = getDirectoryFromPath(getCurrentTemplatePath()),
"/testbox" = expandPath("/testbox") // CF will use the virtual directory to resolve that. This is slightly cheating, but hey
};
}
Bouquet for Adobe: Frank Jennings is a star
G'day:
Myself along with a coupla others gave the poor quality of the ColdFusion docs a bit of a battering on Twitter last night:
Myself along with a coupla others gave the poor quality of the ColdFusion docs a bit of a battering on Twitter last night:
Now that the @coldfusion documentation is a wiki, we can go in and add egg to @Adobe's face where appropriate: https://t.co/ARQzcQ8g0M
— Adam Tuttle (@AdamTuttle) December 19, 2013
.@fermatjen, can you pls kick someone in Bangalore up the arse and get them to finish their docs? https://t.co/uuhkI3taYz cc @ColdFusion
— Adam Cameron (@dacCfml) December 19, 2013
@dacCfml Don't you love it how they say it's a wiki but the public cannot edit it? Seen so many inconsistent examples I've wanted to fix.And it continues...
— Indy (@Indy_Griffiths) December 19, 2013
Wednesday 18 December 2013
Getting the word about CFML out there: Wikipedia (etc)
G'day:
This is a bit of a "random thought" more than an article. My focus on this blog at present is to catch up with responding to people's comments, before writing anything new. I've got another 1-2 unit testing / TDD articles partly drafted (just in my head at the moment), but this draft has been sitting around for a while, so I figured I'd just press "send" on it. It's not one of my usual 1000+ word mammoths. Which might be a relief.
I spend a lot of time reading stuff on Wikipedia. We probably all do. But I sit around in the evening - just for pleasure - reading pseudo-random articles. Usually whilst watching a movie or something.
Anyway, I read all sorts of topics, and often read up on programming-related topics too. Being a programmer 'n' all. One thing I have noticed is that a lot of articles on programming techniques have examples in various languages, eg: "this is how one does a for() loop in PHP" or whatever is relevant for the article. And another thing I have noticed is that CFML seldom features on the list of languages with examples. Take the "for loop" page on Wikipedia: it has examples in 24 different languages. But no CFML anywhere. It'd be easy to add something in, and I will try to get around to it soon.
After only about 10min research, I found a list of articles that could mention CFML but don't:
I reckon we could do worse than making sure CFML does get a mention on these pages (and any other relevant pages). It will all help get knowledge of CFML out there, and just get the concept of CFML in front of more people's eyes. It also might help demonstrate that CFML is still very much an alive and actively used & developed language.
So I'm gonna chip away at this stuff when the mood takes me. If you find any pages on Wikipedia that could use a mention of CFML... consider editing it. Or if you don't think you're up to that, let me know and I'll add it to the list above. If you do pitch in and help, let me know and I'll cross the page off the list above.
Another area we could stand to improve CFML's exposure is on the info page for ColdFusion on Stack Overflow. Compare this page: ColdFusion (fewer than 200 words); to this page: PHP (close to 2500 words!). Obviously volume != quality, but still... we're not doing ourselves proud there.
What other public resources are there in which CFML could promote itself better? Where can we contribute?
(I warned you this is a bit of a nothing article. Sorry).
--
Adam
This is a bit of a "random thought" more than an article. My focus on this blog at present is to catch up with responding to people's comments, before writing anything new. I've got another 1-2 unit testing / TDD articles partly drafted (just in my head at the moment), but this draft has been sitting around for a while, so I figured I'd just press "send" on it. It's not one of my usual 1000+ word mammoths. Which might be a relief.
I spend a lot of time reading stuff on Wikipedia. We probably all do. But I sit around in the evening - just for pleasure - reading pseudo-random articles. Usually whilst watching a movie or something.
Anyway, I read all sorts of topics, and often read up on programming-related topics too. Being a programmer 'n' all. One thing I have noticed is that a lot of articles on programming techniques have examples in various languages, eg: "this is how one does a for() loop in PHP" or whatever is relevant for the article. And another thing I have noticed is that CFML seldom features on the list of languages with examples. Take the "for loop" page on Wikipedia: it has examples in 24 different languages. But no CFML anywhere. It'd be easy to add something in, and I will try to get around to it soon.
After only about 10min research, I found a list of articles that could mention CFML but don't:
For loop(done)Do while loop(done)Foreach loop(done)While loop(done)?:(done)Anonymous function(done)- Mutator method
- this (computer programming)
- First-class function
- Base 36
Exception-handling syntax(done)
I reckon we could do worse than making sure CFML does get a mention on these pages (and any other relevant pages). It will all help get knowledge of CFML out there, and just get the concept of CFML in front of more people's eyes. It also might help demonstrate that CFML is still very much an alive and actively used & developed language.
So I'm gonna chip away at this stuff when the mood takes me. If you find any pages on Wikipedia that could use a mention of CFML... consider editing it. Or if you don't think you're up to that, let me know and I'll add it to the list above. If you do pitch in and help, let me know and I'll cross the page off the list above.
Another area we could stand to improve CFML's exposure is on the info page for ColdFusion on Stack Overflow. Compare this page: ColdFusion (fewer than 200 words); to this page: PHP (close to 2500 words!). Obviously volume != quality, but still... we're not doing ourselves proud there.
What other public resources are there in which CFML could promote itself better? Where can we contribute?
(I warned you this is a bit of a nothing article. Sorry).
--
Adam
Tuesday 17 December 2013
Not really news: Railo does grouped looping in CFScript
G'day:
I've recently been trying to be more positive about CFML... I get a bit of shit from people who think being honest about my impressions of ColdFusion equates to being negative. Bleak, perhaps, but I don't think it's negative. Anyway, Brad started a Twitter tag #favoriteCFML (I'll forgive the US-EN spelling this once ;-), and I've been popping the odd positive thought up on Twitter when it occurs to me. Join in if you like.
Anyway... this one deserves more than 140 chars.
I've recently been trying to be more positive about CFML... I get a bit of shit from people who think being honest about my impressions of ColdFusion equates to being negative. Bleak, perhaps, but I don't think it's negative. Anyway, Brad started a Twitter tag #favoriteCFML (I'll forgive the US-EN spelling this once ;-), and I've been popping the odd positive thought up on Twitter when it occurs to me. Join in if you like.
Anyway... this one deserves more than 140 chars.
Micha offers Railo-centric feedback on Adobe's CFSummit ColdFusion 2016 promises
G'day:
I meant to post this ages ago when it was more relevant, but it got buried in my in box.
Back when I posted the article "CFSummit: interesting ColdFusion 2016 stuff " (whilst CFSummit was actually on!), Micha from Railo responded with his own thoughts on what Adobe had said.
I really appreciate how much time Micha puts over to helping me on this blog by following up various things I raise or wonder about. His community approach is definitely something the guys from Adobe could take note of.
In this case I think it's pretty interesting stuff, so I'm going to reproduce his comment here, in case people missed it the first time.
Again, sorry to not do this straight away when it was more fresh.
I've adjusted this slightly for formatting, but otherwise it's the same copy as per Micha's original comment:
I meant to post this ages ago when it was more relevant, but it got buried in my in box.
Back when I posted the article "CFSummit: interesting ColdFusion 2016 stuff " (whilst CFSummit was actually on!), Micha from Railo responded with his own thoughts on what Adobe had said.
I really appreciate how much time Micha puts over to helping me on this blog by following up various things I raise or wonder about. His community approach is definitely something the guys from Adobe could take note of.
In this case I think it's pretty interesting stuff, so I'm going to reproduce his comment here, in case people missed it the first time.
Again, sorry to not do this straight away when it was more fresh.
I've adjusted this slightly for formatting, but otherwise it's the same copy as per Micha's original comment:
Monday 16 December 2013
CFML: A rare mishit from Railo: function caching
G'day:
Railo are pretty much where it's at as far as enhancing CFML goes. Adobe - due to a criminally long dev cycle, and seemingly complete ignorance of web development trends & practices - just drag the chain as far as keeping CFML competetive with other web dev options goes; and I believe Adobe are singularly responsible for diluting the relevance of CFML as a language ("I've come to a conclusion"). Railo's increased confidence with leading CFML enhancement - rather than just following in Adobe's shadow - gives me faith than CFML isn't "dead" as far as being an option for new projects and new developer interest goes.
However Railo is not a panacea, and sometimes they balls things up. Here's a situation in which I think they have let themselves down.
A while back Railo added function-result-caching to CFML. This was - I think - due to a suggestion that bubbled out from the community. It's good to listen to the community, but I think this is an example where there's a difference between "hearing" and "listening". They should have heard what the community said, but they shouldn't necessarily have listened. The issue is here - and obviously this is just my own opinion - the suggestion from the community was a bloody stupid one. But Railo implemented it.
Now I am not suggesting function-result-caching is not a desirable feature, but the way Railo chose to implement was basically to take exactly what the community discussion suggested, didn't sanity check it or assess how best a solution might be implemented in a programming language (rather than... for example... an application, or basically some implementation code), and built a very situation-specific solution into CFML.
Railo are pretty much where it's at as far as enhancing CFML goes. Adobe - due to a criminally long dev cycle, and seemingly complete ignorance of web development trends & practices - just drag the chain as far as keeping CFML competetive with other web dev options goes; and I believe Adobe are singularly responsible for diluting the relevance of CFML as a language ("I've come to a conclusion"). Railo's increased confidence with leading CFML enhancement - rather than just following in Adobe's shadow - gives me faith than CFML isn't "dead" as far as being an option for new projects and new developer interest goes.
However Railo is not a panacea, and sometimes they balls things up. Here's a situation in which I think they have let themselves down.
A while back Railo added function-result-caching to CFML. This was - I think - due to a suggestion that bubbled out from the community. It's good to listen to the community, but I think this is an example where there's a difference between "hearing" and "listening". They should have heard what the community said, but they shouldn't necessarily have listened. The issue is here - and obviously this is just my own opinion - the suggestion from the community was a bloody stupid one. But Railo implemented it.
Now I am not suggesting function-result-caching is not a desirable feature, but the way Railo chose to implement was basically to take exactly what the community discussion suggested, didn't sanity check it or assess how best a solution might be implemented in a programming language (rather than... for example... an application, or basically some implementation code), and built a very situation-specific solution into CFML.
Friday 13 December 2013
CFML: Quicky: another feed going to @CfmlNotifier
G'day:
If you follow @CfmlNotifier on Twitter (and if yer a CF developer, you should be!), you'll note I've started another feed to it, reporting along the lines of:
I'm doing a status update on that everysix hours 24 hours. It'll be interesting to see how the count changes. Due to how Twitter works, if I duplicate a status message (eg: in six hours time there's still 87 untriaged ColdFusion 10 bugs), the latter update will possibly be marked as a duplicate and not sent. I think this is OK: "no news is... well, not good news, but at least the status quo".
Hopefully you find this at least mildly informative.
And don't forget to vote for bugs that affect you, or if you need to give Adobe further information (like "why did you close this ticket saying 'never fix'? It's a right sball-ache for me!"). They do listen.
FYI, the count was over 100 a coupla days ago, so they do seem to be doing some triage at the moment. So that's good news. And it's certainly an improvement from when I wrote this: "212 untriaged ColdFusion 10 bugs" (back in May). Good work Adobe, but... hey... there's still quite a way to go, eh? All this stuff should be triaged already. They don't need to be fixed, but they should be triaged as soon as they're raised. Not months / years later.
Oh, and I know I've been a bit quiet this week (on the blog, anyhow). Work's been busy, and I've been a bit crook with a cold, so just watching telly in the evenings. I've got a coupla articles on the go, I just need the code sorted out.
Righto.
--
Adam
If you follow @CfmlNotifier on Twitter (and if yer a CF developer, you should be!), you'll note I've started another feed to it, reporting along the lines of:
2014-04-05 14:36 Untriaged ColdFusion 10 bug count: 61. List: http://t.co/XGoReqPrfq
— CfmlNotifier (@CfmlNotifier) April 5, 2014
I'm doing a status update on that every
Hopefully you find this at least mildly informative.
And don't forget to vote for bugs that affect you, or if you need to give Adobe further information (like "why did you close this ticket saying 'never fix'? It's a right sball-ache for me!"). They do listen.
FYI, the count was over 100 a coupla days ago, so they do seem to be doing some triage at the moment. So that's good news. And it's certainly an improvement from when I wrote this: "212 untriaged ColdFusion 10 bugs" (back in May). Good work Adobe, but... hey... there's still quite a way to go, eh? All this stuff should be triaged already. They don't need to be fixed, but they should be triaged as soon as they're raised. Not months / years later.
Oh, and I know I've been a bit quiet this week (on the blog, anyhow). Work's been busy, and I've been a bit crook with a cold, so just watching telly in the evenings. I've got a coupla articles on the go, I just need the code sorted out.
Righto.
--
Adam
Thursday 12 December 2013
Help me with some code, if you have a moment: numberFormat()
G'day:
Hey, I'm trying to come up with the most expedient way to effect this. I have a number which could be zero or more decimal places. If there are zero decimal places, I just want to display it as an integer. If it has a decimal component, I want to display up to two decimal places, but without any trailing zeros. EG:
I would have thought this would be easily achievable with just numberFormat(), but it only has a mandatory decimal point, it does not have an optional one. Also "9" does not work as documented: it always displays a digit on the right-hand-side of the decimal point. So numberFormat("1.0", "9.9") returns "1.0", whereas if it's optional, it should return "1" (although the "." being mandatory would cause issues still here, as that would mean I'd get "1.", which helps no-one).
Obviously this is not a tricky problem to solve other ways, with a regex or a coupla separate statements, but I thought I'd pick yer brains in case I'm missing something obvious, or someone can come up with a nice single-expression solution.
I've created a testRig.cfm gist here:
If you fancy it, post yer own gist with your treatment of this inserted where indicated. Wouldn't it be nice if we had a CFMLFiddle type thing?
Thanks for any suggestions!
--
Adam
Hey, I'm trying to come up with the most expedient way to effect this. I have a number which could be zero or more decimal places. If there are zero decimal places, I just want to display it as an integer. If it has a decimal component, I want to display up to two decimal places, but without any trailing zeros. EG:
Value | Desired rendering |
---|---|
0 | 0 |
0.0 | 0 |
0.00 | 0 |
0.000 | 0 |
0.001 | 0 |
0.1 | 0.1 |
0.10 | 0.1 |
0.100 | 0.1 |
0.101 | 0.1 |
0.11 | 0.11 |
0.110 | 0.11 |
0.111 | 0.11 |
I would have thought this would be easily achievable with just numberFormat(), but it only has a mandatory decimal point, it does not have an optional one. Also "9" does not work as documented: it always displays a digit on the right-hand-side of the decimal point. So numberFormat("1.0", "9.9") returns "1.0", whereas if it's optional, it should return "1" (although the "." being mandatory would cause issues still here, as that would mean I'd get "1.", which helps no-one).
Obviously this is not a tricky problem to solve other ways, with a regex or a coupla separate statements, but I thought I'd pick yer brains in case I'm missing something obvious, or someone can come up with a nice single-expression solution.
I've created a testRig.cfm gist here:
If you fancy it, post yer own gist with your treatment of this inserted where indicated. Wouldn't it be nice if we had a CFMLFiddle type thing?
Thanks for any suggestions!
--
Adam
Tuesday 10 December 2013
Things I don't know much about: SEO and WordPress
G'day:
A reader has sent me an email asking for help, but I don't know much about the topics they're asking about. So perhaps you lot can offer some insight...
A reader has sent me an email asking for help, but I don't know much about the topics they're asking about. So perhaps you lot can offer some insight...
Monday 9 December 2013
I should pay attention to what my readers tell me! expectException()
G'day:
Sometimes I don't pay attention very well, or my attention span wanders...
... what was I saying?
Oh yeah, MXUnit. And me not paying attention to good advice people give me. I've just adjusted a bunch of my tests from previous articles in my unit testing / TDD series, to handle exceptions better.
Sometimes I don't pay attention very well, or my attention span wanders...
... what was I saying?
Oh yeah, MXUnit. And me not paying attention to good advice people give me. I've just adjusted a bunch of my tests from previous articles in my unit testing / TDD series, to handle exceptions better.
trycf.com
G'day:
One of our CFML community colleagues has dun good. Abram Adams has created a site trycf.com, which deserve our attention and our support. It's based on the same concept as tryruby.org, which is also a handy site. Basically it offers CFML tutorials, which one completes interactively and in-browser, with a built-in CFML edit / execute window.
One of our CFML community colleagues has dun good. Abram Adams has created a site trycf.com, which deserve our attention and our support. It's based on the same concept as tryruby.org, which is also a handy site. Basically it offers CFML tutorials, which one completes interactively and in-browser, with a built-in CFML edit / execute window.
Sunday 8 December 2013
Something I'd not looked at before: ColdFusion security sandboxes
G'day:
Russ posted a question on Twitter yesterday:
Russ posted a question on Twitter yesterday:
has anyone else had problems with security sandboxes not working in #coldfusion 10, as in everything works even when it is disabledI have to concede this is part of ColdFusion I have never looked at. He didn't get any useful responses over night, so I decided to check whether I could either confirm the behaviour he was seeing, or declare "works OK on my machine".
— Russ Michaels (@RussMichaels) December 8, 2013
CFML: For the sake of completeness: createLocalisedDayOfWeekAsInteger()
: a closure approach
G'day:
A coupla days ago I continued my TDD / unit testing series with an article "Unit Testing / TDD - refactoring existing code". This engendered a few comments, none of which paid any attention to the intent article, instead focused on something pretty much irrelevant to what I was discussing. This engendered this reaction from me:
Oh how I love my readership sometimes. But, actually, the comments got me thinking...
That article covered the approval process for CFLib of a function dayOfWeekAsInteger(), which is the reverse of dayOfWeekAsString(): it takes a string (in English only), and returns which day of the week that corresponds to, starting with Sunday as the first day. Why does it start on Sunday instead of Monday (as per ISO 8601)? Because it's the inverse of dayOfWeekAsString(), and that's how that does it. Is that right? Well: no. Is that relevant to either this UDF or the blog article? Also: no.
However James's suggestion for how Simon could rewrite his function to suit James's requirements (James: write your own functions if you want them different from how others have implemented them!) did get me thinking about how to do it. A thought popped into my head that one could leverage closure to create a function which was specific to a given locale. This might not be exactly what James wants, but it was what I decided to look into, because it's slightly interesting, and covers a feature of closure I've not covered anywhere on this blog yet. So it interests me.
A coupla days ago I continued my TDD / unit testing series with an article "Unit Testing / TDD - refactoring existing code". This engendered a few comments, none of which paid any attention to the intent article, instead focused on something pretty much irrelevant to what I was discussing. This engendered this reaction from me:
Bashes. Head. Against. Desk. In. Despair.
— Adam Cameron (@dacCfml) December 6, 2013
Oh how I love my readership sometimes. But, actually, the comments got me thinking...
That article covered the approval process for CFLib of a function dayOfWeekAsInteger(), which is the reverse of dayOfWeekAsString(): it takes a string (in English only), and returns which day of the week that corresponds to, starting with Sunday as the first day. Why does it start on Sunday instead of Monday (as per ISO 8601)? Because it's the inverse of dayOfWeekAsString(), and that's how that does it. Is that right? Well: no. Is that relevant to either this UDF or the blog article? Also: no.
However James's suggestion for how Simon could rewrite his function to suit James's requirements (James: write your own functions if you want them different from how others have implemented them!) did get me thinking about how to do it. A thought popped into my head that one could leverage closure to create a function which was specific to a given locale. This might not be exactly what James wants, but it was what I decided to look into, because it's slightly interesting, and covers a feature of closure I've not covered anywhere on this blog yet. So it interests me.
Saturday 7 December 2013
500: to put things in perspective
G'day:
In August, Ray posted this: "Over 5000! (Almost...)".
So it puts things into perspective that this is only my 500th article. Still: I've only been going for 513 days, so that ain't bad. Almost one per day (obviously the content is variable though!). I was only really aiming for one a week - concerned I'd run out of things to say - so I'm doin' all right, really.
And, no, I will not remind you every 100 articles (I did an article at 400 too: "Bad Request"). But I probably will remind you @ the 1000 mark. If I get there.
Cheers for reading & commenting & stuff.
--
Adam
In August, Ray posted this: "Over 5000! (Almost...)".
So it puts things into perspective that this is only my 500th article. Still: I've only been going for 513 days, so that ain't bad. Almost one per day (obviously the content is variable though!). I was only really aiming for one a week - concerned I'd run out of things to say - so I'm doin' all right, really.
And, no, I will not remind you every 100 articles (I did an article at 400 too: "Bad Request"). But I probably will remind you @ the 1000 mark. If I get there.
Cheers for reading & commenting & stuff.
--
Adam
CFML weirdness with chr(0)
G'day:
I was trying to help someone on Stack Overflow y/day with their question "How to pad a string with null/zero-bytes in ColdFusion & difference between CF on MacOS and Windows". The question is actually shorten than the title: how to add null characters to a string in CFML.
I thought the answer was a simple "use chr(0)", but this turned out to not be a viable answer on ColdFusion (or Railo for that matter).
In response to my suggestion, Stack Overflow veteran Leigh made the observation "Unfortunately no. The character just disappears. But URLDecode("
And the - surprising to me - output:
string: [test]
length: [4]
Um... where gone?
I tried this on Railo... thinking Railo's more likely to get it right than CF is, and it had the same output. Testing on OpenBD got what I'd consider to be the correct results:
string: [test]
length: [5]
The NULL isn't printable, so doesn't render anything, but it should still actually be there, and that string should consist of five bytes: 0, 116, 101, 115, 116. That's a length of five. As per OpenBD's output.
That said, I know that NULLs do have special meaning in some strings, for example C has the concept of a null-terminated string, in which the null signifies the end of the string, not a character in the string. I wasn't aware of this being a "thing" in Java, but maybe it was.
I refined my code somewhat to not be simply end-padding the string:
So I'm sticking a NULL at the beginning and end of the string. If it was acting as a terminator, s would simply be an empty string afterwards. But I get this (CF and Railo both):
foo:3:102
102 is indeed the ASCII code for f.
So what's the story here? I still had a suspicion that something "non-stupid" was happening here, and I just didn't get it. Maybe there's something about a NULL char's standard handling that means it's not added to strings. Although this seems far-fetched as obviously there's use-cases for it (see the Stack Overflow question), and indeed Leigh came up with the fudged way to do it:
I was trying to help someone on Stack Overflow y/day with their question "How to pad a string with null/zero-bytes in ColdFusion & difference between CF on MacOS and Windows". The question is actually shorten than the title: how to add null characters to a string in CFML.
I thought the answer was a simple "use chr(0)", but this turned out to not be a viable answer on ColdFusion (or Railo for that matter).
In response to my suggestion, Stack Overflow veteran Leigh made the observation "Unfortunately no. The character just disappears. But URLDecode("
%
0
0
") will generate a single null byte". I've not known Leigh to be wrong so I didn't doubt him, but that sounded really odd, so I decided to check it out.<!--- baseline.cfm --->
<cfset s = "test" & chr(0)>
<cfoutput>
string: [#s#]<br>
length: [#len(s)#]<br>
</cfoutput>
And the - surprising to me - output:
string: [test]
length: [4]
Um... where gone?
I tried this on Railo... thinking Railo's more likely to get it right than CF is, and it had the same output. Testing on OpenBD got what I'd consider to be the correct results:
string: [test]
length: [5]
The NULL isn't printable, so doesn't render anything, but it should still actually be there, and that string should consist of five bytes: 0, 116, 101, 115, 116. That's a length of five. As per OpenBD's output.
That said, I know that NULLs do have special meaning in some strings, for example C has the concept of a null-terminated string, in which the null signifies the end of the string, not a character in the string. I wasn't aware of this being a "thing" in Java, but maybe it was.
I refined my code somewhat to not be simply end-padding the string:
<!--- outputStringContainingChr0.cfm --->
<cfset s = chr(0) & "foo#chr(0)#">
<cfoutput>#s#:#len(s)#:#asc(left(s,1))#</cfoutput>
So I'm sticking a NULL at the beginning and end of the string. If it was acting as a terminator, s would simply be an empty string afterwards. But I get this (CF and Railo both):
foo:3:102
102 is indeed the ASCII code for f.
So what's the story here? I still had a suspicion that something "non-stupid" was happening here, and I just didn't get it. Maybe there's something about a NULL char's standard handling that means it's not added to strings. Although this seems far-fetched as obviously there's use-cases for it (see the Stack Overflow question), and indeed Leigh came up with the fudged way to do it:
Groovy: G'day World
G'day:
I'm investigating some ColdFusion stupidity (more on that after this article), and as part of my comparison of the behaviour in other languages, I figured I should also test on another JVM-based language. And given all the adulation of Groovy at the moment, and an exceptional presentation "Grails/Groovy Primer for ColdFusion Developers" from Scott Stroz, I decided to D/L and install Groovy.
Warning: this will be a short article, and will only take me as far as getting "G'day World" onto the screen. It's really aimed at CFMLers like myself who never find time to do anything else, and just a demonstration as to how quick this all was.
Previously I've written up similar exploits on PHP: "PHP: from zero to... Hello World" and Ruby: "Ruby: stream-of-consciousness".
OK, so here's what I did to get Groovy up and running on my machine:
I'm investigating some ColdFusion stupidity (more on that after this article), and as part of my comparison of the behaviour in other languages, I figured I should also test on another JVM-based language. And given all the adulation of Groovy at the moment, and an exceptional presentation "Grails/Groovy Primer for ColdFusion Developers" from Scott Stroz, I decided to D/L and install Groovy.
Warning: this will be a short article, and will only take me as far as getting "G'day World" onto the screen. It's really aimed at CFMLers like myself who never find time to do anything else, and just a demonstration as to how quick this all was.
Previously I've written up similar exploits on PHP: "PHP: from zero to... Hello World" and Ruby: "Ruby: stream-of-consciousness".
OK, so here's what I did to get Groovy up and running on my machine:
CFML: Response for Russ
G'day:
I feel like writing more in response to Russ than Twitter messaging will sensibly allow, so am doing a quick blog article instead.
The context is as follows:
Thread for blog article
Note that the RT's of Matt's messages aren't quite in the correct order, but as his Twitter account is "private" (that was easily circumvented ;-), I cannot include them inline in a custom timeline. But the gist of the exchange is preserved.
Right, so I feel the decline of CFML as a language is mostly down to Adobe's stewardship of it. There are a few factors here:
I feel like writing more in response to Russ than Twitter messaging will sensibly allow, so am doing a quick blog article instead.
The context is as follows:
Thread for blog article
Note that the RT's of Matt's messages aren't quite in the correct order, but as his Twitter account is "private" (that was easily circumvented ;-), I cannot include them inline in a custom timeline. But the gist of the exchange is preserved.
Right, so I feel the decline of CFML as a language is mostly down to Adobe's stewardship of it. There are a few factors here:
Friday 6 December 2013
Unit Testing / TDD - refactoring existing code
G'day:
I've just had a new submission in the CFLib queue, and as it's an easy one to test and release, I'm gonna jump on it and get it out there today. However I want to test it first, plus I want to refactor it slightly, so I'm gonna use TDD to do so. And document what I'm doing as I go.
This continues a series on unit testing and TDD that I'm doing, the rest of which are tagged with either "Unit Testing" or "TDD" or both, so you can look them up via those links (there's too many to list now, so I won't bother).
The code for this article will be here: https://github.com/daccfml/scratch/tree/master/cflib/dayOfWeekAsInt (only the baseline files are there at the moment, as I haven't written the code yet ;-).
I've just had a new submission in the CFLib queue, and as it's an easy one to test and release, I'm gonna jump on it and get it out there today. However I want to test it first, plus I want to refactor it slightly, so I'm gonna use TDD to do so. And document what I'm doing as I go.
This continues a series on unit testing and TDD that I'm doing, the rest of which are tagged with either "Unit Testing" or "TDD" or both, so you can look them up via those links (there's too many to list now, so I won't bother).
The code for this article will be here: https://github.com/daccfml/scratch/tree/master/cflib/dayOfWeekAsInt (only the baseline files are there at the moment, as I haven't written the code yet ;-).
A question in more than 140 characters
G'day:
I'm just wondering what you think, and can't fit the question into a Twitter message.
Say I have a function which takes a string, and returns the numeric represent of that string in the context of a set list of values. EG: the function is dayOfWeekAsInt() takes "Monday" and returns 2 (Monday being the second day of the week). What should the function do if it receives invalid input, say "Moonday"?
I'd err towards raising an exception, but another option could be to return 0. On one hand "Moonday" is not a valid input, so an exception is good response. On the other hand "Moonday" has no index in the context of the series Sunday-Saturday, so I can see a case for zero too.
Thoughts?
--
Adam
I'm just wondering what you think, and can't fit the question into a Twitter message.
Say I have a function which takes a string, and returns the numeric represent of that string in the context of a set list of values. EG: the function is dayOfWeekAsInt() takes "Monday" and returns 2 (Monday being the second day of the week). What should the function do if it receives invalid input, say "Moonday"?
I'd err towards raising an exception, but another option could be to return 0. On one hand "Moonday" is not a valid input, so an exception is good response. On the other hand "Moonday" has no index in the context of the series Sunday-Saturday, so I can see a case for zero too.
Thoughts?
--
Adam
Subscribe to:
Posts (Atom)