ColdFusion on Wheels Blog


CFWheels on CF Alive the Sequel

Back in February of 2019 David Belanger and Tom King from the CFWheels core team sat down with Michaela Light on the ColdFusion Alive Podcast to have a chat about CFWheels. A few weeks ago Peter Amiri had a chance to speak to Michaela Light about recent developments in the CFWheels community, how to contribute to the project, and the road map ahead.

Episode 122 of the ColdFusion Alive Podcast

You can view the episode notes on the TeraTech website.


The CFWheels Channel on CFML Slack Has Been Archived

Back in May 2022 we posted a blog article announcing that CFWheels has moved to GitHub Discussions. At the time this effected the retirement of the Google mailing list and redirecting the links on the home page to the new GitHub Discussions site. Now the time has come to also retire the CFWheels channel on the CFML Slack instance.

Feel free to go back and read the original post from May that laid out the reasoning for this move but we thought it was important to reiterate our thought process once again. At the core, the reasons for this move are to move our discussions closer to the code, allowing the poster and respondent to more easily link to specific branches, files, and even lines of code. Issues can be converted to discussions if they warrant further community input or discussions promoted to an issue once an issue or feature has had open consultation and next steps identified. Discussions can be marked as answered and the specific answer identified for future reference. And most importantly, all these discussions, collaborations, and consultations are searchable and discoverable by search engines so the community as a whole reaps the benefits.

We have seen how the move to GitHub discussions by other open source communities has benefited their community and brought their discussions closer to the codebase. We hope this move by us will be similarly fruitful.


CFWheels v2.4.0 Released

This version is the accumulation of bug fixes and minor enhancements over the last quarter. This release welcomes John Bampton and Coleman Sperando, two first time contributors to the project.

Download Zip

If updating from CFWheels 2.3.x:

It should be an easy upgrade, just swap out the wheels folder.

If you installed CFWheels with CommandBox and have a box.sjon file:

Enter install cfwheels in the root of your site to update your wheels folder to the latest.

Changelog

Bug Fixes

  • issue-1091-wheels-paths-in-error-template #1091 – [Adam Chapman]
  • issue-1082-validations should not trim properties #1082 – [Adam Chapman]
  • issue-1088-Adds SQL parsing regex tweak which correctly handles whitespace #1088 – [Adam Chapman, Adam Cameron]

