├── code ├── 2 │ ├── doVariation.cfm │ ├── do.cfm │ ├── while.cfm │ ├── person.cfm │ ├── filter.cfm │ ├── every.cfm │ ├── continue.cfm │ └── Person.cfc ├── 4 │ ├── components │ │ ├── ParentClass.cfc │ │ ├── MyClass.cfc │ │ ├── WithAnnotation.cfc │ │ ├── testClass.cfm │ │ └── withAnnotation.cfm │ ├── minimalImplicitAccessors │ │ ├── Application.cfc │ │ ├── person.cfm │ │ └── Person.cfc │ ├── getters │ │ ├── person.cfm │ │ └── Person.cfc │ ├── minimalProperties │ │ ├── person.cfm │ │ └── Person.cfc │ └── minimalAccessors │ │ ├── person.cfm │ │ └── Person.cfc └── 6 │ ├── compileError.cfm │ ├── divisionByZero.cfm │ ├── rethrow.cfm │ ├── javaException.cfm │ ├── throw.cfm │ ├── anException.cfm │ ├── file.cfm │ ├── namespacedException.cfm │ └── finally.cfm ├── .gitignore ├── images ├── 0 │ └── serverDump.png ├── 4 │ └── componentMetadata.png └── 6 │ ├── Thumbs.db │ ├── throw.png │ ├── rethrow.png │ ├── compileError.png │ └── divisionByZero.png ├── Contributors.md ├── chapters ├── 23-tbc.md ├── 24-tbc.md ├── 17-advanced_orm.md ├── 18-file_system.md ├── 21-threading_locking.md ├── 19-rest.md ├── 08-clean_code.md ├── 22-java.md ├── 16-advanced_db.md ├── 20-function_expressions.md ├── 07-tdd.md ├── 15-advanced_oo.md ├── 10-application_framework.md ├── 13-tags.md ├── 14-custom_tags.md ├── 05-components_conted.md ├── 11-orm.md ├── 12-mvc.md ├── 09-request_response.md ├── 04-components.md ├── 00-housekeeping.md ├── 01-commands.md ├── 06-error_handling.md ├── 02-flow_control.md └── 03-types.md ├── licence.md ├── CONTRIBUTING.md └── README.md /code/4/components/ParentClass.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | -------------------------------------------------------------------------------- /code/4/components/MyClass.cfc: -------------------------------------------------------------------------------- 1 | component extends=ParentClass { 2 | 3 | } -------------------------------------------------------------------------------- /code/6/compileError.cfm: -------------------------------------------------------------------------------- 1 | 2 | result = someFunction(; 3 | -------------------------------------------------------------------------------- /code/6/divisionByZero.cfm: -------------------------------------------------------------------------------- 1 | 2 | result = 1 / 0; 3 | -------------------------------------------------------------------------------- /images/6/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/6/Thumbs.db -------------------------------------------------------------------------------- /images/6/throw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/6/throw.png -------------------------------------------------------------------------------- /images/6/rethrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/6/rethrow.png -------------------------------------------------------------------------------- /code/2/doVariation.cfm: -------------------------------------------------------------------------------- 1 | 2 | i=1; 3 | do writeOutput(i); 4 | while (++i <= 5); 5 | -------------------------------------------------------------------------------- /code/4/components/WithAnnotation.cfc: -------------------------------------------------------------------------------- 1 | component specialAnnotation="Annotation Value" { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /images/0/serverDump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/0/serverDump.png -------------------------------------------------------------------------------- /code/2/do.cfm: -------------------------------------------------------------------------------- 1 | 2 | i=1; 3 | do { 4 | writeOutput(i); 5 | } while (++i <= 5); 6 | -------------------------------------------------------------------------------- /code/4/components/testClass.cfm: -------------------------------------------------------------------------------- 1 | 2 | object = new MyClass(); 3 | writeDump(object); 4 | -------------------------------------------------------------------------------- /code/4/minimalImplicitAccessors/Application.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | this.invokeImplicitAccessor = true; 3 | } -------------------------------------------------------------------------------- /images/6/compileError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/6/compileError.png -------------------------------------------------------------------------------- /images/6/divisionByZero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/6/divisionByZero.png -------------------------------------------------------------------------------- /code/2/while.cfm: -------------------------------------------------------------------------------- 1 | 2 | i=0; 3 | while (++i <= 5) { 4 | writeOutput(i); // 12345 5 | } 6 | -------------------------------------------------------------------------------- /images/4/componentMetadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamcameron/cfml24h/HEAD/images/4/componentMetadata.png -------------------------------------------------------------------------------- /code/4/components/withAnnotation.cfm: -------------------------------------------------------------------------------- 1 | 2 | object = new WithAnnotation(); 3 | writeDump(getMetadata(object)); 4 | -------------------------------------------------------------------------------- /code/4/getters/person.cfm: -------------------------------------------------------------------------------- 1 | 2 | // person.cfm 3 | person = new Person("Abigail", "Bowen"); 4 | writeDump(person); 5 | -------------------------------------------------------------------------------- /code/2/person.cfm: -------------------------------------------------------------------------------- 1 | 2 | friend = new Person("Isla", "Jeffries"); 3 | 4 | fullNameOfFriend = friend.getFullName(); 5 | writeOutput(fullNameOfFriend); 6 | -------------------------------------------------------------------------------- /code/6/rethrow.cfm: -------------------------------------------------------------------------------- 1 | 2 | try { 3 | result = 1 / 0; 4 | } catch (any e){ 5 | writeOutput("type: #e.type#; message: #e.message#"); 6 | rethrow; 7 | } 8 | -------------------------------------------------------------------------------- /code/2/filter.cfm: -------------------------------------------------------------------------------- 1 | 2 | letters = ["a", "b", "c", "d", "e", "f"]; 3 | 4 | oddLetters = letters.filter(function(letter, index){ 5 | return index MOD 2; 6 | }); 7 | -------------------------------------------------------------------------------- /Contributors.md: -------------------------------------------------------------------------------- 1 | Thanks to the following people: 2 | 3 | * Alex Skinner 4 | * Mark Drew 5 | * Ray Camden 6 | * Matt Busche 7 | * Ryan Guill 8 | * Andy Myers 9 | * James Mohler 10 | -------------------------------------------------------------------------------- /code/2/every.cfm: -------------------------------------------------------------------------------- 1 | 2 | numbers = [1,2,3,4,5]; 3 | 4 | isEntirelyNumeric = numbers.every(function(number){ 5 | writeOutput(number); 6 | return isNumeric(number); 7 | }); 8 | -------------------------------------------------------------------------------- /code/6/javaException.cfm: -------------------------------------------------------------------------------- 1 | 2 | try { 3 | throw(object=createObject("java", "java.lang.Exception").init("This is a Java Exception")); 4 | }catch(any e){ 5 | writeDump(e); 6 | } 7 | -------------------------------------------------------------------------------- /code/4/minimalProperties/person.cfm: -------------------------------------------------------------------------------- 1 | 2 | person = new Person("Abigail", "Bowen"); 3 | 4 | writeDump({ 5 | variables = person.getVariables(), 6 | this = person.getThis() 7 | }); 8 | -------------------------------------------------------------------------------- /code/2/continue.cfm: -------------------------------------------------------------------------------- 1 | 2 | for (i=1; i <= 5; i++){ 3 | writeOutput("Before continue: #i#
"); 4 | continue; 5 | writeOutput("After continue: #i#
"); 6 | } 7 | writeOutput("After loop
"); 8 |
-------------------------------------------------------------------------------- /code/4/minimalImplicitAccessors/person.cfm: -------------------------------------------------------------------------------- 1 | 2 | person = new Person("Abigail", "Bowen"); 3 | 4 | writeDump({ 5 | firstName = person.firstName, 6 | lastName = person.lastName 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /chapters/23-tbc.md: -------------------------------------------------------------------------------- 1 | # TBC # 2 | 3 | NOTES: 4 | no idea 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/24-tbc.md: -------------------------------------------------------------------------------- 1 | # TBC # 2 | 3 | NOTES: 4 | no idea 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /code/4/minimalAccessors/person.cfm: -------------------------------------------------------------------------------- 1 | 2 | // person.cfm 3 | person = new Person("Abigail", "Bowen"); 4 | writeDump(person); 5 | 6 | person2 = createObject("Person"); 7 | person2.init("Catriona", "Dawes"); 8 | writeDump(person2); 9 | 10 | -------------------------------------------------------------------------------- /chapters/17-advanced_orm.md: -------------------------------------------------------------------------------- 1 | # Advanced ORM # 2 | 3 | NOTES: 4 | HQL & inheritance & composition 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/18-file_system.md: -------------------------------------------------------------------------------- 1 | # File system / HTTP / FTP # 2 | 3 | NOTES: 4 | General file ops. 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/21-threading_locking.md: -------------------------------------------------------------------------------- 1 | # Threads and locking # 2 | 3 | NOTES: 4 | As it says on the tin 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /code/4/minimalAccessors/Person.cfc: -------------------------------------------------------------------------------- 1 | // Person.cfc 2 | component { 3 | 4 | property firstName; 5 | property lastName; 6 | 7 | function init(firstName, lastName){ 8 | variables.firstName = arguments.firstName; 9 | variables.lastName = arguments.lastName; 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /code/6/throw.cfm: -------------------------------------------------------------------------------- 1 | 2 | try { 3 | result = 1 / 0; 4 | } catch (any e){ 5 | writeOutput("type: #e.type#; message: #e.message#"); 6 | throw(type="BadExpressionException", message="The provided expression was invalid", detail="Original exception: #e.type#"); 7 | } 8 | -------------------------------------------------------------------------------- /chapters/19-rest.md: -------------------------------------------------------------------------------- 1 | # REST # 2 | 3 | NOTES: 4 | CFC annotations and summary of what REST is (source from AdamT?) 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/08-clean_code.md: -------------------------------------------------------------------------------- 1 | # Clean code # 2 | 3 | NOTES: 4 | Was hoping to get Brian Sadler to write this chapter. 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | 11 | -------------------------------------------------------------------------------- /code/2/Person.cfc: -------------------------------------------------------------------------------- 1 | component accessors=true { 2 | property firstName; 3 | property lastName; 4 | 5 | function init(firstName, lastName){ 6 | setFirstName(firstName); 7 | setLastName(lastName); 8 | } 9 | 10 | function getFullName(){ 11 | return firstName & " " & lastName; 12 | } 13 | } -------------------------------------------------------------------------------- /chapters/22-java.md: -------------------------------------------------------------------------------- 1 | # Java interaction # 2 | 3 | NOTES: 4 | The various ways CFML and Java can interact (in both directions) 5 | 6 | 7 | 8 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 9 | 10 | Don't submit pull requests for content yet. 11 | -------------------------------------------------------------------------------- /code/6/anException.cfm: -------------------------------------------------------------------------------- 1 | 2 | try { 3 | throw( 4 | type = "SomeException", 5 | errorCode = "123", 6 | message = "This is an exception", 7 | detail = "And more detail on the exception", 8 | extendedinfo = "More info still" 9 | ); 10 | }catch(any e){ 11 | writeDump(e); 12 | } 13 | -------------------------------------------------------------------------------- /chapters/16-advanced_db.md: -------------------------------------------------------------------------------- 1 | # Advanced DB # 2 | 3 | NOTES: 4 | * The concepts behind separating storage logic and business logic via procs? 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/20-function_expressions.md: -------------------------------------------------------------------------------- 1 | # Functional programming concepts # 2 | 3 | NOTES: 4 | Closures and iteration techniques and rationales 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/07-tdd.md: -------------------------------------------------------------------------------- 1 | # TDD # 2 | 3 | NOTES: 4 | Explanation of why it's ESSENTIAL. Perhaps distilled from the first three-ish entries in my TDD series from my blog 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/15-advanced_oo.md: -------------------------------------------------------------------------------- 1 | # Advanced OO # 2 | 3 | NOTES: 4 | No specific plans here, just figure that interfaces, polymorphism and perhaps a design pattern or two might be useful. 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/10-application_framework.md: -------------------------------------------------------------------------------- 1 | # Application framework # 2 | 3 | NOTES: 4 | Application.cfc methods and how they interrelate with the lifecycle stuff from the previous chapter 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /code/4/minimalProperties/Person.cfc: -------------------------------------------------------------------------------- 1 | component { 2 | property firstName; 3 | property lastName; 4 | 5 | function init(firstName, lastName){ 6 | variables.firstName = arguments.firstName; 7 | variables.lastName = arguments.lastName; 8 | } 9 | 10 | function getVariables(){ 11 | return variables; 12 | } 13 | 14 | function getThis(){ 15 | return this; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /chapters/13-tags.md: -------------------------------------------------------------------------------- 1 | # View logic with CFML tags # 2 | 3 | NOTES: 4 | Purposesly downplayed. Only referenced in the context of views, and most tags ignored / actively discouraged as a vestige of an obsolete dev paradigm. 5 | 6 | 7 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 8 | 9 | Don't submit pull requests for content yet. 10 | -------------------------------------------------------------------------------- /chapters/14-custom_tags.md: -------------------------------------------------------------------------------- 1 | # Custom Tags # 2 | 3 | NOTES: 4 | Somehow re-invigorate them as a view solution. 5 | 6 | On the other hand, maybe show integrating a templating engine for views instead of tags *at all*? 7 | 8 | 9 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 10 | 11 | Don't submit pull requests for content yet. 12 | -------------------------------------------------------------------------------- /licence.md: -------------------------------------------------------------------------------- 1 | [![Creative Commons Licence](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)](http://creativecommons.org/licenses/by-sa/4.0/) 2 | 3 | [This project](https://github.com/adamcameron/cfml24h), created by [Adam Cameron](https://github.com/adamcameron) 4 | is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/). 5 | -------------------------------------------------------------------------------- /chapters/05-components_conted.md: -------------------------------------------------------------------------------- 1 | # OO wth CFML cont'ed # 2 | 3 | NOTES: 4 | General intent here is that the topic is too big to cover in one chapter, so this is overspill. 5 | 6 | Content to be decided once chapter 4 is drafted. 7 | 8 | 9 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 10 | 11 | Don't submit pull requests for content yet. 12 | -------------------------------------------------------------------------------- /code/6/file.cfm: -------------------------------------------------------------------------------- 1 | 2 | try { 3 | writeOutput("Open a file for reading
"); 4 | f = fileOpen(getCurrentTemplatePath()); 5 | line = f.readLine(); 6 | randRange(0,1) ? throw(type="ForcedException") : false; 7 | } catch (ForcedException e){ 8 | writeOutput("Deal with the exception
"); 9 | } finally { 10 | f.close(); 11 | writeOutput("And the file is safely closed
"); 12 | } 13 |
-------------------------------------------------------------------------------- /code/6/namespacedException.cfm: -------------------------------------------------------------------------------- 1 | 2 | try { 3 | switch (randRange(1,3)){ 4 | case 1: 5 | throw(type="com.mydomain.myapp.SomeException"); 6 | break; 7 | case 2: 8 | throw(type="com.mydomain.myapp.SomeOtherException"); 9 | break; 10 | default: 11 | throw(type="DifferentException"); 12 | break; 13 | 14 | } 15 | } catch(com.mydomain e){ 16 | writeOutput("com.mydomain exception caught: #e.type#"); 17 | } catch(any e){ 18 | writeOutput("Any other sort of exception caught: #e.type#"); 19 | } 20 | -------------------------------------------------------------------------------- /code/4/getters/Person.cfc: -------------------------------------------------------------------------------- 1 | // Person.cfc 2 | component { 3 | 4 | property firstName; 5 | property lastName; 6 | 7 | function init(firstName, lastName){ 8 | variables.firstName = arguments.firstName; 9 | variables.lastName = arguments.lastName; 10 | } 11 | 12 | function getFirstName(){ 13 | writeLog("#getFunctionCalledName()#() called
"); 14 | return variables.firstName; 15 | } 16 | 17 | function getLastName(){ 18 | writeLog("#getFunctionCalledName()#() called
"); 19 | return variables.lastName; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /chapters/11-orm.md: -------------------------------------------------------------------------------- 1 | # Basic database persistence with ORM # 2 | 3 | NOTES: 4 | Both CFML syntax, but general narrative of the intent of ORM: which is not a permanent solution, but a quick-to-market first version. 5 | 6 | Also discuss that domain designs relying on getter/setter paradigm is probably a poor one, although this is encouraged by ORM implementations. 7 | 8 | See if Sean wants to comment on Viet Nam. 9 | 10 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 11 | 12 | Don't submit pull requests for content yet. 13 | -------------------------------------------------------------------------------- /code/4/minimalImplicitAccessors/Person.cfc: -------------------------------------------------------------------------------- 1 | component accessors=true { 2 | 3 | property firstName; 4 | property lastName; 5 | 6 | function init(firstName, lastName){ 7 | variables.firstName = arguments.firstName; 8 | variables.lastName = arguments.lastName; 9 | } 10 | 11 | function setFirstName(firstName){ 12 | writeOutput("#getFunctionCalledName()#() called
"); 13 | variables.firstName = arguments.firstName; 14 | } 15 | function getFirstName(){ 16 | writeOutput("#getFunctionCalledName()#() called
"); 17 | return variables.firstName; 18 | } 19 | 20 | function setLastName(lastName){ 21 | writeOutput("#getFunctionCalledName()#() called
"); 22 | variables.lastName = arguments.lastName; 23 | } 24 | function getLastName(){ 25 | writeOutput("#getFunctionCalledName()#() called
"); 26 | return variables.lastName; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /code/6/finally.cfm: -------------------------------------------------------------------------------- 1 | 2 | writeOutput("Before the test code
"); 3 | try { 4 | switch (randRange(1,4)){ 5 | case 1: 6 | writeOutput("Throwing a CaughtException
"); 7 | throw(type="CaughtException"); 8 | break; 9 | case 2: 10 | writeOutput("Throwing a RethrownException
"); 11 | throw(type="RethrownException"); 12 | break; 13 | case 3: 14 | writeOutput("Throwing a UncaughtException
"); 15 | throw(type="UncaughtException"); 16 | break; 17 | default: 18 | writeOutput("Not throwing an exception
"); 19 | break; 20 | 21 | } 22 | } catch (CaughtException e){ 23 | writeOutput("Caught a #e.type#
"); 24 | } catch (RethrownException e){ 25 | writeOutput("Rethrowing a #e.type#
"); 26 | rethrow; 27 | } finally { 28 | writeOutput("This is run irrespective of anything else going on in this try / catch / finally block
"); 29 | } 30 | writeOutput("After the test code
"); 31 |
-------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Just some notes for contributors: 2 | 3 | * read the whole lot of what's there before creating issues for what should be covered. I've had a few issues raised for stuff that's already covered, which clearly demonstrates people are not doing the minimal due diligence before putting their oar in. 4 | * Try to "get" the narrative voice and match it when writing content. Obviously it'll not be possible for one person to write exactly like another person. My approach is "conversational" rather than "formal". Basically I write stuff exactly how I think it, rather than "versing my words". 5 | * Don't use tags in any code examples. 6 | * Use methods rather than functions in code examples. EG: `myString.len()`, not `len(myString)`. 7 | * all code examples should follow "clean code" practices. IE: should be brief and self-describing. If you need to add a comment, it's probably not "clean". 8 | * Code examples should stay focused: don't include any code not directly necessary to demonstrating the point. Examples will never require CSS, and other than in "tags"/"custom tags" chapter, never need markup. 9 | * Code must run on *ColdFusion 11*. Restrict Lucee examples to where it's absolutely necessary. 10 | * As indicated, don't write content for the empty chapters without first having a chat with me. 11 | * TBC 12 | 13 | -------------------------------------------------------------------------------- /chapters/12-mvc.md: -------------------------------------------------------------------------------- 1 | # MVC basics # 2 | 3 | NOTES: 4 | Start with something like Nolan's presentation at CFSummit 2015 re MVC without a framework, as I think that describes MVC really well. 5 | 6 | Show how an established framework like FW/1 can remove a lot of hand coding if a dev is tempted to DIY. 7 | 8 | 9 | For pull requests, initially let's just do section subheadings to first arrive at a structural consensus. 10 | 11 | Don't submit pull requests for content yet. 12 | 13 | 14 | ### FW/1 ### 15 | 16 | 17 | Usage FW/1 uses Application.cfc and simple conventions to provide an MVC framework in a single file. 18 | 19 | 20 | 21 | 22 | **/Application.cfc** 23 | 24 | ```cfc 25 | component extends="framework.one" { 26 | 27 | this.name= "Sample"; 28 | this.sessionManagement = "yes"; 29 | this.sessionTimeout = CreateTimeSpan(0, 0, 30, 0); 30 | 31 | variables.framework = { 32 | home = 'main.home', 33 | baseURL = 'useCgiScriptName', 34 | trace = isDebugMode() 35 | }; 36 | 37 | variables.framework.routes = [ 38 | { "main/{id:[0-9]+}" = "main/home/id/:id"}, 39 | { "main/home" = "main/home"} 40 | ]; 41 | 42 | } 43 | ``` 44 | 45 | **/framework** 46 | 47 | FW/1 files go here 48 | 49 | 50 | **/views/main/home.cfm** 51 | 52 | ```cfc 53 | 54 | 55 |

Welcome to FW/1, #getSafeHTML(rc.id)#

56 | ``` 57 | 58 | 59 | Documentation and Blog: 60 | http://framework-one.github.io/ 61 | 62 | 63 | Source Code and Examples for FW/1: 64 | https://github.com/framework-one/fw1 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note # 2 | 3 | The only real observation I'll make is the chapters here are very much just intended to be the draft notes for the text content. I'm not fussed about formatting or more "touchy feeling" elements yet. 4 | 5 | That'll come once I start the first round of editing. Whenever that might be. 6 | 7 | So if you want to mess with the formatting, that's *fine*, but perhaps a bit pointless as it won't be the final formatting anyhow. 8 | 9 | What I'm *really* after is technical editing: 10 | * is what I say accurate 11 | * is what I say understandable 12 | * is what I say the right way to go about saying it 13 | 14 | Cheers for any help! 15 | 16 | ## Current sit. rep. ## 17 | 18 | - [Housekeeping](chapters/00-housekeeping.md) - FIRST DRAFT 19 | - [Variables and expressions](chapters/01-commands.md) - FIRST DRAFT 20 | - [Control structures](chapters/02-flow_control.md) - FIRST DRAFT 21 | - [Built-in types](chapters/03-types.md) - FIRST DRAFT 22 | - [Components](chapters/04-components.md) - incl. remote usage (FYI only, as opposed to a recommended approach) - PARTIAL DRAFT 23 | - [Components cont'ed](chapters/05-components_conted.md) (properties, accessors) - PLACEHOLDER (might not be necessary) 24 | - [Error handling](chapters/06-error_handling.md) - FIRST DRAFT 25 | - [TDD](chapters/07-tdd.md) - PLACEHOLDER 26 | - [Clean code](chapters/08-clean_code.md) - PLACEHOLDER 27 | - [Request/response process & scopes](chapters/09-request_response.md) - the request/response stuff will be based on http://blog.adamcameron.me/2012/10/the-coldfusion-requestresponse-process.html - DRAFT IN PROGRESS 28 | - [Application framework](chapters/10-application_framework.md) - PLACEHOLDER 29 | - [ORM](chapters/11-orm.md) - PLACEHOLDER 30 | - [MVC basics](chapters/12-mvc.md) - PLACEHOLDER 31 | - [Tags](chapters/13-tags.md) - limited to which should be used in views: output, loop, if, etc. And # usage. - PLACEHOLDER 32 | - [Custom Tags](chapters/14-custom_tags.md) - PLACEHOLDER 33 | - [Advanced OO](chapters/15-advanced_oo.md) - possibly can be covered within earlier chapters. I'm thinking inheritance, interfaces, polymorphism etc here. I think the OO chapter breakdown needs some thought - PLACEHOLDER 34 | - [Advanced DB](chapters/16-advanced_db.md) - this will be `````` (or ```queryExecute()```!) and calling stored procs. Might drop this back - PLACEHOLDER 35 | - [Advanced ORM](chapters/17-advanced_orm.md) - HQL. Poss roll this into the "Advanced DB" one - PLACEHOLDER 36 | - [File system / HTTP / FTP](chapters/18-file_system.md) - PLACEHOLDER 37 | - [REST](chapters/19-rest.md) - PLACEHOLDER 38 | - [Function Expressions (and iteration methods)](chapters/20-function_expressions.md) - PLACEHOLDER 39 | - [Threads and locking](chapters/21-threading_locking.md) - PLACEHOLDER 40 | - [Java interaction](chapters/22-java.md) - PLACEHOLDER 41 | - [TBC](chapters/23-tbc.md) - PLACEHOLDER 42 | - [TBC](chapters/24-tbc.md) - PLACEHOLDER 43 | 44 | Currently MIA 45 | - defensive coding 46 | - basic debugging & metrics 47 | 48 | 49 | Things not covered 50 | ------------------ 51 | 52 | 1. Charting 53 | 2. Scheduler 54 | 3. PDFs 55 | 4. Images 56 | 5. Spreadsheets 57 | 6. Solr 58 | 8. Administration and deployment 59 | 21. WebSocket 60 | -------------------------------------------------------------------------------- /chapters/09-request_response.md: -------------------------------------------------------------------------------- 1 | # The CFML application server, the request/response process & scopes # 2 | 3 | This section covers - at a very "executive summary" level - how a CFML server "works", at least in the context of responding to page requests, anyhow. 4 | 5 | It also describes the data flow of a request from the time it's made from the client, across the network, being processed by the server, returned to the client, rendered and being made ready for consumption. Basically what happens from when one browses to a a web page through until the web page is displayed and operational. 6 | 7 | It also covers a (loosely) tangential concept: scopes. These are objects that have differing "lifetimes" in the context of a block of code, or a request, or a user's visit to a site etc. Scopes are what variables are stored in. 8 | 9 | 10 | ## The CFML application server ## 11 | 12 | This is not going to go into too much detail of the inner workings of Java servlet containers or that sort of thing: it's just a superficial look at what the CFML server does. 13 | 14 | The CFML server is a software application that runs within a JVM (Java Virtual Machine). The JVM basically executes programs written in Java byte code. Which programs it runs - in the case of CFML processing - is dictated by a servlet container (eg: Apache Tomcat) which is another JVM application which basically runs a bunch of threads which listen on TCP/IP ports for requests coming in from a web server (or any source, I guess; but in the context of CFML, it's a web server). The servlet container will be configured to check the pattern of the URL being requested (for example: its file extension is .cfm or .cfc), and pass processing the request off to a servlet. The CFML application is a servlet (well: it's more than one, but that's not important in this context). The CFML servlet then works out what CFML code - ie: your source code files - are needed to respond to the request, compiles (if necessary) and executes the code, and returns the resultant data to the servlet container. The servlet container then potentially does its own thing to finalise the response, then passes all of that back to the web server. 15 | 16 | Each request that comes into the CFML servlet runs on a thread, and it in turn can fire off additional threads as needs must. Once the request is completed, the thread is released and it waits for another request. The number of threads available to service requests is finite, the number being part of the CFML server's config. If the CFML server is set to use 50 threads, and it's currently processing 50 requests, then the 51st request will need to wait until one of the other threads finishes with its request and becomes available. 17 | 18 | The JVM itself runs continuously in the background, unlike some other language's mechanisms. For example with PHP, a new PHP process is run for every request, but it is shut down at the end of the request. JVMs execute code very quickly whilst they are running, but do take quite a while to initially start up (a number of seconds). Once it's running though, its resources are available for use by JVM applications until it's shut down. This becomes relevant when we come to discuss scopes, further down. 19 | 20 | 21 | 22 | ## The request/response process ## 23 | 24 | 25 | ## Scopes ## 26 | 27 | As mentioned above, scopes are objects into which variables go. There are several scopes that the CFML server maintains, and all variables go into one or other of these scopes. Each scope has two significant characteristics: 28 | 29 | 30 | ### Access/availability ### 31 | 32 | Most scopes are avaialble to all code. That said one should exercise good judgement and sound architectural practices in one's code, so whilst server-wide variables might be available to the code within a method within a class, this does not mean one should access it directly just because one can. One should encapsulate and decouple code and practise information hiding as much as possible in one's code. 33 | 34 | Code is executed in a context, and that context might be contained within other contexts. For example all code runs in the context of a JVM; within that a given CFML application also has its own context, and within that a visitor to the application (ie: browsing to the web site) can have their own session which sticks with them for the duration of their visit. From a code-centric point of view, a component's code is run in a seprate memory space (so: context) from the code that calls the component's code; and functions being executed likewise have their own context to store variables in which is separate again from the component itself, and the calling code. 35 | 36 | A couple of scopes have localised context, and variables within them are only accessible within that context. For example the context of the `local` scope within a function is only accessible to the code within that calling instance of that specific function. Other functions called within the first function have a separate local scope. And calling code has no access to a function's local scope. 37 | 38 | 39 | ### Lifetime ### 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /chapters/04-components.md: -------------------------------------------------------------------------------- 1 | # OO with CFML # 2 | 3 | ## Object orientation, but quickly ## 4 | 5 | Object oriented (OO) principles are pretty much outwith the scope of this book - other than discussing the basics - I'm going to assume you either already know about OOP, or will find out off your own bat. This book is about CFML, not about all the various other theoretical concepts one needs to have under one's belt to be able to use CFML. 6 | 7 | CFML provides a bunch of basic data types as discussed in [Built-in types](03-types.md): strings, dates, arrays, structs etc. All of these are implemented as objects which have properties (ie: values) and methods (ie: functions which perform actions). This is basic object orientation at work. 8 | 9 | I'll quickly run through some terms though. 10 | 11 | 12 | ### Class (/component) ### 13 | 14 | A class is the definition of some data and some behaviour that that data has to represent an object of some description. For some reason in CFML classes are called components, but I think that's pretty poor and slightly misleading nomenclature, so I'll stick with the term "class" in this text. 15 | 16 | In CFML, for all intents and purposes, a class is all the code within a CFC file (ie: a file with a .cfc file extension, eg: MyClass.cfc). In some languages classes have runtime manifestations (eg, Java has a class [java.lang.Class](http://docs.oracle.com/javase/8/docs/api/java/lang/Class.html), and one can instantiate Class objects and call methods on them). One cannot do this in CFML. So just think of the class or the component as the code that represents it. It does not really have any runtime representation. 17 | 18 | Classes comprise properties and methods (see below). 19 | 20 | Pretty much all code except for views (see [MVC basics](12-mvc.md)) should be implemented as classes. 21 | 22 | 23 | ### Object ### 24 | 25 | An object is a runtime instance of a class. A class defines how a given type of object works, then one creates instances of that class as objects, which we then use in our code. A Person class might define that a person has a `firstName` and a `lastName`. And object is a realisation of this for a given person, eg an object representing "Peter Quint" is created with a `firstName` of "Peter" and a `lastName` of "Quint". 26 | 27 | 28 | ### Property ### 29 | 30 | A property is a characteristic of an object: basically a data value that the differentiates between one object of a given type and another one. EG: a Person class might have `firstName` andd `lastName` properties defined. A given Person object might have a `firstName` property value of "Rohit" and a `lastName` of "Sharma". A second person might have a `firstName` of "Tony" and `lastName` "Underwood". One can think of properties as variables, really. 31 | 32 | Unlike other languages, properties are only private in CFML. There is no concept of a public property. Properties must be accessed via accessor methods. 33 | 34 | 35 | ### Method ### 36 | 37 | A method describes some behaviour of an object. Methods are implemented as functions. An example method for a Person object might be `getFullName()` which might return the firstName property value followed by the lastName property value, both separated by a space (eg: "Victor White"). 38 | 39 | Unlike other languages, CFML has only object methods, not class (or static) methods. 40 | 41 | 42 | ### Constructor ### 43 | 44 | The constructor method is called when a new object is created. This enables values passed to the constructor to be used to set the initial state of the object. In CFML, the default constructor method is `init()` (but this can be overriden; see below). 45 | 46 | ### Pseudo-constructor ### 47 | 48 | This is an odd idiosyncrasy of CFML. Code in a class definition which falls outside any method is executed when an object instance of the class is created. This is kind of like a constructor (and predates CFML having constructors), but has the drawback that it cannot receive any input arguments. In general, don't use the pseudo-constructor construct; just use the constructor. 49 | 50 | 51 | ### Accessors ### 52 | 53 | These are methods that simply access property values: either to simply return their values, or to set them (sometimes referred specifically as "mutators", but that's perhaps taking the jargon unnecessarily far). Accessors are convenient, but they should be used sparingly. Their usage does possibly indicate bad class design (see "Encapsulation" below, and the [Clean Code](08-clean-code.md) chapter. 54 | 55 | 56 | ### Method Access ### 57 | 58 | Methods can have differing access restrictions: 59 | 60 | * `public` methods are called on an object itself, from the calling code. For example `somePerson.getFullName()`. 61 | * `private` methods can only be called from which the class's own code. They are not accessible to the code that created the object instance. A private method can be accessed from the class's own code, or any class that extends the class that defines the private method. IE: a subclass can access its parent or other ancestor class's private methods. This is slightly different from other languages in which `private` methods are only accessible from the class that defines said method. 62 | * `package` methods can be accessed from any class that is homed in the same package (in CFML this is just based on the directory the class is defined in, or one of its subdirectories). 63 | * `remote` methods can be accessed directly via HTTP requests made to the class file, using syntax http://example.com/path/to/SomeComponent.cfc?method=nameOfMethod. 64 | 65 | Note that CFML doesn't have the notion of `protected` methods. Well it does, but it misuses the term `private` to reflect the same thing other languages would use the notion of `protected` for. In reality, it does not actually have the concept of truly `private` methods in the sense other languages might have, ie: methods only available to the exact class that defines them, and no others. 66 | 67 | As touched on above, CFML does not have different access levels on properties. They are all `private` (using the CFML meaning of `private`, not the commonly accepted usage of that word). 68 | 69 | 70 | ### Inheritance ### 71 | 72 | If one class would represent a "fine-tuning" or a more precise or specialised implementation of another class, then it could be made to `extend` that parent class. This enables code to have class hierarchies. For example there might be a Shape class, which is extended by a TwoDimensionalShapre class, which is extended by a Triangle class. Or the situation might be that a Manager *is a*n Employee, which *is a* Person: in this case Manager would extend Employee, which would extend Person. This allows the more specialised classes to still share behaviour (properties and methods) of their "base class". 73 | 74 | CFML only supports single inheritance. 75 | 76 | 77 | ### Overriding and overloading methods ### 78 | 79 | A parent class might implement a method for a broader solution than the requirement that a subclass might need. A Person class might define the `getFullName()` method as returning "#firstName# #lastName#", whereas for a subclass of Person - Employee - might instead need to define `getFullName()` to return "#lastName#, #firstname#". In this case Employee implements its own `getFullName()` method which *overrides* the parent class's equivalent method. 80 | 81 | In a strongly typed language it's possible to implement same-named methods which accept different combinations of argument types. This is method *overloading*. Because CFML is both loosely typed and implements variadic methods (ie: they can take a variable number of arguments), it would be difficult to implement method overloading reliably, so it doesn't bother. 82 | 83 | 84 | ### Interfaces ### 85 | 86 | Classes can implement *interfaces* which allow allow type-checking on partial behaviour. Interfaces allow a kind of multiple inheritance, where a class is made to "implement" and interface, which means it will definitely have a certain subset of methods to describe a certain behaviour. For example a class might be "Iterable" (it can be looped over, say by implementing a `next()` method, etc) or "Comparable" (implementing an `equals()` method). 87 | 88 | 89 | ### Polymorphism ### 90 | 91 | If class - Manager - extends class Employee which then extends class Person, then anywhere a Person is needed, a Manager object is valid, because whilst being a Manager, it is *also* a Person (and an Employee, for that matter). 92 | 93 | Similarly, say a `sort()` method expects an array of `Comparable` objects (ie: they implement the `Comparable` interface), then it doesn't matter what actual class the objects are, provided they implement the `Comparable` interface. 94 | 95 | This ability to use more specific types of object in situations calling for a less specific kind of object is "polymorphism". 96 | 97 | 98 | ### Encapsulation ### 99 | 100 | One driving concept of good OO is that a given class - well its methods really - only ever works with: 101 | * exitsing property values within an object; 102 | * other variables internal to an object instance representing the state of the object; 103 | * argument values passed into methods that act on properties or other variables. 104 | 105 | Basically the idea is that the code in a class should only work with values defined by its own code (or code in ancestor classes, in the case of inheritance). A class defines a public interface (ie: its public methods), and that should be the only way any values can be passed into an instance of that class. 106 | 107 | The reason for this is that if a class directly accesses materiel from its calling code's context then the class can only really operate in that specific situation, and can't easily be reused elsewhere. A class *should* be usable in any context, and not rely on the state of any calling context. 108 | 109 | Encapsulation extends beyond that. Often times a class will be implemented with accessors for all its properties, and calling code will apply additional logic to those property values, having extracted them from the object they originally came in. *Sometimes* this is OK, but generally this suggests that the class's design is incomplete (or "anaemic") because external code is necessary to apply all the necessary logic to the properties. There's a balance to be made here, but alarms ought to go off whenever one is accessing an object's properties by accessors which simply get the raw property value. 110 | 111 | ### Common OO concepts CFML doesn't implement ### 112 | 113 | * Multiple inheritance 114 | * Method overloading 115 | * Mixins or traits 116 | * Abstract classes or methods 117 | * Class or static methods or properties 118 | * Destructors 119 | * Final classes, methods or properties 120 | 121 | ### Summary of OO terms ### 122 | 123 | That was rather less brief than I initially meant it to be. And sorry for not having shown even a single line of code so far in this chapter! 124 | 125 | There is an endless number of OO terms out there which I have not mentioned here, one way or the other. But I've covered the ones that are important to CFML, and made it clear which common concepts CFML doesn't implement. 126 | 127 | CFML's OO implementation is reasonably fully-featured when considering the requirements and behaviour of dynamic languages, compared to the usually more rigid language rules of more staticly typed languages which might benefit from some constructs that CFML simply doesn't need. 128 | 129 | 130 | ## Components ## 131 | 132 | A class is defined via a CFC file (**C**old**F**usion **C**omponent). Only a single class can be defined per file, and the class definition is the only code allowed in the file. This is as opposed to other languages which allow multiple classes to be defined in a single source code file, and other procedural code to also reside in the same file. 133 | 134 | The syntax for defining a class is: 135 | 136 | ````cfc 137 | component { 138 | 139 | } 140 | ```` 141 | 142 | One does not need to specify the name of the class as it's inferred from the file name. 143 | 144 | A class definition can have a number of modifying attributes which are specified as name/value pairs similar to how an HTML tag might specify attributes, eg: 145 | 146 | ````cfc 147 | component extends=ParentClass implements=SomeInterface { 148 | 149 | } 150 | ```` 151 | 152 | It's slightly unorthodox to have to specify the `=` in there, but this is because one can specify any sort of metadata in the component declaration, and the only way the compiler can work out what's an attribute and what's its value is by having the syntax `attribute=value`, eg: 153 | 154 | ````cfc 155 | component specialAnnotation=annotationValue { 156 | 157 | } 158 | ```` 159 | 160 | If the value has embedded spaces, one needs to quote it: 161 | 162 | ````cfc 163 | component specialAnnotation="Annotation Value" { 164 | 165 | } 166 | ```` 167 | 168 | This sort of metadata can be used in any way. Often development frameworks will use annotations for special bespoke extensibility. The metadata can be fetched by calling `getMetadata()` on an object: 169 | 170 | ````cfc 171 | object = new WithAnnotation(); 172 | writeDump(getMetadata(object)); 173 | ```` 174 | 175 | Which will yield something along the lines of: 176 | 177 | 178 | 179 | (see the bespoke metadata as that penultimate entry there). 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /chapters/00-housekeeping.md: -------------------------------------------------------------------------------- 1 | # Housekeeping # 2 | 3 | ## 0 ## 4 | 5 | First things first: why is this chapter 0? Mostly because the stuff here is nothing to do with learning CFML, it's just the usual wittering on one gets at the beginning of a book. Skip as much of it as you like, but the last section covers getting the development environment I'll be using in the code example sorted out. 6 | 7 | 8 | ## Why? ## 9 | 10 | Why am I writing this book? Good question. Basically I think CFML has moved on faster than most of the the learning materials out there have, and those materials are teaching a very old-fashioned way of approaching CFML-written applications. They probably do our community more harm than good by encouraging bad, primitive coding practices. I figure if I take a contemporary approach it will encourage everyone - existing CFML developers and newbies alike - to move on. 11 | 12 | 13 | ## What issues am I addressing? ## 14 | 15 | For too long, CFML code was approached from a web-page-centric and tag-based-code-centric perspective. This was fine in 90s and earlier part of this century, but time has moved on, so has CFML, and that approach amounts to "bad practice" these days. You will be learning CFML using almost entirely script syntax, relegating tags to the section on views. In earlier versions of CFML one did still need to fall back to tag-based code for business logic code rather than just view code, but this has not really been the case for a few years now. It's time to accept this. 16 | 17 | 18 | ## CFML? Tags? Script? Huh? ## 19 | 20 | The previous paras perhaps won't mean much to someone who is completely ignorant of CFML. But then again I doubt anyone who is completely ignorant of CFML will be reading it. If you are... at least go read the Wikipedia page on CFML (http://en.wikipedia.org/wiki/ColdFusion_Markup_Language) to get a handle on its history and general information about it. This is not a history book, so I'm not covering that stuff. Except for a bit of context, a few paras below. 21 | 22 | As it currently stands, CFML is a web-centric, loosely and dynamically typed language aimed at developing web-based applications and web sites. It arrived on the scene around about the same time as PHP, so it's one of the founding members in that space. 23 | 24 | Typical CFML code looks similar to most "curly-brace" languages: 25 | 26 | ``` 27 | name = "Zachary"; 28 | writeOutput("G'day #name#"); // G'day Zachary 29 | ``` 30 | 31 | In this example I demonstrate setting a variable `name`, and outputting as part of an interpolated string, resulting in *G'day Zachary* 32 | 33 | Most CFML code should be written using this "script" syntax, however CFML also has a tag-based syntax used in view files. The equivalent tag-based code for the above example would be: 34 | 35 | ``` 36 | 37 | G'day #name# 38 | ``` 39 | 40 | It has a resemblance to HTML, and is designed to integrate and intermingle with inline text such as HTML within a view file. 41 | 42 | The language is processed by a Java servlet running on Tomcat or JBoss or some other servlet container, which compiles CFML source code to Java byte code, and executes it. This all happens when a request is received by a web server, and the web server has been configured to pass requests to the servlet container and onto the CFML servlet. The CFML servlet works out what code it needs to fulfil the request, compiles it, runs it, and returns any generated data (say: HTML or JSON) to the web server. The web server then sends that back to the client browser to do with it what it will. 43 | 44 | It's important to note that CFML is a language, it is not a servlet in itself. At present, there are two main vendors who provide a CFML-processing servlet: Adobe and Lucee. 45 | 46 | Adobe has a closed-source, paid for product - ColdFusion - which is aimed at the enterprise market. Adobe inherited the ColdFusion product from their purchase and subsumption of Macromedia; Macromedia had previously done the same thing to Allaire, the original creators of "Cold Fusion". ColdFusion is not a flagship product of Adobe, and it has mostly been languishing in one of their backwater development departments since it became an Adobe product. It does seem occasional development, but the impression I get is that Adobe only persist with ColdFusion whilst the client-base they purchased as part of Macromedia continue to pay their licence renewals. ColdFusion is currently at version 11. Adobe release a new version of ColdFusion every couple of years. In the interim they will release one or two service packs for the currently-supported ColdFusion releases (only versions 10 and 11 are currently supported; support for version 9 ended at the end of 2014). 47 | 48 | Railo was a company who lead the Railo open source project. The Railo solution was free and open source, and was more actively developed than ColdFusion. In its early days Railo was positioned as a free alternative to ColdFusion, and definitely positioned itself in ColdFusion's larger shadow. Because it was an open source project, Railo is continuously releasing new version of the language, both adding new features and fixing bugs. 49 | 50 | Whilst I was writing this the Railo project basically ended, and was replaced by Lucee. The reasons for this are outwith the remit of this book. 51 | 52 | Lucee is currently on version 4.5. Note that the ColdFusion version number and the Lucee version number bear no relation to each other (this is sometimes confusing). 53 | 54 | There is another CFML-esque implementation: Open BlueDragon. I mention that only for completeness: it is mostly irrelevant these days, and this will be the only mention of it in this book. 55 | 56 | For many years the ColdFusion product was the only CFML servlet available, hence sometimes "ColdFusion" is used to mean "CFML". However ColdFusion is a servlet (well: a bunch of servlets, re-licensed third party Java libraries and an administration UI), and CFML is the language. This book is about the language CFML, not about ColdFusion. However in this book I will be using ColdFusion 11's implementation of CFML for my code examples. Where possible I will use code that will run on both ColdFusion 11 and Lucee 4.5. I will mention vendor differences only when it's necessary for the example. I hope that one will be able to focus on the code and not worry about the vendor as much as possible throughout this book. 57 | 58 | 59 | ## Cameron ## 60 | 61 | Who am I? I'm Adam Cameron and I've been a CFML developer since early 2000. Throughout most of that time, I have been solely a CFML developer, with just a smattering of JavaScript on the site. I have been an active participant in the CFML community for that long too: helping people with problems via various Q&A forums, helping Adobe on the ColdFusion Pre-Release Program, and also putting my oar in in the Lucee community too. I know CFML pretty well, and figure my take on it is possibly worth writing down. I primarily used ColdFusion (version 9) for my day job. I say "used" in the past tense as I now mostly work with PHP, but I remain part of the CFML community. 62 | 63 | 64 | ## You ## 65 | 66 | I'm writing this from the ground up, so should be suitable for a newbie to CFML. I will kind of assume a certain pre-knowledge of programming terminology in places though. I also hope it'll be beneficial for existing CFML devs to read as well. Hopefully I can teach old dogs new tricks too. Or at least get them snarling in disagreement with me. 67 | 68 | 69 | ## 24 hours ## 70 | 71 | Well... take that with a grain of salt. It's a cliched title, but it sets the scene as to the general approach I shall take. I am not going to time how long it takes to read and absorb each chapter, but I will be breaking sections down into bite-sized chunks. That said, you should probably assume the book-based part of the learning process might take an hour per section, but it's then over to you to actually learn it. One cannot learn anything from just reading a book, these pages are just information dissemination. It's up to you to then take that information and learn from it. The code examples will be minimal: you're expected to then write your own code and experiment sufficiently to become comfortably with it. As one of my tutors at polytech (back in the early 1990s this was) was wont to say "I'm not here to teach, you're here to learn". 72 | 73 | Also I shall not be covering everything in CFML in this book. I will be teaching the syntax, data types, constructs, and elements of functionality sufficient to get you productive. This is not a reference (as some tomes tend to be), there is online documentation for that. There's also an awful lot of CFML that one simply doesn't need to know. There's perhaps 500 top-level functions in the CFML language, and I've probably not used half of them. And there's possibly 100 tags and you should only ever used about *a dozen* of them. Are the rest of them just faciliate bad practice. There is no value in knowing a lot of this stuff. There is also no value in teaching about them all, because a function is a function... once one knows how to use functions, one can then use the online docs to reference the rest of them. 74 | 75 | Similarly stuff like generating images, PDF files, charts, what-have-you - all of which CFML has support for - is tangential to the core language, and I don't think there's any need to discuss that sort of thing here. I'm going to get you a good handle on the core of CFML, and that'll enable you to work the rest out for yourself. 76 | 77 | I'm also not going to waste time discussing how to install (beyond superficially) or administer ColdFusion: this is already well documented by Adobe. I'm not going to discuss how to install a database server: again, the vendors have done this. I'm not going to specifically discuss HTML, HTTP, or ColdFusion maintenance or security. None of that stuff has anything to do with the CFML language, and there are better resources around for all that stuff. I hasten to add this is all vital information for you to know, but it's not my job here to teach you it. This is about CFML. This book will cover enough to get you learning CFML, and no more. As I said: it's up to you to follow-up in those other areas by yourself. 78 | 79 | 80 | ## IDE? ## 81 | 82 | You don't need one. I don't use one. All you need is a text editor, although one with CFML support might be helpful once you move on from just a few lines of code. For my coding at work I use [SublimeText](http://www.sublimetext.com/), with the [CFML plug-in](https://packagecontrol.io/packages/CFML). Whilst CFML needs to be compiled to run, this is all handled via the servlet, when the code is requested. It's transparent from your perspective: there is no build process or anything like that, and you never even see the compiled files. The servlet takes care of all that for you. All you need to do is save CFML code to a file, then use a browser to browse to it which will in turn result in the ColdFusion server doing everything else automatically. 83 | 84 | 85 | ## Running the code ## 86 | 87 | To run the code you will need to download and install [ColdFusion Express](https://www.adobe.com/products/coldfusion-family.html#content-dotcom-en-products-coldfusion-family-bodycontent1-ttt-1). 88 | 89 | ### Installing ColdFusion Express ### 90 | 91 | It's just a zip file. So unzip it somewhere. I usually unzip it in `C:\apps\adobe\coldfusion\11\express`, but I mostly have that level of isolation because I have a lot of different CFML installs from various vendors running at once (eg, I also have `adobe\coldfusion\11\enterprise`, `lucee\4.5`, `railo\4.2` all in that apps directory). 92 | 93 | ### File locations ### 94 | 95 | Occasionally - like in the paragraph following this one - I will refer to files within the ColdFusion install directory. In my case, as per above, my install directory is `C:\apps\adobe\coldfusion\11\express`, and I will refer to paths relative to that like this: `[coldfusion]\some\path\here\'. 96 | 97 | I will also occasionally refer to files in the web root. By default this is in `[coldfusion]\cfusion\wwwroot`. I'll refer to that as `[webroot]`. The web root can be configure to be anywhere, but that's outwith the scope of this discussion here. I'll discuss best practices for how to home files later (CHAPTER TBC). 98 | 99 | I'm afraid you *nix people that I am a perennial Windows user, so everything I do here will be Windows-centric, in the few situations it is relevant (which is hardly ever, in this book). You're a clever bunch so you'll be able to translate my Windows instructions to *nix ones. And if you can't: more fool you for using *nix ;-) 100 | 101 | ### Running ColdFusion Express ### 102 | 103 | 1. start a command prompt. 104 | 2. execute `[coldfusion]\cfusion\bin\cfstartup.bat` 105 | 3. that'll start spewing a whole lot of bumpf out on the screen, which should culminate in something like this, eventually: 106 | 107 | ``` 108 | INFO: Server startup in 15223 ms 109 | ``` 110 | 111 | (This laptop is old and slow, so hopefully yours will be a bit faster than that). 112 | 113 | Note it might not be the last thing that displays, you're likely to get an error about `PDFgServlet`, but don't worry about that. It's related to a ColdFusion feature we'll not be needing for our purposes, and it's indicative of anything going wrong anyhow. 114 | 115 | By default, the web server ColdFusion Express runs listens on port 8888, so the best way to check if ColdFusion is up and running is to browse to http://localhost:8888/CFIDE/administrator. If that presents a login screen: you're up and running. If it gives an error, then you have some troubleshooting to do: go to Google. Well: before going to Google, the CLI might have output an error for you, so grab that and google it. This book is not about troubleshooting ColdFusion installs, and there's stacks of resources out there to cover this, so it should be easy enough to get it. 116 | 117 | If ColdFusion is up and running, you can quickly test your install by doing this: 118 | 119 | 1. create a text file containing this code: 120 | 121 | ``` 122 | 123 | ``` 124 | 125 | 2. Save it as `[webroot]\serverDump.cfm` (or save it within a subdirectory if you want to organise stuff better: up to you). All basic CFML script files should be saved with a .cfm extension. 126 | 127 | 3. browse to http://localhost:8888/serverDump.cfm. You should get something like this: 128 | 129 | 130 | 131 | (I've excised some stuff from that, but you get the idea). 132 | 133 | 4. If you see something like that: you're good to go. 134 | 135 | 136 | ### Running the code in this book ### 137 | 138 | In general, to run CFML code: save it in a file, and then browse to the URL of that file. There's more to it than that, but for the purposes of running some code examples: that's enough knowledge to work with. 139 | 140 | The code samples in this book *might* be complete enough to simply copy and paste into a file, save it, and browse to it to see the results, but I make no promises in this regard. A lot of teaching material falls over itself to provide self-contained code examples so that that approach works all the time. This engenders a bit of laziness on the part of the person doing the learning, so I'm not going to make a point of encouraging that laziness. You're reading this to learn CFML, so this will mean active effort on your part, which might mean giving some thought to earlier material to write code which will set the ground work for a given code snippet to work. Now... I will *attempt* to make the examples stand-alone, but this won't always be the most expedient way to get the point across, so... well... be warned. Some effort on your part to facilitate your own learning might be require here. In general one learns by *doing*, not simply reading, or not simply by following instructions. I'm doing you a favour here, really. Honest. 141 | 142 | One thing I will try very hard not to do is to require you to flick *forward* in the book to understand something. All code examples should only ever build on what you've already done. I will however assume that once I've said / demonstrated something that you've taken the time to remember / understand it. 143 | 144 | Also for any given code example, I reckon you're well placed to mess around with it a bit to try different variations of what I demonstrate, and see what happens: understanding the variations of how code can work is gold when understanding it. I will also presuppose you will take it upon yourself to RTFM on any statement, function, tag etc I mention. As I said earlier: this is a not a reference... the docs are for that. You need to read the docs. You. Need. To. Read. The. Docs. Also I'll deal with general programming concepts which are not specific to CFML. Go read up on those too. EG: when I talk about `switch` statements in the [Flow control structures](02-flow_control.md) chapter, go read what Wikipedia has to say about [switch statements](https://en.wikipedia.org/wiki/Switch_statement). It's really worth properly *understanding* these concepts. 145 | 146 | ## OK... ## 147 | 148 | ... let's get on with it. 149 | -------------------------------------------------------------------------------- /chapters/01-commands.md: -------------------------------------------------------------------------------- 1 | # Commands/statements/expressions, variables, operators # 2 | 3 | I'll not coddle you too much, and assume you know about variables, expressions and operators, what they're for etc. If for some reason you don't, you're probably not doing yourself any favours by reading this book before doing some basic "Programming 101" sort of study before reading language-specific guidance. But good on you for trying. Maybe you can pick it all up from reading this lot, but it's not the way I'd approach that particular learning exercise. 4 | 5 | 6 | ## Commands / statements / expressions ## 7 | 8 | Commands, statements and expressions are the basic units of programming. All are units of work, and the distinction is generally a syntactical one. Wikipedia makes a good distinction bettween statements and expressions (http://en.wikipedia.org/wiki/Statement_%28computer_science%29#Expressions): 9 | 10 | * statements are a unit of work - they have side effects (where the main side effect is "your program") - and don't return anything 11 | * an expression returns a value, but otherwise doesn't have side effects. 12 | 13 | I've never thought of it that way, but I think it's a reasonable approach to CFML coding. I also add the notion of a command, which is a basic CFML construct like "if" or "return" or "case" etc. 14 | 15 | To me, a statement in CFML is an instruction to the program to do something. And an expression is a value used to do something. An expression can - internal to its implementation - contain various statements; a statement is the basic work unit. And commands are the native CFML bits which it intrinsically knows what to do with. 16 | 17 | I suppose statements apply an expression to a command? I'll have to think about that. It's tricky because the CFML docs don't actually make these distinctions, so one has to infer the language building blocks from behaviour. This makes documenting it less straight forward. 18 | 19 | One important thing to note is that all CFML constructs are case insensitive. This includes statements, function/method names, variable names. Basically everything. 20 | 21 | 22 | ### Commands ### 23 | 24 | These are the baseline language constructs like "return", "if", "case" etc. They define program flow, and represent low-level language operations which effect statements, and use expressions. 25 | 26 | 27 | ### Statements ### 28 | 29 | A variable assignment is a statement: 30 | 31 | ```cfc 32 | myVar = someValue; 33 | ``` 34 | 35 | Statements are represented by one of three syntactical constructs: 36 | 37 | 38 | #### Execution #### 39 | 40 | ```cfc 41 | return a; 42 | ``` 43 | 44 | This gives a value to a command, and the command does something with it. In this case, the return command takes the value and transfers it back to the calling code. 45 | 46 | 47 | #### Assignment #### 48 | 49 | ```cfc 50 | a = b; 51 | ``` 52 | 53 | The left-hand side can be null if one is solely interested in what b does, eg: 54 | 55 | ```cfc 56 | someObject.aMethod() 57 | ``` 58 | 59 | This acts on the object, but doesn't return a value. 60 | 61 | 62 | #### Flow control statement #### 63 | 64 | ```cfc 65 | if (condition) statement(s) 66 | ``` 67 | 68 | When it's a single statement: 69 | 70 | ```cfc 71 | if (condition) return; 72 | ``` 73 | 74 | Or a block of code: 75 | 76 | ```cfc 77 | if (condition){ 78 | // multiple statements 79 | } 80 | ``` 81 | 82 | Statements either end with a semicolon: 83 | 84 | ```cfc 85 | a = b; 86 | return a; 87 | ``` 88 | 89 | Or a block of statements if the command allows it: 90 | 91 | ```cfc 92 | if (a){ 93 | // multiple statements 94 | } 95 | ``` 96 | 97 | Note that the Lucee CFML implementation make semi-colons optional in almost all situations, but not in some ambiguous situations. My recommendation is to always use them, as the rules as to when they are (/not) needed are poorly defined, so it's a matter of trial and error in one's code. 98 | 99 | 100 | #### Statement blocks and variable scope #### 101 | 102 | In some languages, a variable declared within a statement's block is localised to that block. This is not the case in CFML: CFML has no block-level variable scope. 103 | 104 | 105 | ### Expressions ### 106 | 107 | An expression is a construct that returns a value. Ideally (but only ideally) it has no side effects beyond that. 108 | 109 | These are expressions: 110 | 111 | ```cfc 112 | "day after tomorrow: " 113 | 1+1 114 | now() 115 | "day after tomorrow: " & dateAdd("d", (1+1), now()) 116 | ``` 117 | 118 | A basic string returns a value: the contents of the string. An arithmetic expression returns the answer. A function call returns a value. And any number of expressions can be chained together to form another expression. Each sub-expression's result is used as a step in the main expression. 119 | 120 | As a rule of thumb, an expression is a representation of a value. Any time there can be a value, it could be an expression. The only rule is that ultimately, the value of the expression needs to be appropriate to what the next expression is expecting. In my example above, dateAdd() needs a string value as its first argument (and there are rules for the values that string can be), a numeric value for its second argument, and a date/time for its third argument. Then the dateAdd() call itself is an expression too. 121 | 122 | Also bear in mind that the key part of control statements (if, for, switch, etc) is also an expression: 123 | 124 | ```cfc 125 | if (someValue) // do something 126 | ``` 127 | 128 | Here "someValue" is an expression. It simply needs to return a value that is boolean. 129 | 130 | 131 | ### Functions? ### 132 | 133 | How do functions - either CFML's own ones, or your own - fit into this? The definition of the functions comprise commands (eg: "`function`") and statements ("```function f(x){/*statements*/}```"). And when you call a function (```f(1)```) it's an expression. You might have a statement that calls a function and assigns the result to a variable(```y = f(1);```). 134 | 135 | To muddy the waters, functions can also be declared via function expressions! 136 | 137 | ```cfc 138 | // declaring function f() via a function statement: 139 | function f(x){ 140 | return x; 141 | } 142 | 143 | // defining a function via function expression, and assigning it to a variable f: 144 | f = function (x){ 145 | return x; 146 | }; 147 | ``` 148 | 149 | There's reasons to use either of these appraoches, but that's for a later chapter. 150 | 151 | 152 | ### Summary ### 153 | 154 | I think all code is pretty much made of statements which apply expressions to commands. With varying degrees of optionality along the way. It might seem like semantics, but being able to identify the individual parts of your code will make it easier to understand the bigger picture of what's going on. To be frank... before writing this stuff down, I didn't have a fully-formed understanding of how it all comes together. 155 | 156 | 157 | ### Warning ### 158 | 159 | Both Lucee and ColdFusion's dialects of CFML have constructs that defy reasoning, and might look like expressions but are actually commands, or statements. I'm going to avoid these non-standard constructs if possible, as they are generally avoidable when coding. I might need to add a "caveats" section to this book at some stage (my writing process is very stream of consciousness, and this is only chapter one, so I haven't yet analysed how much discussion this topic needs!) 160 | 161 | 162 | ## Variables ## 163 | 164 | OK, so in CFML the basic way variables work is the same as any other language: 165 | 166 | ```cfc 167 | someVariableName = someValue 168 | ``` 169 | 170 | That said, as with all language-specific implementations, there are some rules and practices. Variable names in CFML can be anything. !€#☆ is a valid variable name. However you're making your life more difficult than it needs to be by not limiting yourself to a subset of all possible typographical glyphs. There are three (hmmm... two and a half, perhaps) syntax variations. And only one of them will let you call a variable anything. 171 | 172 | 173 | ### Basic ### 174 | 175 | The basic syntax is as demonstrated above: 176 | 177 | ```cfc 178 | someVariable = someValue 179 | ``` 180 | 181 | Using this unqualified syntax, one needs to follow basic variable-naming rules. These are in the docs ([find link]), but currently they are: 182 | 183 | > 1. can be any combination of upper and lowercase roman alphabet characters, digits, currency symbols and/or the underscore character. 184 | > 2. that notwithstanding, cannot start with a digit 185 | 186 | Bearing that in mind, these are valid: 187 | 188 | ```cfc 189 | a = 1; 190 | B = 2; 191 | _ = 3; 192 | £ = 4; 193 | ``` 194 | 195 | This is invalid: 196 | 197 | ```cfc 198 | 5c = 5; 199 | ``` 200 | 201 | One other thing to note: CFML is completely case-insensitive. a variable "myVar" is the same as "myvar" or "MYVAR" (except people reading your code will hate you if you use all caps. The CFML runtime won't care. If you want to use all caps, go get a job programming COBOL). 202 | 203 | Another thing to note is that in CFML one has to assign a variable a value when declaring it. One cannot simply declare a variable, thus: 204 | 205 | ```cfc 206 | myVar; 207 | ``` 208 | 209 | One needs to give the variable an initial value, eg: 210 | 211 | 212 | ```cfc 213 | myVar = ""; 214 | ``` 215 | 216 | 217 | ### Dot notation ### 218 | 219 | When referencing variables in a specific scope (see relevant chapter), or properties of a specific object, the most syntactically-clear syntax is dot notation: 220 | 221 | ```cfc 222 | session.isLoggedIn 223 | person.firstname 224 | ``` 225 | 226 | One uses the dot-operator to separate a variable or property (or method) from its scope or object. And if one is using dot notation, the same basic variable rules as stated above apply. 227 | 228 | 229 | ### Associative array notation ### 230 | 231 | This is also known as "bracket notation". Its a more "forgiving" notation than dot notation, as the key provided in the brackets can be any string: 232 | 233 | ```cfc 234 | variables["!€#☆"] = "completely valid variable name"; 235 | ``` 236 | 237 | This might seem fairly contrived - my example certainly is - but it opens up two possibilities. Firstly, data doesn't always originate from within the CFML system itself. Database columns can have odd names (with spaces, pound signs, etc), and different languages one might be interconnecting with with CFML will have different naming rules too. This is CFML's way of dealing with that. 238 | 239 | 240 | #### Dynamic variable names #### 241 | 242 | Associative array notation also allows for completely dynamic variable names. The value in the square brackets is just a string, so it can also be a variable containing a string. Or indeed any expression which returns a string. For example: 243 | 244 | ```cfc 245 | myVarName = "foo"; 246 | variables[myVarName] = "bar"; 247 | ``` 248 | 249 | This sets a variable with a name of `foo` to have a value of `"bar"`. 250 | 251 | [insert dump here] 252 | 253 | Or one could use a function which returns a string as the variable name: 254 | 255 | ```cfc 256 | variables[createUuid()] = "very unique variable name"; 257 | ``` 258 | 259 | [insert dump here] 260 | 261 | And given CFML is loosely-typed, any expression which could be interpretted as a string could be used: 262 | 263 | ```cfc 264 | variables[now()] = "a variable with the current timestamp as its name"; 265 | ``` 266 | 267 | [insert dump here] 268 | 269 | These examples are all very contrived, but show the general technique. The take-away here is that when using associative array notation, the variable name can be anything. 270 | 271 | Another important benefit of associative array notation is that the case of the variable name is preserved. CFML will still treat it case-insensitively, but when data-interchanging between other systems - via say JSON to JavaScript code - it becomes important for variable names to be case-sensitive. 272 | 273 | 274 | ### Coding style ### 275 | 276 | In general, use basic notation or dot notation, and - accordingly - simple variable names. In your own CFML code, stick to the rules. Reserve associative array notation for these situation: 277 | 278 | * you are interchanging data with other systems with different variable-naming rules, 279 | * or are case-sensitive. 280 | * If you need to reference a variable name dynamically, for whatever reason. 281 | 282 | Sticking to these rules make your code more clear. Use the most restrictive syntax where possible (basic notation), only using more complex notation - dot notation or associative array notation - when necessary. 283 | 284 | 285 | ## Operators ## 286 | 287 | I discussed above how one expression can be built from other expressions. The missing piece of the puzzle is how expressions are joined together. Operators. 288 | 289 | If you stop to think about it: operators exist to apply an operator to one or more expressions. Or when specifically considering operators, they apply an operation to one or more operands (the operands are expressions). 290 | 291 | 292 | ### Unary operators ### 293 | 294 | These are applied to a single operand. 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 310 | 311 | 312 | 313 | 318 | 319 | 320 | 321 | 327 | 328 | 329 |
OperatorPurposeExamples/observations
-Switches the sign of a numeric operand 305 |
x=1;
306 | y = -x; // -1
307 | x = -1;
308 | y = -x; // 1
309 |
!Performs a boolean inverse on the operation 314 |
!true // false
315 | !false // true
316 | 317 |
++, --Short hand increment / decrementThese either increment (++) or decrement (--) the operand.
Unlike other operators, they can be applied either as a suffix (or to linguistically challenged people: "postfix") (i++) or a prefix (++i).
The difference is the prefix version applies the operation and then returns the result; whereas the suffix returns the value then applies the operation. See examples for the difference. If one considers the earlier notion that an expression ought not have side effects, then the suffix versions are less than ideal, in a way, as they return one value, but the operand they act on ends up with a different value.
Using the suffix version is so ubiquitous that I'd not worry about this too much
322 |
x = 1;
323 | y = ++x; // y=2, x=2
324 | z = x++; // z=2, x=3
325 | 
326 |
330 | 331 | 332 | ### Binary Operators ### 333 | 334 | These take an operand either side of the operator: 335 | 336 | ```cfc 337 | operand operator operand 338 | ``` 339 | 340 | eg: 341 | 342 | ```cfc 343 | 1 + 2 344 | true || false 345 | ``` 346 | 347 | These are all well-documented, so what I'll advise here is a winnowing of the wheat from the chaff. For example: use the docs to be aware of the ```LESS THAN OR EQUAL TO``` operator (no joke!), but never use it. 348 | 349 | Use these ones: 350 | 351 | 352 | #### Arithmetic #### 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 |
OperatorPurposeExamples/observations
+, -, /, *As you'd expect[I won't insult your intelligence]
%Remainder5 % 2; // 1
+=, -=, *=, /=, %=Shorthand operatorsShorthand equivalents of above operators when acting on the same variable on both sides of the statement, eg: x += 2 is equivalent to x = x + 2. This is purely syntactic sugar
^Exponent2 ^ 3; // 8
373 | 374 | 375 | #### String #### 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 392 | 393 | 394 |
OperatorPurposeExamples/observations
&ConcatenateConcatenates two string operands
greeting = "G'day " & "world"; // "G'day world"
&=Shorthand 387 | As per the arithmetic equivalents:
388 | s &= "a"
389 | is equivalent to:
390 | s = s & "a" 391 |
395 | 396 | 397 | #### Boolean #### 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 430 | 431 | 432 | 433 | 434 | 455 | 456 | 457 | 458 | 459 | 507 | 508 | 509 |
OperatorPurposeExamples/observations
!not
&&and`and` is short-circuited in that if the first operand is `false` (meaning the entire expression can only be false) it does not evaluate the second operand at all.
||or`or` is short-circuited in that if the first operand is `true` (meaning the entire expression can only be true) it does not evaluate the second operand at all.
XORexclusive or 411 | True if only *one* operand is true:
412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 |
A XOR BB=trueB=false
A=truefalsetrue
A=falsetruefalse
429 |
EQVequivalence 435 | True if both operands are the same:
436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 |
A EQV BB=trueB=false
A=truetruefalse
A=falsefalsetrue
453 | Basically the inverse of XOR 454 |
IMPimplication 460 | Only if the first operand is true, the second operand must also be true. Otherwise true.
461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 |
A IMP BB=trueB=false
A=truetruefalse
A=falsetruetrue
478 | Equivalent to !A || B
479 |
480 | And example might be in a situation where a function can optionally filter by date, eg:
481 |
482 | 483 | getRecords(boolean filterByDate=false, date date) 484 | 485 |

486 | Argument validation could be:

487 | 488 |
argsAreValid = filterByDate IMP structKeyExists(arguments, "date"));
489 | if (!argsAreValid){
490 | throw(type="InvalidArgumentsException");
491 | }
492 |
493 |

494 | This operator has a fundamental flaw in that it's not "short-circuited": the second operand is always evaluated even if the first makes the condition true (eg: the first operand is a false condition). This prevents the most useful potential use of this operator which would be:

495 | 496 |
structKeyExists(URL, "someValue") IMP isValidAccordingToSomeRule(URL.someValue) {
497 | // all good
498 | } else {
499 | // not so good
500 | }
501 |
502 |

503 | This fails because the second operand is evaluated even if URL.someValue doesn't exist, which would have caused the first operand to cause a false condition, and the entire expression is therefore false even before considering the second operand.
504 |
505 | I also think it's a difficult operator to understand, so simply using !A || B is easier to follow than A IMP B. 506 |
510 | 511 | 512 | #### Decision #### 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 534 | 535 | 536 | 537 |
OperatorPurposeExamples/observations
==(These should all be familiar, so I'll not "explain" them)Note that this will attempt to cast the operands to numerics, which can result in unexpected results when comparing strings, eg: "007" will be equal to "7". Use the compare() function for string comparisons in situations where it's important to do a string comparison. Similarly, use dateCompare() to compare dates.
!=
>
>=
<
<=
CONTAINSChecks whether a string contains a substring 529 | a CONTAINS b is true if the string a contains substring b. 530 | EG: "slaughter" CONTAINS "laughter" is true. Inappropriate, but true. 531 | 532 | I'd use the find() function instead, to be honest. 533 |
DOES NOT CONTAINThe opposite of the above
538 | 539 | 540 | #### Conditional assignment #### 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 560 | 561 | 562 |
OperatorPurposeExamples/observations
?:Assign on null / null-coalescing 551 | The result is the first operand if it is defined, otherwise the second operand:

552 | 553 | a = 1;
554 | b = a ?: 2; // a is not null, so b=2
555 | c = d ?: 3; // d is null, so c=3
556 |
557 |

558 | Sometimes colloquially and unfortunately referred to as the "Elvis operator". 559 |
563 | 564 | 565 | ### Ternary operators ### 566 | 567 | There's only one: ```?:``` 568 | 569 | Yeah, this is not the same as the ```?:``` binary operator. Which is confusing when documenting it. However the usage is different, so there's no ambiguity. 570 | 571 | The binary version works like this: 572 | 573 | ```result = operand ?: operand``` 574 | 575 | The ternary version like this: 576 | 577 | ```result = operand ? operand : operand``` 578 | 579 | Example: 580 | 581 | ```cfc 582 | coinToss = randRange(0,1) ? "heads" : "tails"; 583 | ``` 584 | 585 | ```randRange()``` returns a random integer between the two specified boundaries (so in this case the value can be ```0``` or ```1```). In CFML ```0``` is ```false```, and any other numeric value (eg: ```1``` in this case) is ```true```, so this emulates tossing a coin. ```coinToss``` will receive the value either ```heads``` or ```tails```. -------------------------------------------------------------------------------- /chapters/06-error_handling.md: -------------------------------------------------------------------------------- 1 | # Error handling # 2 | 3 | I touched on exception handling before, but only in the context of how exception handling impacts flow control, rather than really going through the concepts behind it. 4 | 5 | 6 | ## Errors happen ## 7 | 8 | Code will error. That's the first thing to accept... there's no such thing as bug free code (one can come up with trite examples to the contrary, but you know what I mean), and we just need to deal with bugs as they occur. But also sometimes is a fairly "logically sound" piece of code, things still go wrong, and whilst in some situations it's not reasonable to expect a problem so no reason to really try to code defensively around it, there are other situations in which it's more easy to predict that something will go wrong. These situations generally revolve around interactions with external systems rather than your own code. External systems are things like remote web services, third-party APIs, the local file system and... humans. These things all conspire to make our code break. Our code should deal with predictable systems by coding so that errors are not likely to occur - and errors that do occur are probably bugs - however when dealing with external systems one should only code an application to "work" for predictable inputs and outputs, and when unpredictable events happen - exception circumstances - the best way to handle it is to expect it to break, and pick up the pieces carefully once it's done so. This is exception handling. 9 | 10 | Another completely legitimate scenario is to handle an occurrence from some external system (say: user input), expect it to be unreliable, intercept an error situation and as part of dealing with that situation either allow the error to bubble back to the user, or bubble a different error to the user. 11 | 12 | I'm a big believer in "garbage in / garbage out": if my code receives duff inputs or other behaviour from an external system, don't try to work out what might have been meant, just throw an error back. 13 | 14 | But before getting too entrenched in "what to do", let's look at how to do it. 15 | 16 | We might have some code: 17 | 18 | ````cfc 19 | result = runSomeProcessThatReliesOnAnExternalSystem(); 20 | ```` 21 | 22 | We should always code defensively, which means the only code that we can trust is code that we wrote: our own application, basically. Defensive coding suggests that that line of code could error. If we let it error, then it's our fault. It we deal with it erroring, we've done out best, which is good enough. 23 | 24 | Normally if the CFML server encounters something it doesn't like, it'll just error. If it's a runtime error, this means it will raise an exception, and halt. And all things being equal, you'll get an error on the screen. 25 | 26 | However because it raises an exception before it halts, this gives our application code a chance to deal with the exception. We can catch it, inspect it, and decide what best to do with it. To catch an exception, we need to tell CFML we're on the look-up for exceptions to catch. We do this by "trying" to run some code: 27 | 28 | ````cfc 29 | try { 30 | result = someExternalSystem.runSomeProcess(); 31 | } 32 | ```` 33 | 34 | This is syntactically incomplete, because as well as *trying* the code, we need to tell CFML what we're going to do with it once we've tried it. There are a couple of options, but we're just looking at catching whatever exception has been thrown first: 35 | 36 | ````cfc 37 | catch (any e) { 38 | // do something here 39 | } 40 | ```` 41 | 42 | To be clear, that's not syntactically complete either. We need to stick 'em together: 43 | 44 | ````cfc 45 | try { 46 | result = someExternalSystem.runSomeProcess(); 47 | } catch (any e) { 48 | // do something here 49 | } 50 | ```` 51 | 52 | I separated it out merely to get you to think about what's going on: we `try` some code; we `catch` any exception that tried code might throw. 53 | 54 | 55 | ## Digression: types of errors ## 56 | 57 | I've perhaps not been as careful as I should be with my terminology here, but this is reflective of common usage. I will - no doubt - use the notion of "error" and "exception" interchangeably. This is unhelpfully vague. Errors come in various flavours, and not all of them result in exceptions. In CFML there are really only two types of errors to worry about: compile-time errors, and run-time errors. 58 | 59 | 60 | ### Compilation errors ### 61 | 62 | Because there's no discrete compilation step in producing a CFML application, people often don't realise that CFML code is indeed compiled before it is executed. When you write code, it's not that actual code that runs. The CFML compiler compiles it to JVM-ready byte-code, and it's *this* that is executed. CFML uses a notion of "Just In Time" compiling. As a file is needed by your application, it's compiled. This is discussed more in depth in [Request/response process & scopes](chapters/09-request_response.md). 63 | 64 | This is relevant in the context of errors because if code is not syntactically valid, then it can't compile, so CFML will throw an error. Here's an example: 65 | 66 | ````cfc 67 | result = someFunction(; 68 | ```` 69 | 70 | Note that that code is malformed: it doesn't have its closing parenthesis. Syntax errors like this prevent the file from compiling, and yield a compile error: 71 | 72 | 73 | 74 | 75 | 76 | This is not *particularly* clear that there's been a compile error, but if you see "syntax error", then it's a compile error. 77 | 78 | Why is this important? Because compile errors occur *before the code can be run*. And to handle errors - to catch exceptions - we need to use code (ie: `try` / `catch`). And for code to be read, it needs to be run... and for it to run, it needs to be compiled first. 79 | 80 | The ramification is that one can only use CFML's error handling to deal with *runtime* errors. It cannot deal with compilation errors. 81 | 82 | We can't do this: 83 | 84 | ````cfc 85 | try { 86 | result = someFunction(; 87 | } catch (any e){ 88 | // deal with that syntax error 89 | } 90 | ```` 91 | 92 | 93 | ### Runtime errors ### 94 | 95 | The only errors we can deal with are runtime errors: errors that crop up as part of the code running. This is a runtime error: 96 | 97 | ````cfc 98 | result = 1 / 0; 99 | ```` 100 | 101 | `1 / 0` is not a valid expression bececause one cannot divide by zero: it's a mathematical impossibility. The CFML runtime will detect this, error, and as part of the error occurring, it will raise an exception. So we can `catch` that: 102 | 103 | ````cfc 104 | try { 105 | result = 1 / 0; 106 | } catch (any e){ 107 | writeOutput("type: #e.type#; message: #e.message#"); 108 | } 109 | ```` 110 | 111 | This will result in this output: 112 | 113 | ````cfc 114 | type: Expression; message: Division by zero. 115 | ```` 116 | 117 | But because this is a runtime error, we're given an exception to deal with, and our *code* can deal with it. 118 | 119 | 120 | ## Dealing with runtime exceptions ## 121 | 122 | Oh, let's look at how that previous code runs *without* the `try` / `catch`: 123 | 124 | 125 | 126 | So using `try`/`catch` the error still occurs, but instead of exiting, we are able to catch the exception, then do something. In my example I'm just outputing some info about the exception, but one can do whatever one wants there to deal with the situation as appropriate. 127 | 128 | It's worth reviewing the `try`/`catch` section in the [Control structures](02-flow_control.md#try-catch) chapter to understand the flow control of a the `try`/`catch` process, but basically it's 129 | 130 | ````cfc 131 | // 1. this runs 132 | try { 133 | // 2. this runs 134 | // 3. this errors 135 | // -. this does not run 136 | } catch (any e){ 137 | // 4. processing jumps to here 138 | // 5. etc 139 | } 140 | // 6. then continues on from here 141 | 142 | ```` 143 | 144 | So it's important to note that when one handles an error, then processing *doesn't* halt like it does when an unhandled error is encountered. So if one wants to handle some error condition, but then still halt processing: one actually needs to code for that. There are a few options here: 145 | 146 | * rethrow the original exception - this will cause the same error condition again, and processing will stop with that error 147 | * throw a *different* exception - this again will cause an error, but bubble up a different exception to code possibly handling it or listening for it. 148 | * simply abort processing. 149 | 150 | 151 | ### Aborting ### 152 | 153 | Here's an example: 154 | 155 | ````cfc 156 | try { 157 | result = someExternalSystem.runSomeProcess(); 158 | } catch (any e){ 159 | someExternalSystem.closeConnection(); 160 | logger.logError("#e.type# #e.message# occurred when calling runSomeProcess()"); 161 | abort; 162 | } 163 | ```` 164 | 165 | This speculative example shows running some tidy-up code when the error occurs, then logging that it happened, but then simply aborting processing. `abort` simply says to the CFML engine "that's the end of that". 166 | 167 | 168 | ### Rethrow ### 169 | 170 | Instead of the `abort`, we could use `rethrow` to cause the same error to be repeated. Going back to our earlier example: 171 | 172 | ````cfc 173 | try { 174 | result = 1 / 0; 175 | } catch (any e){ 176 | writeOutput("type: #e.type#; message: #e.message#"); 177 | rethrow; 178 | } 179 | ```` 180 | 181 | Now we get: 182 | 183 | 184 | 185 | So we get both the error handling code running, but then we just get the original error again. 186 | 187 | 188 | ### Raising new exceptions with `throw` ### 189 | 190 | Or, similarly, we can cause a new error to occur, by throwing our own exception: 191 | 192 | ````cfc 193 | try { 194 | result = 1 / 0; 195 | } catch (any e){ 196 | writeOutput("type: #e.type#; message: #e.message#"); 197 | throw(type="BadExpressionException", message="The provided expression was invalid", detail="Original exception: #e.type#"); 198 | } 199 | ```` 200 | 201 | 202 | 203 | 204 | ### Continuing ### 205 | 206 | Now we don't *need* to halt processing either with an `abort`, or throwing an exception with `rethrow` or `throw`. As touched on above, processing will - by default - just continue on after the `catch` statement. Sometimes this is fine: the code that fails within the `try` / `catch` might be optional or "nice to have", and it's really no odds if it runs or not. Or it's more important that subsequent code still runs even if the optional "nice to have" code has failed. 207 | 208 | An example might be that we have a booking system, and as part of the booking we log some metrics and send some emails. Whilst this info is excellent to capture, it's not essential... but what *is* essential is the booking goes through (and... err... we take the client's money ;-), and we show the user the booking confirmation page. So we might have this sort of code: 209 | 210 | ````cfc 211 | try { 212 | metricsLoggingService.log(stuffToLogBeforeBooking); 213 | } catch (any ignore){ 214 | // if we don't get the stuff in the log, it doesn't really matter 215 | } 216 | bookingService.book(); 217 | try { 218 | emailService.sendPropertyEmail(); 219 | emailService.sendCustomerEmail(); 220 | } catch (any exceptionFromEmail){ 221 | try { 222 | // try to let our customers services team know they need to contact the proeprty and client cos they didn't get their emails 223 | emailService.sendCustomerServiceEmail(); 224 | } catch (any ignore){ // "ignore" is not a special word, it's just a convention saying "I will not be using this exception object" 225 | // well we tried, but it's more important to give the customer a uniform experience 226 | } 227 | } 228 | try { 229 | metricsLoggingService.log(stuffToLogAfterBooking); 230 | } catch (any ignore){ 231 | // again, if we don't get the stuff in the log, it doesn't really matter 232 | } 233 | requestService.relocateToConfirmationPage(); 234 | ```` 235 | 236 | Here we just use the `try` / `catch` to protect against optional code interfering with important code running. In some cases we simply don't mind if the optional code runs or not. In the case of the emails not being sent, we try remedial action (emailing customer services), but if *that* errors, "the show must go on", so we just keep going. 237 | 238 | Our actual code handles this lot slightly differently, but it's a reasonable example. 239 | 240 | 241 | ## Causing exceptions ## 242 | 243 | One might wonder if we've gone to all the effort of catching an exception, why then just throw it again, or throw a *different* exception. Fair question. 244 | 245 | It's all down to separation of concerns, I guess (I sound vague as I'm trying to work out how to word it). There's various levels of code running in a given request. For example: 246 | 247 | * a given web page's code... 248 | * ... might call a back end API to get its data... 249 | * ... which in turn calls a remote API or DB or something which stores the data. 250 | 251 | The DB tier might error for a number of reasons: no connections, bad data, etc. This is important for the API to know, but the actual web app isn't interested in the minutiae of the data storage (that it's JDBC or ODBC or even that it's a DB), it's just interested in whether the API gives it objects back or not. So whether or not the API has remedial action to perform in these situations, if it wants to bubble back an exception to the calling code, then something specific to its own codebase rather than the vagary of the DB is more appropriate to bubble back. Say for example it might wrap up various different JDBC exceptions as one ApiStorageFailureException. That's the sort of thing the web page's code needs to know about. Not JDBC stuff. 252 | 253 | In our situation we have the following set-up: 254 | 255 | * the web site (eg: the files for the web pages themselves) 256 | * an API that has all the business logic sitting behind that 257 | * a payment service which is a wrapper for... 258 | * ... a connection to the third party banking system 259 | 260 | We use different banking providers depending on the situation, and each of those return different errors to us in different situations. One payment provider could have twenty different things that might cause a payment to be rejected or fail; or another system might only have half a dozen; each with different error codes and behaviours. But our *web site* only needs to know a couple of different variations: the payment was rejected (eg: dodgy credit card), or the payment couldn't be made (eg: comms error), because the website behaves differently depending on which of those it is. We don't want to have our website code needing to have handling for every payment provider's potential error situations. THis is the whole reason we have the abstracted API, after all! Or there might be some other sort of unexpected exception (a bug in our code, or in the payment provider, etc) which we don't want special handling for at all. 261 | 262 | So our API handles each of the "expected" exceptions from the payment providers (they give us a list of all the possible errors), and catches those, then throws our own custom exception reflecting "PaymentRejectedException" or "TransactionFailedException", or "UnexpectedException". Then - no matter which payment provider we're using - the web site knows it only needs to deal with those three situations, with messages like: 263 | 264 | * your payment didn't go through, please try again 265 | * there was an error processing your payment, please try again 266 | * or for the unexpected ones, just show the general error page 267 | 268 | 269 | ## Why let it error? ## 270 | 271 | Another question that could crop up here is if I'm catching the exceptions, why do I think throw *another* exception, instead of "just dealing with it". I think we can agree that the exceptions from external sources like our payment provider are unavoidable, so we need to catch those. But why then cause *more* errors? The thing to remember here is that an error might raise an exception, but an exception is not intrinsically an error. Especially if one raises it on purpose. 272 | 273 | Some code I have seen will try to hide exceptions from the calling code. It'll do this sort of thing: 274 | 275 | ````cfc 276 | function someFunction(){ 277 | var result = {}; 278 | try { 279 | result.data = someRiskyFunction(); 280 | result.status = true; 281 | } catch (any e){ 282 | result.status = false; 283 | result.message = e.message; 284 | } 285 | return result; 286 | } 287 | ```` 288 | So if `someRiskyFunction()` errors, we don't let the error perpetuate, we deal with it. We just tell the calling code whether things worked or not (via the `result.status` value). 289 | 290 | Then in the calling code, do this: 291 | 292 | ````cfc 293 | records = someFunction(); 294 | if (records.status){ 295 | // it's OK 296 | }else{ 297 | // do something else to handle the fact it was no good 298 | } 299 | 300 | ```` 301 | 302 | On the surface of things, this seems fine and it's "good" because there are no errors to deal with. However it's pretty much reinventing the wheel, and using a system-specific mechanism for doing so. The *rest of the industry* uses exception handling for this sort of thing. 303 | 304 | If code didn't work: it's *OK* for it to error. Just deal with it. 305 | 306 | I think this approach stems from developers getting it into their head that exceptions are somehow bad. They're not. *Not dealing* with errors are bad, but exception handling is there for effecting this. Writing code that errors in an uncontrolled fashion due to suboptimal logic? Also bad, but that's a different thing. Basically if code is supposed to do something, and it *doesn't do it*, then raising an exception is completely the appropriate and expected thing to do. 307 | 308 | 309 | ## Letting the correct code make the decisions ## 310 | 311 | Another consideration here is that one ought to pay attention to which code should be responsible for dealing with error situations. 312 | 313 | If code "hides" and exception, then it's taking responsibility to deal with it. And if the code in question is inside a method `makePayment()` (for example), then that's the wrong place to be dealing with it. It's not `makePaymentOrDealWithItIfItDoesntWork()`, so that function should not be dealing with it. It's function is to make a booking. That's it. If it doesn't make the booking, it's failed. 314 | 315 | Also it's not for this Payments API to decide how to handle an error on my site. It's my site's job. This is presupposing that the entire site / application is compartmentalised into "areas of concern", which I'll discuss in the [MVC basics](12-mvc.md) chapter, but the `makePayment()` function should raise a relevant exception, and let the code that call it decide what to do with it. This should bubble all the way to the top level of the web site, where a given web page should deal with it (display a message or change the output somehow), or just filter all the way up to the site's default error page. If you look at things in the context of the entire web site rather than the immediate code that's raising an exception, it becomes easier to see that bubbling an exception back to the most appropriate code to deal with it makes sense. 316 | 317 | 318 | ## A closer look at an exception ## 319 | 320 | We've seen that a `catch` block receives an exception object, eg: 321 | 322 | ````cfc 323 | try { 324 | // risky code 325 | } catch (any e){ 326 | // do somethind with the exception 327 | } 328 | ```` 329 | 330 | In this example, the code in the `catch` block receives a variable `e`, which contains the actual exception object. IE: the very object that was created to represent whatever error occurred. 331 | 332 | An exception object has a number of properties which are exposed properly: 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 |
Property
TypeThis is the type of exception, eg: ArithmeticException, IOException
MessageA human-readable message describing the error
DetailMore human-readable detail on the error
TagContextAn array of code references: basically the call stack of code executing at the time the exception was created
StackTraceThe Java stack trace at the time the error occurred
346 | 347 | All property values are strings, unless specified. 348 | 349 | Note that often exceptions will also have an `errorcode` and `extendedinfo` properties. These are not *always* included though. And various other exception types will expose other properties too. 350 | 351 | One last note: the exception object variable is only available in its `catch` block. It is *not* available in code following a `catch` block. 352 | 353 | 354 | ## Throwing your own exception ## 355 | 356 | Similarly, the `throw()` statement can create a custom exception (which was shown further up): 357 | 358 | ````cfc 359 | try { 360 | throw( 361 | type = "SomeException", 362 | errorCode = "123", 363 | message = "This is an exception", 364 | detail = "And more detail on the exception", 365 | extendedinfo = "More info still" 366 | ); 367 | }catch(any e){ 368 | writeDump(e); 369 | } 370 | ```` 371 | 372 | That dump will show everything exposed by the `e` object. 373 | 374 | 375 | One can also throw a Java exception too: 376 | 377 | ````cfc 378 | throw(object=createObject("java", "java.lang.Exception").init("This is a Java Exception")); 379 | ```` 380 | 381 | Here the `type` will be `java.lang.Exception`, and the `message` `"This is a Java Exception"`. 382 | 383 | Throwing a Java exception is handy when working with Java code that has returned the exception, and you want to `try` / `catch` it, then bubble it further up to the calling code. You'd probably not *usually* manually create the exception object to then throw, you'll just be bubbling back one you've already caught. 384 | 385 | Remember you can also use `rethrow` in a `catch` block to... well... rethrow the exact same exception that the `catch` block caught. 386 | 387 | ## Dealing with different exceptions ## 388 | 389 | On the other end of all these different exceptions, we can also catch precise exceptions too. So far we have only done this: 390 | 391 | ````cfc 392 | try { 393 | throw(type="SomeException"); 394 | }catch(any e){ 395 | // deal with any sort of exception 396 | } 397 | ```` 398 | 399 | However we don't have to catch `any` exception, we can catch specific types: 400 | 401 | ````cfc 402 | try { 403 | throw(type="SomeOtherException"); 404 | } catch(SomeException e){ 405 | // this isn't run 406 | } catch(SomeOtherException e){ 407 | // this is run 408 | } 409 | ```` 410 | 411 | In this example we're actively throwing a `SomeOtherException`, and we have `catch` blocks prepared to catch - respectively - a `SomeException` and a `SomeOtherException`. An exception can only been caught by one catch block. 412 | 413 | Note that if the exception is different from any of the ones specified to be caught, it will *not* be caught, and the error will be thrown, eg: 414 | 415 | ````cfc 416 | try { 417 | throw(type="ADifferentException"); // this exception will not be caught, so will cause an error 418 | } catch(SomeException e){ 419 | // this isn't run 420 | } catch(SomeOtherException e){ 421 | // nor is this 422 | } 423 | 424 | ```` 425 | 426 | 427 | ## Namespacing exceptions ## 428 | 429 | Being able to catch various different exceptions is excellent, but what if one wants to have the same catch block for multiple different exceptions? This is possible too. One can "namespace" exceptions, and then `catch` exception types by namespace: 430 | 431 | ````cfc 432 | try { 433 | switch (randRange(1,3)){ 434 | case 1: 435 | throw(type="com.mydomain.myapp.SomeException"); 436 | break; 437 | case 2: 438 | throw(type="com.mydomain.myapp.SomeOtherException"); 439 | break; 440 | default: 441 | throw(type="DifferentException"); 442 | break; 443 | 444 | } 445 | } catch(com.mydomain e){ 446 | writeOutput("com.mydomain exception caught: #e.type#"); 447 | } catch(any e){ 448 | writeOutput("Any other sort of exception caught: #e.type#"); 449 | } 450 | ```` 451 | 452 | Here we are namespacing our exception by the standard reverse-domain name packaging tactic, throwing one of either a `com.mydomain.myapp.SomeException` or `com.mydomain.myapp.SomeOtherException` from that package: these will be caught by `com.mydomain`. one third of the time this test code will throw a `DifferentException`, and this will be caught by the `any` `catch` block. 453 | 454 | Your namespacing doesn't have to follow any specific pattern, other than separating each element by dots. You can catch groups at any dotted partition (ie: in that example above, one could catch `com`, `com.mydomain`, `com.mydomain.myapp` or `com.mydomain.myapp.SomeException`). 455 | 456 | 457 | ## finally ## 458 | 459 | I mean this in both the sense that this is the final section of this chapter, but also the `finally` functionality. 460 | 461 | As well as having a `try` block and `catch` blocks, one can also have a `finally` block: 462 | 463 | ````cfc 464 | writeOutput("Before the test code
"); 465 | try { 466 | switch (randRange(1,4)){ 467 | case 1: 468 | writeOutput("Throwing a CaughtException
"); 469 | throw(type="CaughtException"); 470 | break; 471 | case 2: 472 | writeOutput("Throwing a RethrownException
"); 473 | throw(type="RethrownException"); 474 | break; 475 | case 3: 476 | writeOutput("Throwing a UncaughtException
"); 477 | throw(type="UncaughtException"); 478 | break; 479 | default: 480 | writeOutput("Not throwing an exception
"); 481 | break; 482 | 483 | } 484 | } catch (CaughtException e){ 485 | writeOutput("Caught a #e.type#
"); 486 | } catch (RethrownException e){ 487 | writeOutput("Rethrowing a #e.type#
"); 488 | rethrow; 489 | } finally { 490 | writeOutput("This is run irrespective of anything else going on in this try / catch / finally block
"); 491 | } 492 | writeOutput("After the test code
"); 493 | ```` 494 | 495 | This is a chunk of code, but it demonstrates that any of these situations will run the `finally` block: 496 | 497 | * an exception is caught 498 | * an exception is thrown (in this case via `rethrow`) in the catch block 499 | * an exception that isn't caught at all 500 | * no exception is thrown at all 501 | 502 | One sitter for the `finally` construct is when doing file operations (which will be covered in [File system / HTTP / FTP](18-file_system.md)). It's always important to exception handle file system operations as what the file system does is outwith your control, and it's always important to *close* a file if you've opened one, irrespective of how the code runs: 503 | 504 | ````cfc 505 | try { 506 | writeOutput("Open a file for reading
"); 507 | f = fileOpen(getCurrentTemplatePath()); 508 | line = f.readLine(); 509 | throw(type="ForcedException"); 510 | } catch (ForcedException e){ 511 | writeOutput("Deal with the exception
"); 512 | } finally { 513 | f.close(); 514 | writeOutput("And the file is safely closed
"); 515 | } 516 | ```` 517 | 518 | As demonstrated in the previous example, whether this code runs fine or whether that exception is thrown (50% of the time), it doesn't matter: the `finally` block will run, and close that file. 519 | 520 | 521 | ## Conclusion ## 522 | 523 | Exception handling is pretty comprehensive in CFML. One has to deal with exceptions when they arise; but also understand that it's OK for code to error if that's the most suitable thing for it to do. Don't "hide" exceptions: just deal with them. 524 | -------------------------------------------------------------------------------- /chapters/02-flow_control.md: -------------------------------------------------------------------------------- 1 | # Flow control structures # 2 | 3 | CFML has all the usual suspects when it comes to flow control structures. I'll split these into three sections: 4 | 5 | * branching - eg: if / switch / try/catch 6 | * looping - eg: for / while / do/while / collection iteration methods 7 | * abstracting - eg: include files, modules, functions, classes 8 | 9 | 10 | ## Branching ## 11 | 12 | ### if ### 13 | 14 | This is exactly the same as most "curly brace" languages: 15 | 16 | ````cfc 17 | if (booleanExpression) statement(s) if true 18 | 19 | if (booleanExpression) statement(s) if true 20 | else statement(s) if false 21 | ```` 22 | 23 | The statement(s) can be either a single statement, or a block of statements: 24 | 25 | ````cfc 26 | if (booleanExpression) 27 | // a single statement to run if true 28 | 29 | if (booleanExpression) { 30 | // a statement to run if true 31 | // another statement to run if true 32 | // etc 33 | } 34 | ```` 35 | 36 | This extends to `else` as well: 37 | 38 | ````cfc 39 | if (booleanExpression) 40 | // a single statement to run if true 41 | else 42 | // a single statement to run if false 43 | 44 | 45 | if (booleanExpression) { 46 | // a statement to run if true 47 | // another statement to run if true 48 | // etc 49 | }else{ 50 | // a statement to run if false 51 | // another statement to run if false 52 | // etc 53 | } 54 | ```` 55 | 56 | I agree with the common recommendation to always use curly-brace-delimited blocks, even if your block has only one statement. It makes the code clearer, and reduces the chance of unexpected behaviour: 57 | 58 | ````cfc 59 | // this is misleading 60 | if (booleanExpression) 61 | // a single statement to run if true 62 | // another statement, but is run whether true or false 63 | 64 | // this is clearer 65 | if (booleanExpression) { 66 | // a single statement to run if true 67 | } 68 | // another statement, but is run whether true or false 69 | ```` 70 | 71 | In the latter example, it's easier to see that the indentation is just wrong, whereas in the first example it'd be easy to misread the second statement as being part of the `if` clause. 72 | 73 | CFML doesn't specifically have an `elseif` construct (although Lucee supports it), one just combines a single-statement `else` clause which is an `if`: 74 | 75 | ````cfc 76 | if (booleanExpression) 77 | // a single statement to run if booleanExpression is true 78 | else if (differentBooleanCondition) 79 | // a single statement to run if differentBooleanCondition is true 80 | else 81 | // a single statement to run if differentBooleanCondition is false 82 | 83 | // or 84 | 85 | if (booleanExpression) { 86 | // a statement to run if booleanExpression is true 87 | // another statement to run if booleanExpression is true 88 | // etc 89 | } else if (differentBooleanCondition){ 90 | // a statement to run if differentBooleanCondition is true 91 | // another statement to run if differentBooleanCondition is true 92 | // etc 93 | }else{ 94 | // a statement to run if differentBooleanCondition is false 95 | // another statement to run if differentBooleanCondition is false 96 | // etc 97 | } 98 | ```` 99 | 100 | 101 | ### switch ### 102 | 103 | Switch/case in CFML works the same was as it generally does in other "curly brace" languages: 104 | 105 | ````cfc 106 | switch (colour) { 107 | case "red": 108 | writeOutput("##FF0000"); 109 | break; 110 | 111 | case "green": 112 | writeOutput("##00FF00"); 113 | break; 114 | 115 | case "blue": 116 | writeOutput("##0000FF"); 117 | break; 118 | 119 | default: 120 | writeOutput("it was some other colour"); 121 | } 122 | ```` 123 | Unlike with `if` statements, the curly braces are required with a `switch` statement. 124 | 125 | It's important to note that once a `case` condition is matched, then all the code in the `switch` block is executed until a `break` statement is encountered: no further `case` clauses are tested. This demonstrates what I mean: 126 | 127 | ````cfc 128 | switch (colour) { 129 | case "red": 130 | writeOutput("##FF0000"); 131 | // no break statement here 132 | 133 | case "green": 134 | writeOutput("##00FF00"); 135 | break; 136 | 137 | case "blue": 138 | writeOutput("##0000FF"); 139 | break; 140 | 141 | default: 142 | writeOutput("it was some other colour"); 143 | } 144 | ```` 145 | 146 | If `colour` was `red`, then the output would be this: 147 | 148 | ```` 149 | #FF0000#00FF00 150 | ```` 151 | 152 | This is because processing skips down to the `red` case, an executes everything down to the next `break` statement, which means these lines get executed: 153 | 154 | ````cfc 155 | writeOutput("##FF0000"); 156 | case "green": 157 | writeOutput("##00FF00"); 158 | break; 159 | ```` 160 | 161 | A `case` is not actually the test condition, that's all handled by the `switch` statement. A `case` is merely a label. 162 | 163 | Sometimes having this "fall through" functionality is desirable, but on the whole it's something to look out for when processing is not behaving how you'd intended it to be. 164 | 165 | A side effect of this is that one can have one block of code executed for multiple `case` labels: 166 | 167 | ````cfc 168 | switch (colour) { 169 | case "red": 170 | case "green": 171 | // this will be run for either of red or green 172 | break; 173 | 174 | // etc 175 | } 176 | ```` 177 | 178 | If you're using ColdFusion, an important thing to note is that the values for the case clauses need to be *constant* values, ie: they need to be literal values. On Lucee they can be any expression: 179 | 180 | ````cfc 181 | // this example is only legal on Lucee 182 | 183 | COLOURS = { 184 | RED = "red" 185 | }; 186 | 187 | function returnsGreen(){ 188 | return "yellow"; 189 | } 190 | 191 | function sometimesReturnsBlue(){ 192 | return randRange(0,1) ? "blue" : "not blue"; 193 | } 194 | 195 | switch (URL.colour) { 196 | case COLOURS.RED: 197 | writeOutput("##FF0000"); 198 | break; 199 | 200 | case returnsGreen(): 201 | writeOutput("##00FF00"); 202 | break; 203 | 204 | case sometimesReturnsBlue(): 205 | writeOutput("##0000FF"); 206 | break; 207 | 208 | default: 209 | writeOutput("it was some other colour"); 210 | } 211 | ```` 212 | 213 | Finally... in all my examples here I have specified a `default` case. This is optional, but I think generally there is grounds to have one. The `default` case is used if none of the previous cases were used. 214 | 215 | 216 | ### try/catch ### 217 | 218 | The try/catch statement is for dealing with exceptions (runtime errors). The syntax for `try` / `catch` (and `finally`) is the same as in other similar languages: 219 | 220 | ````cfc 221 | try { 222 | // code that could possibly error at runtime, or raise an exception for some other reason 223 | } catch (ExceptionType variableToTakeException) { 224 | // code to deal with errors of type ExceptionType 225 | } catch (DifferentExceptionType variableToTakeException) { 226 | // code to deal with errors of type DifferentExceptionType 227 | } finally { 228 | // code to run after the try / catch code. This code *always* runs 229 | } 230 | // subsequent code 231 | ```` 232 | 233 | * If the code in the `try` block completes without error, processing continues in the `finally` block, before continuing to any subsequent code 234 | * If the code in the `try` block errors at runtime, then the CFML engine will look for a `catch` clause to handle the exception. In the code above there are `catch` clauses which will deal with a exception of type `ExceptionType` or `DifferentExceptionType`. If the exception associated with the error is either of those, then the appropriate block of code will be executed. If any other type of exception is raised other than the ones explicitly being "caught", then the exception will *not* be caught, and will be bubbled back to the calling code, or returned as an error to the browser if not caught via some other mechanism. 235 | * Whatever happens, the `finally` clause is always executed, *even if the exception is not caught*. 236 | 237 | There is a special exception type one can catch: "any", which will catch any sort of exception at all: 238 | 239 | ````cfc 240 | } catch (any e) { 241 | // statements 242 | } 243 | ```` 244 | 245 | Some examples will make this more clear, hopefully: 246 | 247 | ````cfc 248 | try { 249 | 250 | writeOutput("code before the error will run
"); 251 | 252 | i = 1/0; // this will give a division by zero error, causing a java.lang.ArithmeticException to be raised 253 | 254 | writeOutput("code after the error will NOT run
"); 255 | 256 | } catch (java.lang.ArithmeticException e) { 257 | 258 | writeOutput("code in the appropriate catch block will run
"); 259 | writeOutput("The exception was of type #e.type#
"); 260 | 261 | } catch (DifferentException e) { 262 | 263 | writeOutput("code in a different catch block will NOT run
"); 264 | 265 | } catch (any e) { 266 | 267 | writeOutput("Only one catch block will be run, so this is ignored
"); 268 | 269 | } finally { 270 | 271 | writeOutput("code in the finally block always runs
"); 272 | 273 | } 274 | 275 | writeOutput("If the exception was caught, processing continues
"); 276 | ```` 277 | 278 | Using Lucee as the CFML engine, this code will output: 279 | 280 | ```` 281 | code before the error will run 282 | code in the appropriate catch block will run 283 | The exception was of type java.lang.ArithmeticException 284 | code in the finally block always runs 285 | If the exception was caught, processing continues 286 | ```` 287 | 288 | Unfortunately there is no standard agreed between the vendors as to which exception various error conditions ought to raise. So on Lucee this raises a`java.lang.ArithmeticException`, but on ColdFusion it raises an `Expression` exception. So on ColdFusion, the `catch` clause would be: 289 | 290 | ````cfc 291 | } catch (expression e) { 292 | 293 | writeOutput("code in the appropriate catch block will run
"); 294 | writeOutput("The exception was of type #e.type#
"); 295 | 296 | } 297 | ```` 298 | 299 | As touched on in the sample code above, the `catch` clause defines a variable to contain details about the exception that has been caught. In the sample code I am using the variable `e`, which is kind of a de facto standard, but one can use a more meaningful variable name if so desired. 300 | 301 | There is more to exception handling, but this chapter is only really about the flow control considerations of exception handling. Exception handling is covered in more depth in [INSERT APPROPRIATE CHAPTER HERE]. 302 | 303 | 304 | ## Looping ## 305 | 306 | Looping is really a special kind of branching: just branching back to an earlier line of code, rather than a subsequent line of code. Because execution is branching back to an earlier line of code, the same condition can be checked multiple times, effecting a logical "loop". 307 | 308 | 309 | ### for ### 310 | 311 | There are a couple of different `for` loops: a generic indexed loop, and a for-in loop. The general approach is the same: there is a `for` statement which conditionally executes statement(s) based on criteria. If the statements following the `for` are enclosed in curly braces, all of them are executed each iteration; without braces, just the single following statement is repeated, eg: 312 | 313 | ````cfc 314 | // single statement 315 | for (criteria) 316 | // this one statement is looped 317 | // this one is not 318 | 319 | // multiple statements 320 | for (criteria) { 321 | // this one is looped over 322 | // so is this and any other statements in the block 323 | } 324 | ```` 325 | 326 | 327 | #### General purpose `for` loop #### 328 | 329 | This is probably the most ubiquitous general looping structure, most familiarly realised as this sort of construct: 330 | 331 | ````cfc 332 | for (i=1; i <= 10; i++){ 333 | // code in here is executed ten times 334 | } 335 | ```` 336 | 337 | This general `for` statement takes three separate (and optional) statements in its definition: 338 | 339 | ````cfc 340 | for (initial; beforeEach; afterEach) statement(s) 341 | ```` 342 | 343 | * `initial`: this statement is executed once and only once before processing any of the rest of the code in the loop. This can be any single assignment statement. 344 | * `beforeEach`: this is a boolean expression which is checked before each iteration (including before the first iteration) of the loop. If the expression is `true`, then the loop body is executed; if it is `false` (or `null`), processing continues *after* the `for` statement's code block. Note that if the expression is `false` from the outset, then the loop body is *not* executed even a first time. So it's possible for a `for` loop's code block to not be executed. 345 | * `afterEach`: at the end of each iteration of the loop, this expression is evaluated. It's only evaluated at all if the `beforeEach` expression was `true`. 346 | * `statement(s)`: the code that is repeated by the loop: either a single statement or a block of statements. 347 | 348 | Bearing those rules in mind, the example above *could* be written like this: 349 | 350 | ````cfc 351 | i=1; 352 | for (;;){ 353 | if (i > 10) break; 354 | // code in here is executed ten times 355 | i++; 356 | } 357 | ```` 358 | (`break` exits from the loop) 359 | 360 | It's not much of a stretch to realise that this sort of loop is not limited to simply counting over a variable and doing something with it, but that's generally what it's used for. Here's a very contrived example of looping over a file's contents and outputting it, using a generic `for` loop: 361 | 362 | ````cfc 363 | f=fileOpen(getCurrentTemplatePath()); 364 | for (line=f.readLine();!isNull(line); line=fileIsEof(f) ? null : f.readLine()){ 365 | writeOutput("#encodeForHtml(line)#
"); 366 | } 367 | ```` 368 | 369 | (This code only works on Lucee with full null support enabled, by the way: CFML by default does not support the `null` keyword). 370 | 371 | 372 | #### Collection `for` loop #### 373 | 374 | This is a procedural approach to iterating over a collection. The collection can be any of an array, a struct, a record set, or a delimited string. They're all handled pretty similarly, so I'll just breeze over the syntax and an example for each. Basically a loop is a loop, so there's not too much to say about each of these. 375 | 376 | 377 | ##### Array ##### 378 | 379 | The syntax here is: 380 | 381 | ````cfc 382 | for (element in array) statements 383 | ```` 384 | 385 | Note that it's the element (value), not the index that gets passed into the loop: 386 | 387 | ````cfc 388 | letters = ["a", "b", "c"]; 389 | for (letter in letters) { 390 | writeOutput(letter); // abc 391 | } 392 | ```` 393 | 394 | 395 | ##### Struct ##### 396 | 397 | Syntax: 398 | 399 | ````cfc 400 | for (key in struct) statements 401 | ```` 402 | 403 | Unlike with arrays, for a struct it's the *key* that's passed into the loop: 404 | 405 | ````cfc 406 | person = {firstName="Andrew", lastName="Bennett"}; 407 | for (key in person) { 408 | writeOutput("#key#: #person[key]#;"); // LASTNAME: Bennett; FIRSTNAME: Andrew; 409 | } 410 | ```` 411 | 412 | The thing to remember here is that structs do not have a sense of ordering (more about this in Chapter 3), so there is no guarantee which order the keys will come out in. In the example above, the keys come out in the reverse order that they were set. 413 | 414 | 415 | ##### Record set ##### 416 | 417 | This allows one to loop over a record set. 418 | 419 | ````cfc 420 | for (row in recordset) statements 421 | ```` 422 | 423 | Each row is returned as a struct in this case. It is keyed on column name: 424 | 425 | ````cfc 426 | people = queryNew( 427 | "id,firstName,lastName", 428 | "integer,varchar,varchar", 429 | [ 430 | [1, "Charlotte", "Dennis"], 431 | [2, "Elise", "Forster"], 432 | [3, "Greta", "Holliday"] 433 | ] 434 | ); 435 | for (person in people){ 436 | writeOutput("#person.id# #person.firstName# #person.lastName#
"); 437 | } 438 | ```` 439 | 440 | This outputs: 441 | 442 | ```` 443 | 1 Charlotte Dennis 444 | 2 Elise Forster 445 | 3 Greta Holliday 446 | ```` 447 | 448 | 449 | ##### String ##### 450 | 451 | This is currently only supported on ColdFusion. Syntax: 452 | 453 | ````cfc 454 | for (element in string) statements 455 | ```` 456 | 457 | Here the string is treated as a comma-separated list of elements: 458 | 459 | ````cfc 460 | numbers = "one,two,three"; 461 | for (number in numbers){ 462 | writeOutput("#number#;"); // one;two;three; 463 | } 464 | ```` 465 | 466 | It's important to note that the only supported delimiter is a comma. 467 | 468 | 469 | 470 | ### break and continue ### 471 | 472 | As I've already used a `break` in one of my examples above, I should actually explain what it does, I guess. 473 | 474 | 475 | #### break #### 476 | 477 | A `break` statement will exit the current loop statement, resuming processing with the first statement after the loop block: 478 | 479 | ````cfc 480 | for (i=1; i <= 5; i++){ 481 | writeOutput("Before break: #i#
"); 482 | break; 483 | writeOutput("After break: #i#
"); 484 | } 485 | writeOutput("After loop
"); 486 | ```` 487 | 488 | The output here is: 489 | 490 | ```` 491 | Before break: 1 492 | After loop 493 | ```` 494 | 495 | So processing enters the loop as per usual, but when it encounters the `break`, the loop is exited completely. Sometimes one only needs to process a loop until some certain condition is met (say: finding a value in an array), at which point in time one doesn't need to check the rest of the array, so one can break out of the loop. There is generally better ways of achieving this end, but that's the most common use case. 496 | 497 | 498 | #### continue #### 499 | 500 | On the other hand, `continue` simply exits from the current *iteration* of the loop, not the entire loop, eg: 501 | 502 | ````cfc 503 | for (i=1; i <= 5; i++){ 504 | writeOutput("Before continue: #i#
"); 505 | continue; 506 | writeOutput("After continue: #i#
"); 507 | } 508 | writeOutput("After loop
"); 509 | ```` 510 | 511 | The output for this one is: 512 | 513 | ```` 514 | Before continue: 1 515 | Before continue: 2 516 | Before continue: 3 517 | Before continue: 4 518 | Before continue: 5 519 | After loop 520 | ```` 521 | 522 | Note that we hit *all* of the "before" messages - so the looping is still running - but the `continue` exits the given iteration, resuming processing at the top of the loop for the next iteration. 523 | 524 | When would one want to do this? Say one has a an array with numbers in it. One might only want to process the array element if it is an even number, but one does want to process *all* the even numbers in the array: 525 | 526 | ````cfc 527 | for (i=1; i <= 10; i++){ 528 | if (i MOD 2){ // if there's a remainder it's an odd number, so we're not interested 529 | continue; 530 | } 531 | // process the even number 532 | writeOutput(i); 533 | } 534 | ```` 535 | 536 | In this example the output is: 537 | 538 | ```` 539 | 246810 540 | ```` 541 | 542 | Right. That's it: that's all there is to say on `break` and `continue`. Back to the actual looping constructs... 543 | 544 | 545 | ### while ### 546 | 547 | A `while` loop checks a condition before each iteration, and if the condition is true: runs the code in the block. When the code in the block is complete, processing is returned to the top of the loop (and the condition is, obviously, evaluated again). A `while` loop can possibly execute no iterations at all, if the condition is `false` to start with. 548 | 549 | ````cfc 550 | while (booleanExpression) statement(s) 551 | ```` 552 | 553 | (as always, the `statement(s)` can be either a single statement or a block of statements). EG: 554 | 555 | ````cfc 556 | i=0; 557 | while (++i <= 5) { 558 | writeOutput(i); // 12345 559 | } 560 | ```` 561 | 562 | 563 | ### do/while ### 564 | 565 | As opposed to its predecessor, a `do`/`while` checks the condition at the *end* of the loop. The side effect here is that the code block is always processed at least one time. 566 | 567 | ````cfc 568 | do { 569 | // statement(s) 570 | } while (booleanExpression); 571 | ```` 572 | 573 | Note that this construct requires a trailing semi-colon after the while clause. Also note that it might seem like the curly braces are needed here, but if there's only the one statement in the block, they are indeed optional, as this example demonstrates: 574 | 575 | ````cfc 576 | i=1; 577 | do writeOutput(i); 578 | while (++i <= 5); 579 | ```` 580 | 581 | However I would not recommend that, as it's sufficiently unorthodox that I think it will confuse most people. Speaking for myself, I only became aware this was possible when writing this! This is much clearer: 582 | 583 | ````cfc 584 | i=1; 585 | do { 586 | writeOutput(i); 587 | } while (++i <= 5); 588 | ```` 589 | 590 | 591 | ### Collection iteration methods ### 592 | 593 | All those previous looping constructs are very general purpose, and don't really do a great job of describing why the iteration process is being undertaken. Quite often one is looping over a collection to perform one of a subset of tasks: 594 | 595 | * transforming the values in the collection somehow: remapping it. 596 | * Filtering elements out of the collection based on a rule. 597 | * Deriving a single value or different kind of value from the elements of the collection. 598 | * Checking if some or all of the elements of a collection meet some criteria. 599 | 600 | When you think about it, you're seldom going to be looping over a collection for the heck of it: you're doing some data processing whilst you do it. CFML has an object-oriented and functional-programming approach to these operations. It has a series of collection-oriented methods which apply a callback (and optionally other parameters) to each element of the collection in turn, performing some operation to arrive at some end. 601 | 602 | There's an entire section on their methods later on, but as they are control statements after a fashion, I'll mention them here. I'll go into more depth later. 603 | 604 | For these examples I'll just show the array-specific methods. The ones for other collections are similar. 605 | 606 | 607 | #### map() #### 608 | 609 | The `map()` method creates a new collection based on the collection having `map()` called upon it, with each element somehow transformed, or with the original element used as some basis for an equivalent element in the new collection. 610 | 611 | Here we remap an array of lowercase letters, simply creating new array which has the same letters uppercased: 612 | 613 | ````cfc 614 | letters = ["a", "b", "c", "d"]; 615 | 616 | upperCaseLetters = letters.map(function(letter){ 617 | return letter.ucase(); 618 | }); 619 | ```` 620 | 621 | This returns a new array: `["A","B","C","D"]` 622 | 623 | The callback actually receives three arguments: 624 | 625 | ````cfc 626 | function(element, index, array) 627 | ```` 628 | 629 | So that's: 630 | * each element (value); 631 | * the index of that element; 632 | * a reference to the entire array. 633 | 634 | And the callback should return the value of the remapped element. 635 | 636 | When using `map()`, the returned collection will always have the same index/keys, but with different values for the elements at that index/key. 637 | 638 | 639 | #### filter() #### 640 | 641 | `filter()` does what it says on the tin: it applies a filter to the collection, filtering out elements based on the logic in the passed-in callback (which, again, is applied to each element of the collection in turn). 642 | 643 | ````cfc 644 | letters = ["a", "b", "c", "d", "e", "f"]; 645 | 646 | oddLetters = letters.filter(function(letter, index){ 647 | return index MOD 2; 648 | }); 649 | ```` 650 | 651 | Here I am using the array index rather than the element value in the callback, and this returns a boolean value wherein `true` implies the element should be preserved in the result; `false` implies the element should be filtered out of the result. So here I am keeping the elements which have an odd index number: 652 | 653 | ````cfc 654 | ["a", "c", "e"] 655 | ```` 656 | 657 | Currently ColdFusion's implementation of `filter()` has a bug in that it only receives the value of the element, not its index nor a reference to the entire collection. So the code above will only run on Lucee. Lucee correctly implements its callback like this: 658 | 659 | ````cfc 660 | function(element, index, array) 661 | ```` 662 | 663 | ColdFusion's one is simply implemented as: 664 | 665 | ````cfc 666 | function(element) 667 | ```` 668 | 669 | 670 | #### reduce() #### 671 | 672 | `reduce()` is slightly conceptually trickier than `map()` and `filter()`, which are both fairly obvious in their intent. The purpose of the `reduce()` method is to take a whole collection and derive one single value from it. This is achieved by applying a callback to each element of the collection - nothing different about that - but this time the callback receives the result of the previous callback call, as well as the next element in the collection. The `reduce()` method also takes an additional argument that is the initial value to pass to the first element's callback. 673 | 674 | ````cfc 675 | letters = ["a", "b", "c", "d", "e", "f"]; 676 | 677 | lettersAsAString = letters.reduce(function(aggregatedLetters, letter){ 678 | return aggregatedLetters & letter; 679 | }, ""); // initial value for aggregatedLetters argument 680 | ```` 681 | 682 | This returns just a string: 683 | 684 | ````cfc 685 | abcdef 686 | ```` 687 | 688 | This stands for more explanation perhaps. Let's follow through each iteration: 689 | 690 | 691 | ##### First iteration ##### 692 | 693 | `aggregatedLetters` = "" (it's initial value, as per the second argument of the `reduce()` call) 694 | 695 | `letter` = "a" (the first element of the array) 696 | 697 | So it returns `"" & "a"`, or `"a"` 698 | 699 | 700 | ##### Second iteration ##### 701 | 702 | `aggregatedLetters` = "a" (the result of the previous callback call) 703 | 704 | `letter` = "b" 705 | 706 | So it returns `"a" & "b"`, or `"ab"` 707 | 708 | 709 | ##### Third iteration ##### 710 | 711 | `aggregatedLetters` = "ab" (the result of the previous callback call) 712 | 713 | `letter` = "c" 714 | 715 | So it returns `"ab" & "c"`, or `"abc"` 716 | 717 | 718 | ##### Fourth iteration ##### 719 | 720 | `aggregatedLetters` = "abc" (the result of the previous callback call) 721 | 722 | `letter` = "d" 723 | 724 | So it returns `"abc" & "d"`, or `"abcd"` 725 | 726 | 727 | (you get the picture) 728 | 729 | So the result of one callback is what's passed into the first argument of the next callback call. 730 | 731 | The method signature for the callback is: 732 | 733 | ````cfc 734 | function(previous, current, index, array) 735 | ```` 736 | 737 | 738 | #### some() #### 739 | 740 | This method is only supported on Lucee. 741 | 742 | `some()` iterates over the collection, checking to see if an element meets some criteria (as defined by the callback). If the callback returns true, then `some()` returns `true`, and stops iterating. 743 | 744 | ````cfc 745 | numbers = [1,2,3,4,5]; 746 | 747 | hasMultipleOfThree = numbers.some(function(number){ 748 | writeOutput(number); // we're just outputing the current number here to see how many iterations some() does. 749 | return number MOD 3 == 0; 750 | }); 751 | ```` 752 | The output of this demonstrates how the iteration stops as soon as the criteria is met: 753 | 754 | ```` 755 | 123 756 | ```` 757 | 758 | Because 3 is indeed a multiple of three, the callback returns `true`, and the iteration process exits. If the iteration exercise reaches the end of the collection before a callback returns `true`, the `some()` returns `false`. 759 | 760 | The method signature for the callback is the same as you'd expect: 761 | 762 | ````cfc 763 | function(element, index, array) 764 | ```` 765 | 766 | 767 | #### every() #### 768 | 769 | This method is only supported on Lucee. 770 | 771 | `every()` is kind of the inverse of `some()`. It continues iterating whilst the callback returns `true`, and exits as soon as the callback returns `false`. If it gets to the end of the collection before returning `false`, `every()` returns `true`. 772 | 773 | ````cfc 774 | numbers = [1,2,3,4,5]; 775 | 776 | isEntirelyNumeric = numbers.every(function(number){ 777 | writeOutput(number); 778 | return isNumeric(number); 779 | }); 780 | ```` 781 | 782 | Here the output demonstrates we interate over the entire array: 783 | 784 | ```` 785 | 12345 786 | ```` 787 | 788 | Had any of the element values been non-numeric, `every()` would return `false` straight away. 789 | 790 | The method signature for the callback is the same as for `some()` 791 | 792 | ````cfc 793 | function(element, index, array) 794 | ```` 795 | 796 | 797 | ## Abstracting code ## 798 | 799 | The third aspect of flow control is the various techniques of refactoring pieces of code out of the mainline code execution. One does this for one of two reasons, generally: 800 | 801 | * code re-use 802 | * code organisation 803 | 804 | A well-written piece of code does one thing, and in a self-contained fashion. This facilitates refactoring that piece of code out of the mainline code into a separate file or function so the code can then be re-used in other situations where the functionality is appropriate. 805 | 806 | Even if the code is distinctly one-use - which is often the case, and there's nothing wrong with that - refactoring out of the mainline to make the code cleaner ([SEE APPROPRIATE CHAPTER]) is still something to strive for. 807 | 808 | Any given piece of code should do one thing. As part of doing that it might call in other code to achieve that one thing, but the detail of performing those substeps should be abstracted-out into a separate location. 809 | 810 | CFML offers a few different ways of factoring sections of logic out of the mainline code, for re-use and organisation. 811 | 812 | 813 | ### Include files ### 814 | 815 | The most basic way of factoring-out code from one file into another is using `include`. `include` facilitates executing code from one file from within another file, eg: 816 | 817 | main.cfm: 818 | ````cfc 819 | // this code will run first 820 | include "myInclude.cfm"; 821 | // this code will run third 822 | ```` 823 | 824 | myInclude.cfm 825 | ````cfc 826 | // this code will run second 827 | ```` 828 | 829 | This allows us to break a large file into smaller files. Let's say we start with a single monolithic file, homepage.cfm: 830 | 831 | ````cfc 832 | // header 833 | // code 834 | // takes 835 | // 20 lines 836 | // ... 837 | // of code 838 | 839 | 840 | // main body 841 | // is 842 | // another 843 | // 100 lines 844 | // ... 845 | // of code 846 | 847 | 848 | // and the footer 849 | // is 10 lines 850 | // ... 851 | // of code 852 | ```` 853 | 854 | There's clearly three sections of code there, which quite reasonably are needed to define the home page, but within that home page are discrete chunks of logic / mark-up. We could refactor that homepage.cfm from 130 lines of code down to three: 855 | 856 | ````cfc 857 | include "header.cfm"; 858 | include "body.cfm"; 859 | include "footer.cfm"; 860 | ```` 861 | 862 | And simply break down the code thus: 863 | 864 | header.cfm 865 | ````cfc 866 | // header 867 | // code 868 | // takes 869 | // 20 lines 870 | // ... 871 | // of code 872 | ```` 873 | 874 | body.cfm 875 | ````cfc 876 | // main body 877 | // is 878 | // another 879 | // 100 lines 880 | // ... 881 | // of code 882 | ```` 883 | 884 | footer.cfm 885 | ````cfc 886 | // and the footer 887 | // is 10 lines 888 | // ... 889 | // of code 890 | ```` 891 | 892 | So the homepage.cfm code now just describes what comprises the home page, but the implementation detail has been factored out into more wieldy chunks. If one has to do some maintenance or enhancements to the header, one only needs to work on a smaller file with just the 20 lines of header code in it. Similarly if maintaining the main body, one focuses on just the code in body.cfm. 893 | 894 | This also sets one up nicely for creating other pages which need the same header and footer: they can simmply include those header.cfm and footer.cfm files too. 895 | 896 | When using `include` it is almost as if the CFML engine inserts the code from the included file into the file including it, and then executes the code as one piece. This is similar to how C implements the `#include` directive. This is not quite what happens. The CFML compiler compiles the included file separately and *before* it's included into the file including it. Then the code is executed. This means the CFML code in an included file needs to be syntactically complete so it can be compiled. Here's an example to demonstrate: 897 | 898 | Original code: 899 | ````cfc 900 | switch (colour){ 901 | case "red" : 902 | writeOutput("FF0000"); 903 | break; 904 | case "green" : 905 | writeOutput("00FF00"); 906 | break; 907 | case "blue" : 908 | writeOutput("0000FF"); 909 | break; 910 | } 911 | ```` 912 | 913 | Attempt to refactor code: 914 | ````cfc 915 | switch (colour){ 916 | include "cases.cfm"; 917 | } 918 | ```` 919 | 920 | cases.cfm 921 | ````cfc 922 | case "red" : 923 | writeOutput("FF0000"); 924 | break; 925 | case "green" : 926 | writeOutput("00FF00"); 927 | break; 928 | case "blue" : 929 | writeOutput("0000FF"); 930 | break; 931 | ```` 932 | 933 | This is illegal because neither file is left syntactically correct. Lucee gives this compile error: 934 | 935 | ```` 936 | invalid construct in switch statement 937 | ```` 938 | 939 | And ColdFision gives this (slightly clearer) one: 940 | 941 | ```` 942 | Only case: or default: statements may be immediately contained by a switch statement. 943 | ```` 944 | 945 | cases.cfm also will not compile: 946 | 947 | ```` 948 | Missing [;] or [line feed] after expression 949 | ```` 950 | 951 | (Lucee cannot even work out what the code is supposed to be doing here, so the error message is a bit misleading). 952 | 953 | When the code *does* compile, it's important to observe that the included code will run in the same memory context as the code that includes it. So variables set in the main code will be directly available in the included file, and variables set or changed in the included file will also be available in the main code after the include completes. 954 | 955 | EG: 956 | 957 | main.cfm 958 | ````cfc 959 | myVar = "set in main.cfm
"; // outputs "set in main.cfm" 960 | writeOutput(myVar); 961 | 962 | include "myInclude.cfm"; 963 | 964 | writeOutput(myVar); // outputs "updated in myInclude.cfm" 965 | ```` 966 | 967 | myInclude.cfm 968 | ````cfc 969 | writeOutput(myVar); // outputs "set in main.cfm" 970 | 971 | myVar = "updated in myInclude.cfm
"; 972 | ```` 973 | 974 | The output of this is: 975 | 976 | ```` 977 | set in main.cfm 978 | set in main.cfm 979 | updated in myInclude.cfm 980 | ```` 981 | 982 | So throughout execution there, `myVar` is the same variable in both files. This is important to note because in all the other ways of abstracting code, the abstracted code runs in its own memory space (which is a good thing). 983 | 984 | `include` is the most simple way of abstracting code from one file into another, but it's probably the least good way of doing so. On the whole one would seldom use `include` when writing modern code. 985 | 986 | 987 | ### Modules ### 988 | 989 | Modules work similarly to includes, except for two main factors: 990 | 991 | * modules use their own memory space, so their code doesn't interfere with the calling code; 992 | * modules can be used as custom tags ([SEE APPROPRIATE CHAPTER]), indeed this is their primary use. 993 | 994 | There's a chapter dedicated to custom tags so I will not venture further down that route just yet. The syntax for calling in a module is confusingly different from using `include`: 995 | 996 | ````cfc 997 | cfmodule(template="myModule.cfm"); 998 | ```` 999 | 1000 | Because myModule.cfm is run in its own memory space, it cannot simply refer to variables in the mainline code, as they are inaccessible. So to access them, they need to be passed into the module as an attribute/value pair: 1001 | 1002 | ````cfc 1003 | myVar = "set in main.cfm
"; 1004 | cfmodule(template="myModule.cfm", message=myVar); 1005 | ```` 1006 | 1007 | I'll extend that example and show how the module runs in a different memory context: 1008 | 1009 | main.cfm 1010 | ````cfc 1011 | myVar = "set in main.cfm
"; 1012 | cfmodule(template="myModule.cfm", message=myVar); 1013 | 1014 | writeOutput("Value of myVar in main.cfm after myModule.cfm has run: " & myVar); 1015 | 1016 | writeOutput("Value of updatedMessage in main.cfm after myModule.cfm has run: " & updatedMessage); 1017 | ```` 1018 | 1019 | myModule.cfm 1020 | ````cfc 1021 | myVarExists = isDefined("myVar"); 1022 | writeOutput("Does myVar exist in myModule.cfm? [#myVarExists#]
"); 1023 | 1024 | writeOutput("Value of attributes.message in myModule.cfm: " & attributes.message); 1025 | 1026 | attributes.message = "updated in myModule.cfm
"; 1027 | writeOutput("Updated value of attributes.message in myModule.cfm: " & attributes.message); 1028 | 1029 | caller.updatedMessage = attributes.message; 1030 | ```` 1031 | 1032 | This outputs: 1033 | 1034 | ```` 1035 | Does myVar exist in myModule.cfm? [false] 1036 | Value of attributes.message in myModule.cfm: set in main.cfm 1037 | Updated value of attributes.message in myModule.cfm: updated in myModule.cfm 1038 | Value of myVar in main.cfm after myModule.cfm has run: set in main.cfm 1039 | Value of updatedMessage in main.cfm after myModule.cfm has run: updated in myModule.cfm 1040 | ```` 1041 | 1042 | Notice these things: 1043 | 1044 | * `myVar` only exists in main.cfm; it does *not* exist in myModule.cfm 1045 | * one passes its value into the module using an attribute, then in myModule.cfm there is a variable `attributes.message` available. 1046 | * changing the value in the module does not impact the original variable in the calling code. 1047 | * one can set variables in the calling context by using the `caller` "scope". 1048 | 1049 | I'll discuss the scoping of variables later; it's sufficient to see it working at the moment. 1050 | 1051 | Again, like using `include`, using `cfModule()` isn't really a great way of abstracting code. Indeed you're even less likely to use it as an approach than `include`. As I touched on above, modules are generally used for custom tags, not being called directly. But it's a handy example to compare the way the different memory contexts work (comparing an included file and a... "moduled"... file). 1052 | 1053 | 1054 | ### Functions ### 1055 | 1056 | We're finally in the territory of code abstraction techniques you *do* want to use. A function is the work-horse way of abstracting code for later / repetitive use. Generally one would organise one's functions into classes, but I'll get to that. 1057 | 1058 | Functions abstract code into its own reusable context, giving it a name, a mechanism to pass values into the code for use, and return a value when done: 1059 | 1060 | ````cfc 1061 | function sumTwoNumbers(n1, n2){ 1062 | var sum = n1 + n2; 1063 | return sum; 1064 | } 1065 | 1066 | x1 = 2; 1067 | y1 = 3; 1068 | 1069 | writeOutput("#x1# + #y1# = #sumTwoNumbers(x1,y1)#
"); 1070 | 1071 | x2 = 5; 1072 | y2 = 7; 1073 | 1074 | writeOutput("#x2# + #y2# = #sumTwoNumbers(x2,y2)#
"); 1075 | ```` 1076 | 1077 | This outputs: 1078 | ```` 1079 | 2 + 3 = 5 1080 | 5 + 7 = 12 1081 | ```` 1082 | 1083 | The function `sumTwoNumbers()` defines that it takes two arguments: `n1` and `n2` (you'd normally give more descriptive names than this!). And the body of the function simply sums those two values, and then returns them. 1084 | 1085 | In the calling code we've got two pairs of completely different variables (`x1`, `y1` and `x2`, `y2`), which we pass - in turn - into `sumTwoNumbers()` as arguments. At no point does the code within `sumTwoNumbers()` refer to any variables in the calling code; and at no point does the calling code reference any variables or code in the function. 1086 | 1087 | This is thing about functions: they ought to be completely encapsulated: they should only work with values being passed into them as arguments, do some processing (with no side effects anywhere else), and return the result of the processing. The calling code leverages this by calling the function with whichever values are necessary, and then using its result. The calling code doesn't need to know *how* the function does its work, it just needs to know how to call the function and to expect its result. 1088 | 1089 | In this example, we could change the implementation of the function: 1090 | 1091 | ````cfc 1092 | function sumTwoNumbers(n1, n2){ 1093 | var sum = 0; 1094 | sum += n1; 1095 | sum += n2; 1096 | return sum; 1097 | } 1098 | ```` 1099 | 1100 | And the calling code stays the same. 1101 | 1102 | Note that one *generally* wouldn't have the function defined in the main code like this, but it's just easier for the example code. And it's important to see how a function operates in its own memory context. 1103 | 1104 | Do note though, how when I am declaring a new variable inside the function, I use the `var` keyword: 1105 | 1106 | ````cfc 1107 | var sum = 0; 1108 | ```` 1109 | 1110 | Using the `var` keyword, the `sum` variable is made local to the function's memory context. Had I *not* used the `var` keyword, then `sum` would have been created in the calling code's memory context. This demonstrates that the code within a function *can* access variables in the maintain code, and set them too. However this should be avoided. The only values a function should use are its argument values, and other values it derives from those. It should *not* directly reference variables from its calling context. The main reason for this is that it means the function actually relies on its calling code to be able to work, which makes it less portable. 1111 | 1112 | 1113 | ### Classes ### 1114 | 1115 | The way one *should* organise one's functions is to put them into a class (for some reason CFML refers to them as "components" in their definition: but just think "class"). Like most OO languages, this is the primary way CFML organises its code, and where almost all of your code will go. 1116 | 1117 | There's an entire section on CFML's OO implementation later on, I'm just doing to discuss the code-abstraction side of things here. 1118 | 1119 | CFML has two different types of code file: a general script file, which just contains procedural code, with the file extension .cfm. These are the files one would use for views or for the entry point into your application. Or if indeed you just needed to write a quick test script like the code I've been listing as we go in this document. If one browses to a web-accessible .cfm file, it will execute, running the code from top to bottom. 1120 | 1121 | The other file type is a CFC (.cfc extension), or "ColdFusion Component". These are not directly executable like .cfm files, they are used to define classes. A function encapsulates logic into a transportable / re-usable unit; a class encapsulates the description of a type of data (made up of properties), plus all its behaviour (methods) into one unit. The basic programming language has a sense of a string, or an array or what-have-you, but it's up to the application to define what - for example - it means to be a Person (or a Vehicle, or a Queue, etc). A Person will have properties (data values) like `firstName` and `lastName`, and it might have a method (behaviour) `getFullName()` which returns what it suggests in its name, based on the values for `firstName` and `lastName`: 1122 | 1123 | ````cfc 1124 | component accessors=true { 1125 | property firstName; 1126 | property lastName; 1127 | 1128 | function init(firstName, lastName){ 1129 | setFirstName(firstName); 1130 | setLastName(lastName); 1131 | } 1132 | 1133 | function getFullName(){ 1134 | return firstName & " " & lastName; 1135 | } 1136 | } 1137 | ```` 1138 | 1139 | And we can then use that Person class to create a Person object in our main code: 1140 | 1141 | ````cfc 1142 | friend = new Person("Isla", "Jeffries"); 1143 | 1144 | fullNameOfFriend = friend.getFullName(); 1145 | writeOutput(fullNameOfFriend); 1146 | ```` 1147 | 1148 | This outputs: 1149 | 1150 | ```` 1151 | Ilsa Jeffries 1152 | ```` 1153 | 1154 | Here the main code doesn't need to know anything about what it is to be a Person, all it needs to know is the method signatures for `init()` and `getFullName()`. All the rest is encapsulated away inside Person.cfc 1155 | 1156 | 1157 | ## Summary ## 1158 | 1159 | Left to its own devices, code will just execute top to bottom. Obviously that's often not going to be much use: we need to make decisions; repeat tasks; and organise our code into easy to follow, clear units of work; and re-usable elements where possible. CFML's got a lot of options to implement good clean code. -------------------------------------------------------------------------------- /chapters/03-types.md: -------------------------------------------------------------------------------- 1 | # Built-in types # 2 | 3 | ## Typefulness ## 4 | 5 | Firstly, CFML is loosely typed, and dynamically typed. You might hear that it's "typeless": it's not. It's very much not. So get that idea out of your head, and chide anyone who suggests it to you. 6 | 7 | 8 | ### Loosely typed ### 9 | 10 | Loosely typed means that one doesn't need to declare a variable's type when declaring the variable. In Java (strongly typed), one might need to do this sort of thing: 11 | 12 | ````cfc 13 | Person author = new Person("Adam", "Cameron"); 14 | ```` 15 | 16 | We need to say that the author variable is going to hold a Person object. Despite that being obvious by the object one is putting in the variable. In CFML one would simply need to do this: 17 | 18 | ````cfc 19 | author = new Person("Adam", "Cameron"); 20 | ```` 21 | 22 | The CFML compiler can work out for itself that author is a `Person`. Or indeed the compiler simply doesn't care: the *runtime* can work it out. 23 | 24 | Also - and we're crossing over into dynamic typing a bit here - a variable's type is not set in stone once it's declared. In a strongly typed language, one cannot do this: 25 | 26 | ````cfc 27 | Colleague person = new Colleague("Aaron"); 28 | // ... later on... 29 | person = new Friend("Brigitta"); 30 | ```` 31 | 32 | The `person` variable is a `Person`, so we cannot change our mind and assign it to a `Friend` object later. 33 | 34 | In CFML we can reassign a variable to be any type, any time we like. It's how we use the variable that matters, not what we declare it to be when first decide to create it. 35 | 36 | 37 | ### Dynamically typed ### 38 | 39 | In a statically typed language, only certain operations are available to certain data types. For example, in Java one cannot do this: 40 | 41 | ````cfc 42 | String x = "17"; 43 | String y = "24"; 44 | 45 | Integer product = x * y; // expecting it to be 408 46 | ```` 47 | 48 | This doesn't work because one cannot multiply strings. 49 | 50 | CFML doesn't care. This'd work fine: 51 | 52 | ````cfc 53 | x = "17"; 54 | y = "24"; 55 | 56 | product = x * y; 57 | ```` 58 | 59 | The CFML runtime will go "yeah, x and y have a numeric values, so I can multiply those no worries". This extends to most operations with in-build CFML types: if the runtime can make sense out of your code, it will. For example, our product variable can be used as a boolean too: 60 | 61 | ````cfc 62 | if (product) // do something 63 | ```` 64 | 65 | Because the rules for a boolean include "any non-zero numeric value is true", then that code runs fine. 66 | 67 | 68 | ### Curate's egg ### 69 | 70 | This is great because it cuts down on a lot of ceremonial boilerplate code, and gets on with it. It is - after all - the computer's job to sort out the menial, repetitive side of code execution, not the developers. The more code just defines what needs doing, rather than the minutiae of how it needs to be done, the better. 71 | 72 | This can be a double-edged sword though. In a statically- and strongly-typed language, the compiler can pick up a lot of mistakes during compilation. For example it could be an accident that you used `x` and `y` in that arithmetic expression. In a strongly-typed compiler, that code wouldn't even compile, let along allow you to run it. 73 | 74 | Another benefit here is that given all that sort of type checking is done at compile time, it means the runtime process doesn't need to do it, meaning it can be lighter-weight. Remember that code is compiled once, and its run many many many times, so it makes sense to do this sort of work once, rather than every time the code is run. 75 | 76 | Equally an IDE's job is a lot easier in a statically-typed language, as it's easier for it to keep track of what type a variable is at any given time (as it can only ever have one type, and your code clearly states what that is. So it's hard to get it wrong!). This means the IDE can more easily be helpful at code-time by offering code hinting (eg: which methods are available to call on an object), and can highlight what will be compile errors (eg: using a numeric where a boolean is needed). 77 | 78 | To be frank: I think the benefits of the compile-type checking are minimal in a lot of situations. If you write your code clearly in the first place, you're less likely to mess it up. In my example above "`x`" and "`y`" are terrible variable names in most situations. The variable name in no way describes what the variable holds. Even product is a poor variable name: it could equally mean the result of a multiplication, or it might be like a product from one's inventory. 79 | 80 | Also the difference between compile-time and run-time type checking is significant in some situations, but not really so much in the environment CFML is designed for. Type-checking is a pretty quick operation compared to the latency of fulfilling an HTTP request, or grabbing data from a DB, or reading a file, etc. I mean orders of magnitude different. And the sort of job CFML code is generally doing is intrinsically going to have a bunch of that slower stuff in it, so the slight hit of doing runtime type-checking will disappear in the background noise. That's not to say there's no consideration there at all, but not one I'd be worrying about too much. 81 | 82 | It's true that one is opened-up to a greater risk of runtime errors with runtime type checking. However your code ought to be developing using TDD (see relevant chapter), so it should a be checked for runtime validity before it's released anyhow. Even if you don't use TDD - but you should - you must still have post-fact unit test coverage to test your logic actually works, and to proof you against regressions between iterations of your code. Unit testing cannot proof against all runtime errors, but using unit testing, integration testing and end-to-end testing should reveal most issues before you release the code. And compile-time error checking is still not a panacea... there's plenty of stuff that can go wrong at run-time in strongly/statically typed code that compiles OK. 83 | 84 | One thing CFML's loose/dynamic typing doesn't help us with at all is within the IDE. Writing Java code in Eclipse or C# code in Visual Studio is a pleasure, because the IDE can help you at every turn, and knows whenever you're writing code that won't compile. It can even write code for you in some situations. This is - for all intents and purposes - impossible with CFML. Still: I've got by for over a decade (as have really a lot of people) without IDE bells and whistles, so whilst the bells and whistles are bloody handy, it's not like one is paralysed if one doesn't have them. 85 | 86 | Another very frustrating shortcoming of CFML is that it falls over itself to be dynamically typed. So one can have a variable containing "1P", and CFML will interpret that as "1899-12-30 13:00:00". "Dec 30 1899" is CFML's "zero" date, so ColdFusion is seeing "1p" as "1pm" there, and without the date component, assumes the zero-date for all other date parts. This is very annoying, but is an example of "dynamic typing gone mad". And this is one of many situations in which CFML will fall over itself to misinterpret your intent under the guise of trying to make sense of your code. 87 | 88 | I would *love* to be able to do this in CFML: 89 | 90 | ````cfc 91 | String definitelyAString = ""; 92 | ```` 93 | 94 | And for the CFML engine to not ever try to treat it as anything other than a string. Often this is not necessary, but sometimes it would just make life easier. 95 | 96 | 97 | ## Simple vs complex types ## 98 | 99 | A simple type is one that has only one component to it, as opposed to many parts that are internally organised (complex types). Examples of simple types are strings, numerics, dates. A complex type is something like an array or a struct. An array is a collection of ordered elements, each of which have their own value. As a simple type has only one value, one can access it directly, eg: 100 | 101 | ````cfc 102 | firstName = "Charlie"; 103 | writeOutput(firstName); // outputs "Charlie" 104 | ```` 105 | 106 | On the other hand a complex type can either be referenced as a whole, or by individual element, eg: 107 | 108 | ````cfc 109 | person = {firstName="Emma", lastName="Farmer"}; 110 | outputWholeName(person); // passing the entire struct 111 | writeOutput(person.firstName); // a reference to just "Emma" 112 | ```` 113 | 114 | I'll explain arrays / structs further down, but note how with simple values there's just the one part, whereas with a complex value one can reference the entire value, or an individual part of the value. In this case I use dot-notation to reference a struct's individual element. 115 | 116 | Note: as a rule, simple values are passed by value-copy (ie: the entire value is duplicated); complex values are passed by reference-copy (only the reference is copied, not the value the reference points to). Except for arrays in ColdFusion (but not Lucee), which are also passed by value-copy. Also note that nothing in CFML is "passed by reference" in the definitive sense of that concept. 117 | 118 | 119 | ## Calling methods on a value ## 120 | 121 | CFML historically was a procedural language (although this goes back a decade now), so most of its value-manipulation is done via procedural-oriented headless functions, eg: 122 | 123 | ````cfc 124 | nameBackwards = reverse("Gina"); // aniG 125 | ```` 126 | In the current versions of CFML supported by vendor engines, the native language has finally gone OO, so one can call a method on a variable: 127 | 128 | ````cfc 129 | name = "Harry"; 130 | nameBackwards = name.reverse(); // yrraH 131 | ```` 132 | 133 | One should aim to write as modern code as possible, so I will be advocating using methods rather than headless functions. This makes sense as the CFML one writes will be OO, so it makes for more uniform code to also use CFML's native OO features too. 134 | 135 | A time of writing, the docs for ColdFusion are still headless-function-centric, unfortunately. However a list of all of them can be found here: https://wikidocs.adobe.com/wiki/display/coldfusionen/Using+the+member+functions. 136 | 137 | 138 | ## Native types ## 139 | 140 | CFML actually has quite a number of built-in types, which all have discrete behaviour. 141 | 142 | 143 | ### Simple types ### 144 | 145 | #### String #### 146 | 147 | The most fundamental data type in CFML is a string. In ColdFusion any literal simple value is by default stored as a string. In Lucee a numeric literal is actually stored as a numeric (etc). However this does not make any difference in how the value is subsequently treated. 148 | 149 | For example, these are *all* strings: 150 | 151 | ````cfc 152 | name = "Jim"; 153 | age = 54; 154 | dateOfBirth = "1961-03-24"; 155 | ```` 156 | 157 | To a human we have a string, a number (most likely an integer), and a date there. To ColdFusion they're all strings. And for all intents and purposes they are all strings on Lucee too: 158 | 159 | ````cfc 160 | writeOutput(len(age)); // 2 (54 being two characters long) 161 | ```` 162 | 163 | In CFML string literal values are expressed via a value delimited by either a double-quote or a single-quote: 164 | 165 | ````cfc 166 | firstName = "Karen"; 167 | lastName = 'Long'; 168 | ```` 169 | 170 | If one needs to use one of those quotes within the string literal value, there are two options: 171 | 1. use the other delimiter 172 | 2. escape the same delimiter by doubling it up 173 | 174 | eg: 175 | 176 | ````cfc 177 | stringContainingDoubleQuote = 'my string has a " within it'; 178 | stringContainingDoubleQuote = "my string has a "" within it"; // the value is: my string has a within it 179 | ```` 180 | 181 | Where possible, I just use the alternate delimiter in these situations instead of doubling it up, as it is slightly clearer. 182 | 183 | Unlike in some other languages, there is no difference between either delimiter. 184 | 185 | My personal preference is for using double quotes, but there is absolutely no hard & fast rule here. My rationale is simply that single quotes are more likely to be part of the string (eg: as an apostrophe), than a double-quote will be. If I was to be pedantic, I'd also observe that " is a quotation mark, but ' is actually an aporstrophe (it is *not* a single quote), so it doesn't make a great deal of sense to use an apostrophe as a quote. But as I said: that's pedantry. 186 | 187 | 188 | ##### String interpolation ##### 189 | 190 | CFML's string interpolation is performing using the `#` ("hash" or "pound-sign", depending on which vagary of English you speak) character: 191 | 192 | ````cfc 193 | firstName = "Michelle"; 194 | greeting = "G'day #firstName#"; 195 | writeOutput(greeting); // G'day Michelle 196 | ```` 197 | 198 | In CFML one can have any expression at all between the delimiting `#` characters, provided it resolves to a string, or can be coerced into a string: 199 | 200 | ````cfc 201 | writeOutput("Today is #now().dateFormat("dddd, d mmmm yyyy")#"); // Today is Saturday, 24 January 2014 202 | ```` 203 | 204 | Notice in the expression above I can use double-quotes quite happily even though the string delimiter in this case is itself a double quote. This is valid because the expression is evaluated as a separate unit to the output of the string. 205 | 206 | An example of type-coercion here would be this: 207 | 208 | ````cfc 209 | writeOutput("Today is #now()#"); // Today is Today is {ts '2015-01-24 14:30:30'} 210 | ```` 211 | 212 | `now()` returns a date, but to interpolate it, the expression's value needs to be a string, so CFML will automatically convert the date into a string (resulting in that rather unwieldy "{ts '2015-01-24 14:30:30'}"). This demonstrates CFML's automatic casting can be a bit "generic", hence using `dateFormat()` in the first example to make the output more human readable. As you probably worked out `dateFormat()` converts a date object to a string, using the argument as a formatting string. 213 | 214 | Note that CFML cannot automatically coerce any aribitrary type back to a string. It won't coerce an array or a struct, for argument's sake: 215 | 216 | ````cfc 217 | person = {firstName="Nigel", lastName="Olsen"}; 218 | writeOutput("Candidate's name is #person#"); 219 | ```` 220 | 221 | This yields a runtime exception along the lines of: 222 | 223 | > Can't cast Complex Object Type Struct to String 224 | 225 | It would be reasonable if CFML automatically coerced a struct or array into - for example - JSON if one tries to use one as a string, but this has yet to be implemented. 226 | 227 | To use a literal `#` character in a string, simply escape it by doubling it: 228 | 229 | ````cfc 230 | writeOutput("CFML uses ## as its string interpolation delimiter"); // CFML uses # as its string interpolation delimiter 231 | ```` 232 | 233 | Documentation for string methods can be found: 234 | 235 | 1. ColdFusion: https://wikidocs.adobe.com/wiki/display/coldfusionen/Using+the+member+functions#Usingthememberfunctions-SupportedStringmemberfunctions 236 | 237 | 2. Lucee: has different options, but at present I do not have a link to the docs for its variations. 238 | 239 | 240 | ##### Comparing strings ##### 241 | 242 | One can use comparison operators (ie: `==`, `>`, `<=` etc) to compare strings: 243 | 244 | ````cfc 245 | s1 = "lower"; 246 | s2 = "upper"; 247 | 248 | if (s1 < s2) { 249 | // and it is, so this block will run 250 | } 251 | ```` 252 | 253 | This can cause issues in a couple of ways. Because of CFML's loose typing, it has to guess what the operand's types are. In the case above, they're definitely both strings. But CFML will get it "wrong" in this situation: 254 | 255 | ````cfc 256 | s1 = "7"; 257 | s2 = "007"; 258 | 259 | writeOutput(s1 == s2); // TRUE 260 | ```` 261 | 262 | However when comparing *strings*, those two are not the same. To force CFML to compare strings *as strings*, then use the `compare()` function: 263 | 264 | 265 | ````cfc 266 | s1 = "7"; 267 | s2 = "007"; 268 | 269 | writeOutput(s1.compare(s2)); // 1 270 | ```` 271 | 272 | `compare()` returns `-1`, `0` or `1` depending on whether the first string is less than, equal to or greater than the second string. Note that the result from `compare()` *cannot* clearly be used in a boolean condition because the `less than` (`-1`) and `greater than` (`1`) results equate to a boolean `true`, and the `equals` (`0`) result equates to boolean `false`. This can be misleading: 273 | 274 | 275 | ````cfc 276 | s1 = "7"; 277 | s2 = "007"; 278 | 279 | if (s1.compare(s2)){ 280 | // they're NOT equal 281 | }else{ 282 | // they ARE equal 283 | } 284 | 285 | ```` 286 | 287 | This is one situation in which it's better to be explicit: 288 | 289 | ````cfc 290 | s1 = "7"; 291 | s2 = "007"; 292 | 293 | if (s1.compare(s2) == 0){ 294 | // they're equal 295 | }else{ 296 | // they're NOT equal 297 | } 298 | 299 | ```` 300 | 301 | Comparisons are done lexically, according to the strings' characters' char codes. This can lead to misleading results with accented characters, as their char codes are all higher than the unaccented characters. EG: 302 | 303 | ````cfc 304 | s1 = "é"; 305 | s2 = "z"; 306 | 307 | writeOutput(s1 < s2); // false 308 | writeOutput(s1.compare(s2)); // 1 309 | writeOutput("#asc(s1)# #asc(s2)#"); // 233 122 310 | ```` 311 | 312 | Obviously (?) in a human-based collation order, `é` falls within the vicinity of `e`, not well after `z`. 313 | 314 | Also note that doing comparisons with the comparison operators does a case-*in*sensitive comparison, whereas `compare()` is case-sensitive. To do a case-insensitive comparison using string comparisons, use `compareNoCase()`. 315 | 316 | 317 | ##### Common string methods ##### 318 | 319 | CFML has a large number of string methods, and they're all well documented so I won't go over all of them here. However here's a summary of some common ones: 320 | 321 | ````cfc 322 | // find a substring 323 | haystack = "string"; 324 | needle = "in"; 325 | position = haystack.find(needle); // 4 326 | 327 | // replace a substring 328 | base = "string"; 329 | original = "in"; 330 | replacement = "un"; 331 | updated = base.replace(original, replacement); // strung 332 | 333 | // length 334 | string = "sample"; 335 | length = string.len(); // 6 336 | 337 | // left() / right() / mid() 338 | string = "left bit middle section right end"; 339 | leftBit = string.left(8); // left bit 340 | middleBit = string.mid(10, 14); // middle section 341 | rightBit = string.right(9); // right end 342 | 343 | // trim() 344 | padded = " actual value here "; 345 | trimmed = padded.trim(); // actual value here 346 | 347 | // ucase() / lcase() 348 | mixedCase = "A Sample String"; 349 | uppercase = mixedcase.ucase(); // A SAMPLE STRING 350 | lowercase = mixedcase.lcase(); // a sample string 351 | ```` 352 | 353 | Note that in CFML, the offset values are 1-based not 0-based. The first character of a string is in position 1, not position 0. 354 | 355 | As I mentioned above, there are a *lot* more string methods than that: https://wikidocs.adobe.com/wiki/display/coldfusionen/Using+the+member+functions#Usingthememberfunctions-SupportedStringmemberfunctions 356 | 357 | 358 | ##### Strings as delimited lists ##### 359 | 360 | CFML has a concept of a "list" which is sometimes mistaken for a discrete data type. It is not, it is simply a series of functions which deal with delimited strings. EG: 361 | 362 | ````cfc 363 | letters = "a,b,c,d"; // a list with four elements:a, b, c and d. The delimiter is a comma 364 | writeOutput(letters.len()); // 7 365 | writeOutput(letters.listLen()); // 4 366 | 367 | writeOutput(letters.listGetAt(2)); // b 368 | 369 | writeOutput(letters.find("c")); // 5 370 | writeOutput(letters.listFind("c")); // 3 371 | ```` 372 | 373 | A list is a string. List functions compartmentalise strings via treating character(s) as an element delimiter rather than part of the data. By default the delimiter is a comma, however all list methods will accept a string argument which can specify alternative delimiters. Each character in the string is treated as a discrete delimiter: 374 | 375 | ````cfc 376 | letters = "a|b/c-d"; 377 | writeOutput(letters.listLen("|/-")); // 4 378 | ```` 379 | 380 | List functions *can* be useful with some pre-existing data structures though. For example to extract the file name from a path, one could do this: 381 | 382 | ````cfc 383 | path = "/path/to/file.cfm"; 384 | fileName = path.listLast("/"); // file.cfm 385 | ```` 386 | 387 | Lists have been popular in the past, but are restrictive and slow performers. If you want to use a compound data type, generally an array would be a better fit. Try to restrict usage of list functionality to converting a string to an array, and then performing other manipulation on the array. 388 | 389 | 390 | ##### Regular expressions ##### 391 | 392 | CFML has pretty good regular expression support. Regex patterns are not discrete objects, but are just strings which are passed to a relevant method, eg: 393 | 394 | ````cfc 395 | heading = "this is a heading"; 396 | titleCase = heading.reReplace("((?:^|\s))(.)", "\1\U\2\E", "all"); // This Is A Heading 397 | ```` 398 | 399 | (that pattern is not foolproof, I know, but it demonstrates the point). 400 | 401 | CFML has three main regular expression methods: 402 | - `reFind()` 403 | - `reReplace()` 404 | - `reMatch()` 405 | 406 | Those are call case sensitive. There are specific case-insensitive variants too: 407 | - `reFindNoCase()` 408 | - `reRelaceNoCase()` 409 | - `reMatchNoCase()` 410 | 411 | It is outside the scope of this book to teach regular expression usage. I have however blogged about regular expressions in CFML fairly thoroughly: http://blog.adamcameron.me/2015/01/regular-expressions-in-cfml-link-summary.html 412 | 413 | 414 | ##### CFML strings are Java Strings ###### 415 | 416 | CFML is a JVM-based language, and CFML compiles down to Java byte code. CFML strings are simply wrappers for java.lang.String objects. One can call any Java String method on a CFML string natively, eg: 417 | 418 | ````cfc 419 | string = "this is a string"; 420 | letters = string.toCharArray(); // ["t","h","i","s"," ","i","s"," ","a"," ","s","t","r","i","n","g"] 421 | ```` 422 | 423 | Here `toCharArray()` is not a CFML method, it's a native Java one. 424 | 425 | 426 | ##### Documentation for string methods ##### 427 | 428 | 1. ColdFusion: https://wikidocs.adobe.com/wiki/display/coldfusionen/Using+the+member+functions#Usingthememberfunctions-SupportedListmemberfunctions 429 | 2. Java: http://docs.oracle.com/javase/8/docs/api/java/lang/String.html 430 | 431 | 432 | #### Numeric #### 433 | 434 | Natively CFML does not distinguish between integer numbers and floating point ones: it's just got "numeric". Under the hood, these are java.lang.Double objects by default. Well: *by default* on ColdFusion they're strings until they need to be used as a numeric, really: 435 | 436 | ````cfc 437 | pi = 3.14; 438 | writeOutput(pi.getClass().getName()); // java.lang.String 439 | pi += 0; 440 | writeOutput(pi.getClass().getName()); // java.lang.Double 441 | ```` 442 | 443 | On Lucee the `pi` variable is a Double from the outset. 444 | 445 | There's not much more to say about numeric values. As per the example above, the syntax for declaring a numeric literal is simply to type it as is: 446 | 447 | ````cfc 448 | answer = 42; 449 | ```` 450 | 451 | There is no syntax variation to express a literal float as opposed to an integer as there is in some languages. 452 | 453 | 454 | #### Date #### 455 | 456 | Dates in CFML represent a date accurate to the millisecond. There is no literal way to declare 457 | a date in CFML, one needs to use a function which returns one, eg: 458 | 459 | ````cfc 460 | d = createDate(2011,3,24); 461 | d = now(); 462 | d = lsParseDateTime("24/3/2011"); 463 | ```` 464 | 465 | Due to CFML's aggressive type coercion, one can generally get away with using a well-formatted string in place of a date object in any situation expecting a date. For example this works: 466 | 467 | ````cfc 468 | dateAsString = "2011-03-24"; 469 | followingDay = dateAdd("d", 1, dateAsString); // the date 2011-03-25 470 | ```` 471 | 472 | In the past I strongly recommended against ever using a string when a date was expected, but I have softened on this now. Provided one uses an unambiguous string format, I think it's fine. Note that the format mm/dd/yyyy which people from the United States might use is *not* unambiguous. A reader from USA would think 09/11/2001 is that terrible day we will never forget. However to me - and most people from outside USA in the English-speaking world, that date is November 9, not September 11. It all depends on your locale as to which way round the `d` and the `m` components are considered. If using a string, use some subset of `yyyy-mm-dd HH:mm:ss`. 473 | 474 | 475 | ##### ODBC date objects ##### 476 | 477 | CFML has some functions (`createOdbcDate()` etc) to support ODBC-styled date objects ostensibly intended to be used when connecting to ODBC data sources. Neither Lucee nor ColdFusion use ODBC any more (and haven't for over a decade), so these functions are pointless. Data sources all use JDBC these days, and a CFML date object will pass seamlessly through JDBC to the DB. Don't use the ODBC-oriented functions. 478 | 479 | 480 | ##### Localised values ##### 481 | 482 | CFML has a number of date formatting functions which return strings, eg: `dateFormat()`. These return USA-centric values, and are no good for internationalised sites. All output should be locale-aware, so do not use functions like `dateFormat()`, `parseDateTime()`. Use the localised equivalents: `lsDateFormat()`, `lsParseDateTime()`. 483 | 484 | 485 | ##### Unexpected behaviour ##### 486 | 487 | When it comes to dates, CFML's aggressive type coercion can be dangerous, and lead to unexpected results. For example `0` is a "valid" date, it will be considered `1899-12-31` which is the zero-date as far as CFML is concerned. Equally `1p` will be interpretted as `1pm` on that date. This is seldom what one wants, so if one starts seeing odd behaviour around dates, check what values you're using. 488 | 489 | 490 | ##### Common date methods ##### 491 | 492 | ````cfc 493 | dateToday = now(); // 2015-01-25 494 | 495 | dateTomorrow = dateToday.add("d", 1); // 2015-01-26 496 | oneMonthAgo = dateToday.add("m", -1); // 2014-12-25 497 | 498 | weeksBetween = oneMonthAgo.diff("w", dateTomorrow); // -4 499 | 500 | currentHour = dateToday.hour(); // 14 501 | 502 | ordinalDayOfWeek = dateToday.dayOfWeek(); // 1 (Sun=1, ... Sat=7) 503 | 504 | dateAsString = dateToday.lsDateFormat("full"); // Sunday, 25 January 2015 505 | 506 | weekOfYear = dateToday.datePart("ww"); // 5 507 | ```` 508 | 509 | As with strings, there are a lot more methods that just those: https://wikidocs.adobe.com/wiki/display/coldfusionen/Using+the+member+functions#Usingthememberfunctions-SupportedDatememberfunctions 510 | 511 | 512 | #### Boolean #### 513 | 514 | Boolean values in CFML are one of the following three variations: 515 | 516 | 1. true / false 517 | 2. "yes" / "no" (note that those are strings, not keywords) 518 | 3. [any non-zero numeric] / 0 519 | 520 | ColdFusion boolean functions return the `"yes"` / `"no"` variation. Lucee, more properly, returns `true` / `false`. One should never compare the actual value of a boolean in a conditional statement, as the conditional will do this automatically. EG: 521 | 522 | ````cfc 523 | // do this 524 | if (isDate(someDateValue)) { 525 | // etc 526 | } 527 | 528 | // do not do this 529 | if (isDate(someDateValue) == true) { 530 | // etc 531 | } 532 | ```` 533 | 534 | This guidance extends to less obvious situations too: 535 | 536 | ````cfc 537 | // do this 538 | if (someArray.len()){ 539 | // etc 540 | } 541 | 542 | // do not do this 543 | if (someArray.len() > 0){ 544 | // etc 545 | } 546 | ```` 547 | 548 | Any non-zero numeric value is *intrinsically* true, so there's no need to check the length of the array is greater than zero. Just that it has length at all is sufficient here. 549 | 550 | 551 | ### Complex types ### 552 | 553 | #### Array #### 554 | 555 | The workhorse complex type in CFML is the array. An array is an ordered collection of elements, where each element is any type of data. Some languages require each array element to be of the same type: not so in CFML. Also the size of the array is dynamic, and the array will grow as needs must. There is no requirement to define the length of the array when declaring it. 556 | 557 | Arrays can be declared using array literal syntax: 558 | 559 | ````cfc 560 | weekDays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]; 561 | ```` 562 | 563 | It's very important to remember that arrays in CFML start from index `1`, not `0`. This is a slightly contentious issue given the continued popularity of `0` as the starting index for array type collections. In my opinion CFML gets it right, and the reasons most other languages - especially newer ones - persist using `0` are both anachronistic and illogical. 564 | 565 | Internally, on both Lucee and ColdFusion arrays are implemented as `java.util.AbstractList`s. 566 | 567 | There is no concept of multi-dimensional arrays in CFML, but one can easily have arrays-of-arrays, which generally amount to the same thing: 568 | 569 | ````cfc 570 | magicSquare = [ 571 | [4,9,2], 572 | [3,5,7], 573 | [8,1,6] 574 | ]; 575 | ```` 576 | 577 | Individual array elements are referenced via array notation: 578 | 579 | ````cfc 580 | firstRow = magicSquare[1]; // [4,9,2] 581 | secondElementOfThirdRow = magicSquare[3][2]; // 1 582 | ```` 583 | 584 | ##### When to use an array ##### 585 | 586 | Arrays are for ordered collections of - somehow - related things, where there are two collective criteria: 587 | * there is a sense of order; a first element comes before a second element, and so on; 588 | * there is no meaningful or usage difference between elements *other than* their sense of ordering. 589 | 590 | For a data structure to be appropriate to be implemented as an array, the order of each element in the collection is significant. IE: one element in the collection could be considered "first", another "last", and there's - for example - as sense that the third element somehow comes after the second element in the sequence. There should be no difference - from the underlying collection's perspective - between the elements of the array beyond that. That sounds a bit vague, and perhaps an example of what should *not* be treated as an array might make it clearer. 591 | 592 | Consider my full name: Donald Adam Cameron. There's a definite sequence there: first name, middle name, last name, so one *might* think: 593 | 594 | ````cfc 595 | fullName = ["Donald", "Adam", "Cameron"]; 596 | ```` 597 | 598 | However a name is not a good example of array data. Whilst the individual names have a sequence, each part of a name does have a slightly different usage, and not all of them have equal "weighting". Especially with me, everyone uses my second name, unless I'm in trouble (with Mum, in which case I have a single name, written in capital letters, followed by an exclamation mark: "DONALD ADAM CAMERON!"), or in official capacities where I'm known as "Donald". *I* use both. And "Cameron" is a family name, and has a different usage to the first two names. Even if you're not a weirdo like me and don't really use (or even *have*) middle names, the same applies: the usage of a person's first name and last names differ. So: not an array. In a decent implementation a name would be represented by a Name object, or a struct: 599 | 600 | ````cfc 601 | fullName = { 602 | firstName = "Donald", 603 | middleName = "Adam", 604 | lastName = "Cameron" 605 | }; 606 | ```` 607 | 608 | This is still a collection, but as each element has different significance, and can be treated accordingly. 609 | 610 | So what *is* an array? 611 | 612 | Sticking with the `name` metaphor, perhaps an array of middle names? My son has two middle names (not my decision, but that's a story for another day): 613 | 614 | ````cfc 615 | fullName = { 616 | firstName = "Zachary", 617 | middleNames = ["Daniel", "Alexander"], 618 | lastName = "Cameron Lynch" 619 | }; 620 | ```` 621 | 622 | Here the middle names are distinct from the first and last names, but the differentiator from one middle name to the next is simply which order they fall in. 623 | 624 | Or perhaps we're developing a localised system wherein the usage order of a name isn't always "given name(s) followed by family names". In some cultures the family name comes first. So our object might become: 625 | 626 | ````cfc 627 | fullName = { 628 | firstName = "Zachary", 629 | middleNames = ["Daniel", "Alexander"], 630 | lastName = "Cameron Lynch", 631 | displayOrder = ["firstName", "middleNames", "lastName"] 632 | }; 633 | ```` 634 | 635 | One cliched metaphor is that of an `Order` (in the commercial sense of that idea): a bill of goods. An Order unto itself would be an object (or struct, but an object would be better), but within that Order there is a collection of order lines: each line item. The line items unto themselves are the exactly the same kind of thing from the perspective of the Order, but they also have a sense of sequence. An `Order` might be like this: 636 | 637 | ````cfc 638 | order = { 639 | number = 3, 640 | recipient = "Adam", 641 | items = [ 642 | {sku=5, count=7, price=11.13}, 643 | {sku=17, count=19, price=23.29} 644 | ] 645 | }; 646 | ```` 647 | 648 | Here the order is a struct, and *each* item is also a struct, but the collection of items is an array. 649 | 650 | As a rule of thumb, if a collection element can be given a meaningful label that distinguishes it from other elements of the collection, then the collection is not an array. 651 | 652 | 653 | ##### Sparse arrays ##### 654 | 655 | Not all array elements need to be populated: 656 | 657 | ````cfc 658 | letters = ["a"]; 659 | letters[3] = "c"; // this also demonstrates assigning a value to an array element 660 | ```` 661 | 662 | This results in an array with an empty element, which could possibly be depicted as: 663 | 664 | ````cfc 665 | letters = ["a", , "c"]; // note, this is not valid CFML syntax: one cannot omit array elements in an array literal 666 | ```` 667 | 668 | 669 | In CFML, the second element here - `letters[2]` - simply isn't defined. EG: 670 | 671 | ````cfc 672 | writeOutput(letters[2]); 673 | ```` 674 | 675 | This will yield a runtime exception: 676 | 677 | > Element at position [2] doesn't exist in array 678 | 679 | One can test for an element's existence: 680 | 681 | ````cfc 682 | if (letters.isDefined(2)){ 683 | writeOutput(letters[2]); 684 | } 685 | ```` 686 | 687 | I would say that if you find yourself creating a "sparse array" (wherein not all elements are defined), then an array might not really be the best choice to store your data. However this situation will legitimately crop up every now and then. But it's worth questioning yourself if you think a sparse array is the correct approach. 688 | 689 | 690 | ##### Common array methods ##### 691 | 692 | ````cfc 693 | letters = ["b"]; 694 | letters.prepend("a"); // ["a", "b"] 695 | letters.append("c"); // ["a", "b", "c"] 696 | 697 | letters.deleteAt(3); // ["a", "b"] 698 | 699 | letters.len(); // 2 700 | 701 | letters = ["a", "b", "c", "d", "e"]; 702 | slice = letters.slice(2,3); // ["b", "c", "d"] 703 | 704 | slice.sort("text","desc"); // ["d", "c", "b"] 705 | 706 | index = slice.find("b"); // 3 707 | ```` 708 | 709 | There are a lot of array methods: these are just a few of the more commonly used ones. See docs: https://wikidocs.adobe.com/wiki/display/coldfusionen/Using+the+member+functions#Usingthememberfunctions-SupportedArraymemberfunctions 710 | 711 | 712 | #### Structs #### 713 | 714 | Structs are another compound type, and indeed I demonstrated one above when discussing names: 715 | 716 | ````cfc 717 | fullName = { 718 | firstName = "Donald", 719 | middleName = "Adam", 720 | lastName = "Cameron" 721 | }; 722 | ```` 723 | 724 | Structs represent collections wherein the elements have a sense of a name (or a label, or a key), and a value. The example above demonstrates the literal notation for defining a struct. 725 | 726 | * The struct is delimited with curly braces (`{` and `}`); 727 | * each element is defined as a key and a value; 728 | * subsequent elements are separated from the former element with a comma. 729 | 730 | There's two significant differences between structs and arrays: 731 | 732 | 1. each element has a name / key / label rather than simply a numeric index; 733 | 2. there is no sense of any ordering between struct elements. 734 | 735 | The first point there is obvious, and doesn't require any further expansion. 736 | 737 | The second point does tend to catch people out: a struct's keys are not ordered. Obviously if one is to iterate over a struct, then each key/value pairing needs to be exposed in turn, and in a given system that ordering might be predictable: it might be the order the pairs were added to the struct, or it might be in alphabetical order by key name. Or some other sequence. The thing is there's no standard, so the ordering of struct keys cannot be relied on. This is at odds with how humans think about such things. In the name example above, "clearly" there's a sense that the `firstName` comes first, the `middleName` comes next, rounded out by the `lastName`. But that's a human conceit, and a struct does not care about that. Whilst it's entirely possible to iterate over a struct, with each step exposing the "next" key/value pair, structs are generally designed to be accessed directly via specific key. I guess this is a difference between structs and arrays. Arrays are *not* generally accessed via specific index; they are treated as an entire collection. Whereas a struct *is* accessed directly, and generally (*generally*) not iterated over. 738 | 739 | 740 | ##### Syntax variations ##### 741 | 742 | There are a few different syntax notation styles for accessing struct elements. I have already mentioned these in the section on variables, so I will just summarise them here. 743 | 744 | 745 | ###### Dot notation ###### 746 | 747 | EG: `struct.key` 748 | 749 | This is the simplest notation, and provided the struct key is known at code-time, and adheres to simple-variable-naming rules, would be the recommended way of accessing a struct's element. It can be used for both setting and getting the key's value: 750 | 751 | ````cfc 752 | fullName = {}; 753 | fullName.firstName = "Peter"; // sets the firstName key to contain "Peter" 754 | writeOutput(fullName.firstName); // outputs the value for the element with key "firstName" 755 | 756 | ```` 757 | 758 | One consideration here is that as CFML is not case-sensitive (`FULLNAME.FIRSTNAME` is the same as `fullname.firstname`, and is the same as `fUlLnAmE.fIrStNaMe`), there is no guarantee as to the casing of the key when it is first declared using this notation. That assignment statement will result in the fullName struct containing a key `FIRSTNAME`, because CFML upper-cases struct key names when dot notation is used. This is because of slightly performance considerations when accessing the keys later. So if key-case is important - for example the struct will be shared with a case-sensitive language like JavaScript - don't use dot notation for *setting* struct keys. 759 | 760 | 761 | ###### Bracket / Associative array notation ###### 762 | 763 | EG: `struct["key"]` 764 | 765 | This is a more flexible notation, and has a couple of benefits over dot notation. 766 | 767 | Firstly, the key name doesn't need to be known at code-time. Because the key name is a string, it can be a runtime value, eg: 768 | 769 | ````cfc 770 | keyName = "firstName"; 771 | writeOutput(fullName[keyName]); // using the above example, this outputs "Peter" 772 | 773 | ```` 774 | 775 | The key can be a variable, an expression which evaluates to a string, or a string-literal. 776 | 777 | Secondly, when using associative array notation, the casing of the key is preserved. This is useful when creating data which will be shared with case-sensitive systems. EG: 778 | 779 | ````cfc 780 | fullName = {}; 781 | fullName.firstName = "Rosie"; 782 | fullName["lastName"] = "Smith"; 783 | 784 | fullNameAsJson = serializeJson(fullName); // {"FIRSTNAME":"Rosie","lastName":"Smith"} 785 | ```` 786 | 787 | 788 | ##### Struct literal declaration considerations ##### 789 | 790 | By default struct-literal notation uses the dot-notation rules for key names. If one wants to preserve case in a key name, or use non-simple key names, simply use strings instead of literals for the key names: 791 | 792 | ````cfc 793 | dynamicKeyName = "thisIsTheKeyName"; 794 | someStruct = { 795 | caseInsensitiveKey = true, // key will be CASEINSENSITIVEKEY 796 | "caseSensitiveKey" = true, // key will remain caseSensitiveKey 797 | "#dynamicKeyName#" = true, // key will be thisIsTheKeyName 798 | "!€##☆" = true // key will be !€#☆ 799 | } 800 | ```` 801 | 802 | 803 | ##### Common struct methods ##### 804 | 805 | ````cfc 806 | person = {firstName="Tina", lastName="Underwood"}; 807 | 808 | keys = person.keyArray(); // ["firstName", "lastName"] 809 | hasMiddleName = person.keyExists("middleName"); // false 810 | numberOfKeys = person.count(); // 2 811 | ```` 812 | 813 | 814 | #### Record sets #### 815 | 816 | A record set or a query is the standard data structure CFML uses to return database query data (hence it being generally - but incorrectly in my opinion - being referred to as a query object). It's also used for some other data-querying operations such as directory listings or LDAP queries and the like. 817 | 818 | A record set is a collection of numerically indexed rows, with each row having a fixed list of string-labelled columns. Similar to a table in the database, or an array of structures (except each structure has the same set of keys). 819 | 820 | An example might be: 821 | 822 | ````cfc 823 | people = queryNew("id,firstName,lastName", "integer,varchar,varchar", [ 824 | [1, "Wendy", "Xiang"], 825 | [2, "Yvette", "Ziegler"], 826 | [3, "Angela", "Brown"], 827 | [4, "Carol", "Davis"] 828 | ]); 829 | ```` 830 | Which is represented as: 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 |
 idfirstNamelastName
11WendyXiang
22YvetteZiegler
33AngelaBrown
44CarolDavis
841 | 842 | 843 | ##### Syntax ##### 844 | 845 | Note that there is no literal syntax for creating a recordset object: one must use the `queryNew()` function. Generally one would not want to create a literal record set anyhow: they are the results of calls to external resources (DB, file system, directories, etc). 846 | 847 | To access an individual element from a recordset, one references via this syntax: 848 | 849 | ````cfc 850 | firstNameOfThirdRecord = people.firstName[3]; // Angela 851 | lastNameOfFourthRecord = people["firstName"][4]; // Davis 852 | ```` 853 | 854 | So this basically follows the same syntax as general variable access: dot-notation for simple or code-time column references; associative array notation for complex or runtime column references. 855 | 856 | individual elements can, likewise, be set using the same syntax: 857 | 858 | ````cfc 859 | people.id[1] = 5; // that row becomes 5, Wendy, Xiang 860 | ```` 861 | 862 | 863 | ##### Common record set functions ##### 864 | 865 | The results of these are fairly self-explanatory 866 | 867 | ````cfc 868 | people.addRow([ 869 | [6, "Frances", "Gosling"], 870 | [7, "Hannah", "Ingalls"] 871 | ]); // adds two new rows to the end of the record set 872 | 873 | people.addColumn( 874 | "middleName", 875 | "varchar", 876 | ["Julia", "Kim", "Lisa", "Melanie", "Nollaig", "Odette"] 877 | ); // adds a new column, specifying the values for each row 878 | 879 | recordForAngela = people.getRow(3); // {id=3, firstName="Angela", middleName="Lisa", lastName="Brown"} 880 | 881 | firstNames = valueList(people.firstName); // Wendy,Yvette,Angela,Carol,Frances,Hannah 882 | ```` 883 | 884 | Notes: 885 | * At time of writing, Lucee has not yet implemented the `getRow()` method. 886 | * Note the non-standard way of referencing a column when using `valueList()`. It's a static value, and must reference the query and the column name, using dot notation. At time of writing, only the headless function has been implemented; there is no OO method for this (on either Lucee or ColdFusion). 887 | 888 | 889 | #### Functions #### 890 | 891 | It's important to be aware that functions are a data types as well. Other data types are more obviously representations of actual *data*, however functions can also be values used in expressions, or the results of expressions, just like any other data type. 892 | 893 | Being able to use functions as/in expressions is one of the more powerful features of CFML. Functions can be declared in one of two ways: via a function statement, or as a literal expression: 894 | 895 | ````cfc 896 | // via fuction statement 897 | function f(x){ 898 | // do something with x 899 | } 900 | ```` 901 | Declaring a function this way creates a named function `f`, and a variable `f` which contains a reference to the function. 902 | 903 | ````cfc 904 | // via function literal 905 | f = function (x){ 906 | // do something with x 907 | } 908 | ```` 909 | 910 | Declaring `f()` this way only creates the reference `f`; it does not create the named function. Accordingly, functions creatred this way are generally known as anonymous functions. 911 | 912 | What's the difference? Well it comes down to when the code is processed. Function statements are processed at compile time, and function literals (or function expressions) are processed at runtime. This has ramifications as to how external variables the function code references are bound. Basically with function created via a statement, external variable references are bound purely by name because that's the best they can do at the time: no actual variable instances exist at compile time after all, and there's no sense of the context the function will be used. With function literals external variables are bound to the actual variable being referenced, and this binding "sticks" for the life of the function. This is known as "closure" - it's said the function "closes over" the variable. 913 | 914 | Note that function literals can be used in any expression where a function is expected, eg: 915 | 916 | ````cfc 917 | newArray = array.map(function(element){ 918 | // do stuff to the element 919 | // return updated element 920 | }); 921 | ```` 922 | 923 | The `map()` method expects a function as its argument, and here we're using a function-literal to define the function as an inline expression in the call to `map()` itself. This makes sense in situations where the function is being used in a one-off sort of way, which is often the case with callbacks like this. 924 | 925 | One could just as easily done this: 926 | 927 | ````cfc 928 | function mapper(element){ 929 | // do stuff to the element 930 | // return updated element 931 | } 932 | 933 | newArray = array.map(mapper); 934 | ```` 935 | 936 | Of course `mapper` might not even be a stand-alone function, it might be a method of a class: 937 | 938 | ````cfc 939 | myObject = new SomeClass(); // SomeClass has a `mapper()` method 940 | 941 | newArray = array.map(myObject.mapper); 942 | ```` 943 | 944 | Functions themselves will be covered in a later chapter. And a discussion on closure and its uses also a chapter of its own. They're both big topics. 945 | 946 | 947 | #### XML #### 948 | 949 | CFML has a specific data type for XML. XML has fallen out of favour a bit in the last couple of years in favour of schemes like JSON, so I'll only touch on XML here. 950 | 951 | ````cfc 952 | x = xmlParse(' 953 | 954 | eee 955 | iii 956 | 957 | '); 958 | fffTagText = x.search("/aaa/fff/text()")[1].xmlValue; // iii 959 | ```` 960 | 961 | Here we take a string and parse it into XML using the function `xmlParse()`, and then we can call the `search()` method on that to perform an XPath search on it. 962 | 963 | Unfortunately CFML's XML support for adding to existing XML objects is a bit clumsy: 964 | 965 | ````cfc 966 | x.aaa[1].XmlChildren.append(x.elemNew("jjj")); 967 | ```` 968 | 969 | This appends an empty `` element after the `` one, within ``, eg: 970 | 971 | ````xml 972 | 973 | eee 974 | iii 975 | 976 | 977 | ```` 978 | 979 | As alluded to in the code here, XML objects can be accessed with a kinda of array notation. Here are some examples: 980 | 981 | ````cfc 982 | x = xmlParse(' 983 | 984 | eee 985 | 986 | hhh 987 | 988 | iii 989 | 990 | '); 991 | x.aaa.bbb; // the first bbb element 992 | x.aaa.bbb[1]; // also the first bbb element 993 | x.aaa.bbb[2]; // the second bbb element 994 | 995 | x.aaa.xmlChildren; // bbb,fff,bbb elements as an array 996 | x.aaa.xmlChildren[1]; // the first bbb element 997 | x.aaa.xmlChildren[2]; // the fff element 998 | 999 | x.aaa.fff.ggg.xmlattributes; // both the hhh and jjj attributes/values as a struct 1000 | x.aaa.fff.ggg.xmlattributes.hhh; // iii 1001 | x.aaa.fff.ggg; // the ggg node 1002 | x.aaa.fff.ggg.xmltext; // lll 1003 | ```` 1004 | 1005 | There's not much more to XML obejct manipulation beyond that. 1006 | 1007 | [cover XSLT briely? Does anyone care these day?] 1008 | 1009 | 1010 | #### Other #### 1011 | 1012 | There are other inbuilt data types which I won't cover here: 1013 | 1014 | * image 1015 | * spreadsheet 1016 | * ftp 1017 | * file 1018 | 1019 | They are either not really core to CFML (image and spreadsheet), or never really *used* as data types (the latter two). There's not really much to say about them that the documentation doesn't cover. 1020 | --------------------------------------------------------------------------------