Miscellaneous

  • Adds cfformat ignore marker comments around core “view” cfm files that contain html markup – [Adam Chapman]
  • Adds the ability to scroll large items horizontally in the test runner UI #1130 – [Adam Chapman]
  • Fix cfformat ignore markers #1129 – [Adam Chapman]
  • Enable finder model methods to returnAs “sql”, mainly for debugging #1141 – [Adam Chapman]
  • Show the Test Runner buttons in the CFWheels GUI on the Package List screen allowing the developer to run the entire test suite instead of one package at a time. – [Peter Amiri]
  • The Base Template now contains all necessary placeholders for the CLI to interact with the application and be able to inject code properly. – [Peter Amiri]
  • By default the Core tests will run in the application datasource, but the developer can setup a different database for running the Core tests to ensure there is no side effects from running the tests. If you do end up setting a different database for the coreTestDatasourceName, make sure to reload your application after running the Core tests. – [Peter Amiri]
  • Fix two broken links in README. [#1150] – [John Bampton – * New Contributor *]
  • Fix spelling [#1151][#1158] – [John Bampton – * New Contributor *]
  • Add .env parser to parse .env files and add the properties found in the file to this.env scope. #1157 – [Peter Amiri]
  • Update the local test suite to supported ARM architecture docker images to make the suite compatible with the Apple Silicon Macs. #1143 – [Peter Amiri]

Guides

  • Fix broken links throughout the guides. – [Peter Amiri]
  • Fixed mailto link in CONTRIBUTING.md #1123 – [Coleman Sperando * New Contributor *]
  • Fix test guides examples #1125 [Adam Chapman]
  • Fix typos in the guides #1161 [Adam Chapman]

CFWheels HTMX Plugin published

A few weeks ago I published a Todo app using CFWheels on the backend and HTMX to provide the interactivity on the front end to make the app look and feel like a full blown SPA app. As I was developing that app I ran into a few things that I wish we had to make development with HTMX a little easier. But I’m getting ahead of myself.

What is HTMX

Well, HTMX was released a couple of years ago and in that short time has just about exploded in the django community. So what is HTMX, HTMX tries to answer the following questions:

  • Why should only <a> and <form> be able to make HTTP requests?
  • Why should only click & submit events trigger them?
  • Why should only GET & POST methods be available?
  • Why should you only be able to replace the entire screen?

By removing these arbitrary constraints, htmx completes HTML as a hypertext medium. You may even start wondering why these features weren’t in HTML in the first place. So let’s look at an examples.

  <script src="https://unpkg.com/[email protected]"></script>
  <!-- have a button POST a click via AJAX -->
  <button hx-post="/clicked" hx-swap="outerHTML">
    Click Me
  </button>

This block of code tells the browser:

When a user clicks on this button, issue an AJAX request to /clicked, and replace the entire button with the HTML response.

Let’s look at a typical anchor tag:

<a href="/blog">Blog</a>

This anchor tag tells the browser:

When a user clicks on this link, issue an HTTP GET request to ‘/blog’ and load the response content into the browser window.

You can see how HTMX feels like a familiar extension to HTML. With this in mind lets look at a following block of HTML:

<button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML"
>
    Click Me!
</button>

This tells HTMX:

When a user clicks on this button, issue an HTTP POST request to ‘/clicked’ and use the content from the response to replace the element with the id parent-div in the DOM

So by using hx-get, hx-post, hx-put, hx-patch, or hx-delete we gain access to all the HTTP verbs. Imagine a delete button on a table row that actually issues a HTTP Delete to your backend.

The hx-trigger attribute gives us access to all the page events. HTML elements have sensible defaults, the button tag will get triggered by a click by default and an input tag will get triggered by a change event by default. But there are some special events as well, like the load event that will trigger the action when the page is initially loaded or the revealed event that will trigger the action, when the element scrolls into view. Think of an infinite scroll UX pattern where an element scrolls into view, which triggers a call to the backend to load more data that gets added to the bottom of the page.

The hx-target attribute lets you specify a different tag to target than the element that triggered the event. You have the typical CSS selectors but also some special syntax like closest TR to target the closest table row.

The last attribute shown in the example above is the hx-swap which specifies how to swap the response into the element. By default, the response replaces the innerHTML of the target element but you can just as easily replace the entire target element by using outerHTML. There are a few more designators that allow you to finely control placing the response before or after the target element in its parent element or at the begging of or end of a target’s child elements.

This is just scratching the surface of what HTMX can do but you should be getting the picture. By sprinkling in a handful of HTML attributes into your markup you can gain interactivity that was the domain of full blown JavaScript frontend frameworks in the past.

Why should we care as CFWheels developers

By default HTMX is backend agnostic. It just deals with HTML and doesn’t care what backend technology you use to generate it. This could just as easily be used in a plain vanilla CFML app or your framework of choice, hopefully it would be CFWheels since you are here reading this. Wheels has some built in features that make working with HTMX a breeze. We already have a templating system, we already have a router and controllers to intercept the HTTP request. We have a number of rendering methods that make responding to requests simple.

If the request is a for a full page, use the renderView() method or simply let the controller hand the request off to the view which in turn renders the view page. If the request is for a portion of the final page then use the renderPartial() method and return a snippet of code tucked away in a partial. The same partial could be used by your initial view page, keeping your code DRY. Sometimes, you just want to return a small bit of text or no text at all and it doesn’t make sense to build out a view or partial for every instance of these scenarios, that’s when the renderText() method comes in handy. Imagine a typical index page from a CRUD application that lists a bunch of rows of data and some action buttons on each row. Let’s assume, one of these buttons is a delete button. Look at the following code:

// image this button on a table row
<button hx-delete="/products/15" 
        hx-target="closest hr"
        hx-swap="outerHTML"
        hx-confirm="Are you sure?">
    Delete
</button>

// imagine this code in your action
function delete() {
  aProduct = model("product").findByKey(params.key);
  aProduct.delete();
  renderText("");
}

So what does the above combination do:

When the user clicks on the Delete button, prompt the user to make sure they are sure they wish to delete the record, if the user affirms the request, issue a DELETE request to the server. The server in turn deletes the record and sends back an empty text response to the client. When the response comes back to the frontend, find the closes table row, and remove it from the table.

We just made an Ajax call to the server, removed the record from the database, and correspondingly updated the UI by just removing a single element from the DOM.

What does this plugin do?

By default, HTMX adds some request headers to the call sent to the backend which can be interrogated to see if the request is in fact an HTMX request. If the request is actually an HTMX request, some additional request headers are made available which can add more color to the call being processed. This plugin automatically adds these header elements to the params structure which makes them automatically available to your controller actions. This makes it easier to work with this data and incorporate it into your request processing logic. Take a look at the following example:

function index() {
  if (params.htmx.reqeust) {
    renderPartial(partial="myPartial", layout="false");
  }
}

This code block says:

When a request comes in to the index action of the controller, check to see if this is an HTMX request and if it is, respond with the mypartial partial and don’t wrap it with the layout. Otherwise respond with the index view page of the current controller.

Think of a paginated index page, where the first call to the index action sends the view with the first page of data and a button or element on the page triggers additional calls to the same action but this time only the next page of data is sent to the front end.

Installing the Plugin

To install this plugin, issue the following command from the root of your application in a CommandBox prompt:

install cfwheels-htmx-plugin

Once installed, reload your application and you’re off to the races.


Wheels CLI matures to Version 1.0

It’s hard to believe it took so long to get here but modern CFML development has come a long way thanks to tools like CommandBox and ForgeBox. The Wheels CLI is built as a CommandBox module and wouldn’t have even been possible without the support of the fine folks at Ortus Solutions.

The first commit to the repo for this project was committed back in July of 2016. It’s taken a while, that’s an understatement, to get here but Wheels itself jumped to 2.0, CommandBox matured, and we were able to put the plumbing in place to support the communication between the CLI and the running server. With nearly 300 commits in the repo, 25 commands in the CLI, and over 20 pages of documentation, it’s now time to take the alpha/beta label off send this baby out into the world.

Some of the more notable commands are wheels new to use our wizard to start a brand new project. With this command and the corresponding wheels generate app command, you can start a new Wheels project in a directory, specify the template to use, pick the CF engine to use, configure the datasource, and setup your reload password. In fact there’s a whole host of generate commands for every type of object you may want to create. There are a bunch of dbmigrate commands to interact with database migrations.

To install the CLI issue the following command:

box install cfwheels-cli

Don’t forget to check out the full CLI Commands section in the guides too.


CFWheels Added to the htmx Server-Side Examples Page

Back in March we published an example app where we took the TodoMVC spec and built a reference implementation of the app with CFWheels and htmx. Here is a quick graphic of the UI of the app.

Todo app implemented with CFWheels and htmx

This app has now been added to the list of Server-Side integration examples on the htmx website.

htmx at it’s core is html over the wire. Instead of returning JSON from backend APIs and consuming them with Javascript to build page interactivity, htmx takes a different approach. It expects actual html snippets to be returned that are swapped into the DOM. How it does it, is by extending html with a handful of additional attributes, that probably should have been there in the first place, which enable any HTML element to issue an AJAX call to the backend, specify what triggers the call, and specify how the returned HTML should be added to the DOM.

We’ll be doing more with HTMX in the future so it’s great to get CFWheels on the radars of the htmx project.


Two New Repositories Published

We have published two new repositories named cfwheels/cfwheels-www and cfwheels/cfwheels-api which handle the CFWheels.org landing page site and the api.cfwheels.org API documentation site respectively.

The cfwheels-api repository is a good example of how you can structure your own projects and keep sensitive information and the core framework files out of your project’s source control repository. The project used a .gitignore file that keeps these files out of the repo. It also uses a box.json file to specify some dependencies to pull the required files back in.

Using dependencies in a box.json file is nothing new but what’s interesting is that cfwheels/cfwheels-api has three dependencies defined. One is the core framework itself and pulls in the latest framework folder. The second is the newly published CFWheels DotEnvSettings Plugin which allows keeping sensitive information out of the repository by using a locally defined .env file. The last dependency is probably the most interesting, cause that one pulls in the Semantic Version module from ForgeBox which is a module that wasn’t specifically written for CFWheels but can be pulled in and used none the less. This opens up the potential of using many more modules from ForgeBox instead of being confined to just CFWheels Plugins.


CFWheels DotEnvSettings Plugin published

A new plugin was published to provide support for .env settings files in a production environment. This plugin is based on Eric Peterson’s CommandBox module and allows the use of .env or similarly named files to store your application secrets so they can be kept out of source control.

LICENSE

Apache License, Version 2.0.

SYSTEM REQUIREMENTS

  • Lucee 5+
  • ColdFusion 9+
  • CFWheels 2+

Instructions

Just drop the zip file into your plugins folder and restart your application or use CommandBox cli to install. Simply type the following at the root of your project:

box install cfwheels-dotenvsettings

Usage

Create a .env file in your project root and add to .gitignore or your version control’s equivalent (don’t commit secrets to your repo!) The file can contain JSON or Java properties style key value pairs:

// property style
MY_SECRET_KEY=somevalue
MY_OTHER_SECRET=shh

// json style
{
  "MY_SECRET_KEY"="somevalue"
 ,"MY_OTHER_SECRET"="shh"
};

The default file name supported is .env but you can use any file name you want. You could even have multiple files for various environment.

In your CFWheels app you can read in your secrets by adding readDotEnvSettings() to your application. The most logical point to do this would be in your events\onApplicationStart.cfm file, but you could do it in your environment specific settings.cfm files as well. You can then access the secrets contained in the file processed using get("MY_SECRET_KEY").

If you want to customize the secrets file to use, you can specify the file name by passing in the file name like so readDotEnvSettings(".env-second").


CFWheels has moved to GitHub Discussions

Although Google Groups has served us well over the years, it’s started to lose some essential abilities and it’s looking a little long in the tooth.

We have decided that it would be a benefit to the community to migrate to GitHub Discussions. GitHub Discussions allows us to bring our community closer to where the code lives.

“People around the world are turning to open source—and to GitHub. It is becoming not only the home for developers, but a platform where people in many roles, doing diverse work, come to learn, engage with the community, and contribute to projects that advance the greater good.”

GitHub Octoverse 2020 Report

The benefits of moving to Github Discussions are numerous, but at a glance, we look forward to having better syntax highlighting, marking comments as answers, flagging threads as Answered, easily turning a discussion into an issue, and many more.

The Google Group will remain available, but we have disabled posting to the group, and we’ll treat it as a read-only archive of past discussions.


CFWheels Announces a Bug Bounty

We are happy to launch a new program that we hope will lead to a more stable framework for all of us. Effective immediately we are launching our Bug Bounty program. When we first conceived of the bounty program we were looking at programs from IssueHunt and BountySource and the main goal was to widen the field of contributors to the CFWheels project as well as crush some of the long standing bugs in the framework.

Most of these bugs are edge cases that don’t effect the core functionality of the framework and for the most part users have found work arounds for. In the past it’s been difficult to dedicate our limited resources towards some of these bugs but now that we have a growing list of monthly sponsors we would like to launch out bounty program to compensate contributors who are willing to tackle some of these.

The easiest way to get started participating in this program is to visit the issues list and look for the $50 Bounty tag. Pick an issue that interests you, clone the repo, and start working on your solution. When you think you have a working solution create a PR and submit it for review. Please read the Contributing to CFWheels chapter in the guides or the contributing guidelines in the repository for details on how to contribute to the CFWheels project.

We look forward to see how the community responds to this bounty program. Depending on how things go, we can envision expanding this program to include enhancements as well as sponsorship of individual enhancements.