Trying To Get ChatGPT 4 To Solve My Hotwire Form Submission Problem
Ben Nadel tries to get ChatGPT to solve a problem that he is having with Hotwire form submissions....
From: Ben Nadel
Ben Nadel tries to get ChatGPT to solve a problem that he is having with Hotwire form submissions....
From: Ben Nadel
Ben Nadel demonstrates how to disable Hotwire Turbo Drive on all links that point to a given subdirectory within a ColdFusion application....
From: Ben Nadel
Ben Nadel demonstrates how to use Russian Doll content nesting with the CFSaveContent tag to compose data in ColdFusion....
From: Ben Nadel
Ben Nadel and the crew talk about a variety of topics from independent consulting to technical debt to common sense to Bloom filters....
From: Ben Nadel
I'm currently at Adobe Summit and this morning at the keynote we announced the beta of Adobe Firefly. Firefly is a generative AI service, which by itself isn't new, but Firefly has a strong commitment to being a more responsible AI service. You can read more about that and Adobe Sensei here if you would like. As an Adobe employee, I'm obviously biased, but the focus on having a responsible service that respects creators feels like a pretty important differentiator. While there are a lot of good uses for this, I decided to have some fun with it and see how well it would do something business-critical... with cats.For my test, I looked up a list of Dungeons and Dragons classes and found this excellent list here. I then went to Firefly and used prompts like so:dungeons and dragons <class> as a catIn general, this worked well, but sometimes I added a bit to get things a bit closer to what I had expected. The "physical" classes looked pretty similar and I probably could have given a bit more context to help Firefly out, but I still found the results delightful.Here's a Fighter:Here's a Paladin:The Barbarian, which is very similar, and could have been improved if I asked for a common weapon like an axe:Now for something really fun - the Bard. The double-sided lute was crazy and I absolutely loved it:Next up is the Cleric:For Druid, I specifically asked to add a "leafy staff", and the result wasn't what I expected, but I loved it:Here's the Ranger - I added "bow and arrow" and the result was much more stylish than I expected:Next is the Monk:The Rogue ended up being my favorite, it looks like they're carrying the severed head of someone they just assasinated:Finally, while there are multiple magic-using classes in D&D, I went with simple and just used "wizard" as a prompt. The eyes on this one are crazy good:This was fun, and if you want to try it yourself, head over to the site and request Beta access. I'm not sure how long it will take to get in, but if you are interested, sign up as soon as possible.Photo by Amauri Mejía on Unsplash
From: Raymond Camden
Hello friends, tomorrow I'm heading out to Vegas for Adobe Summit, so I expect the posting to be a bit light this week.Automating your Mastodon profile with Pipedream.comHere's a great article that talks about using one of my favorite services, Pipedream to automate the updating of a Mastodon profile. I really like Mastodon and the flexibility of its API is pretty great. I've been focused on writing bots, but I love how Stefan uses it to update his profile instead. Check out his article and see for yourself.WebComponent * 2I use an Evernote note to keep track of the links I want to share, and for some reason, these two links have been in my queue for a few months now. They kept getting pushed down by new awesomeness. Today I look to fix that.First up is Awesome Web Components, a huge list of web component articles hosted as a GitHub repository. Sometime this week I need to find time to contribute a few of my articles to it.Next up is a set of toots tagged, WebComponentsAdvent. I'm a big fan of the "Advent of X" type format as a way of sharing daily tips about a technical topic. (I also like my beer and wine Advent collections too, neither of which we've finished.) When clicking the link, be sure to scroll down to the bottom to read in the right order.And now for something completely unnecessary...I know what you're thinking, "My office is missing something, Raymond, do you have any suggestions?" Why yes, I do. What about some lit-up jellyfish that dance in a tube of water? Just head over to Amazon to pick up the jellyfish lava lamp that's been in my office this past week and I absolutely love it. My wife and I saw it recently in the background of a YouTube video, searched on Amazon, and pick it up literally mid-video. It's cheap, and a lot of these kinda things don't work as well as the product page says, but this one's been a true delight.Here is a picture of it in my office, but honestly it's not a great picture and it looks a heck of a lot cooler in motion:
From: Raymond Camden
Conference season is here again! And I'll be kicking it off by speaking at Adobe ColdFusion Summit East 2023 in Washington DC!
From: South of Shasta: Software Development, Web Design, Training
Last September, I blogged about how I used the Spotify API and Pipedream to discover new music: Discover New Music with the Spotify API and Pipedream. I used a Pipedream workflow to select a random track from Spotify and email me a track every morning. I've still got this process running and I enjoy seeing it every morning. More recently, I noticed a cool bit of album art in my Spotify client and it occurred to me that it would be kind of cool to see more. With that in mind, I present to you my latest Mastodon bot, Random Album Cover. You can see an example toot here: Random Album Cover @randomalbumcover@botsin.space Album: Steven Universe The Movie (Original Soundtrack) (https://open.spotify.com/album/3RDksSu4beLjc45EKe5vYO)Artist(s): Steven UniverseReleased: 2019-09-02.img-3bb9733e11f448523670adcbd1248d3f {aspect-ratio: 640 / 640} 5:16 PM • March 17, 2023 (UTC) I have no idea what you'll see when viewing this post as it will be generated during a build, but I'm looking at a striking album cover from an artist I've never heard of, NLE Choppa. So, how was it built?For the most part, it follows the logic of my previous post, doing the following:Select a random letterRandomly decide to make it the beginning of a search string ("A something") or in the middle ("something A something")Select a random number between 0 and 1000Hit the Spotify API. Their API doesn't have a "real" random search, but we use the random letter and offset to search.Given our set of results, select a random record from that.All of the above hasn't changed from the previous post, except I switched the search from track to album. Next, I download the image to a temporary directory. This is straight from the Pipedream samples:import stream from "stream";import { promisify } from "util";import fs from "fs";import got from "got";export default defineComponent({ async run({ steps, $ }) { const pipeline = promisify(stream.pipeline); return await pipeline( got.stream(steps.select_random_album.$return_value.images[0].url), fs.createWriteStream('/tmp/cover.jpg') ); },})And then I post the toot. This code is pretty short as it makes use of the excellent mastodon-api package. My only real work is crafting the text to go along with the image.import Mastodon from 'mastodon-api'import fs from 'fs'export default defineComponent({ async run({ steps, $ }) { const M = new Mastodon({ access_token: process.env.RANDOMALBUMCOVER_MASTODON, api_url: 'https://botsin.space/api/v1/', }); let artists = steps.select_random_album.$return_value.artists.reduce((cur, art) => { if(cur == '') return art.name; return cur + ', ' + art.name },''); let toot = `Album: ${steps.select_random_album.$return_value.name} (${steps.select_random_album.$return_value.external_urls.spotify})Artist(s): ${artists}Released: ${steps.select_random_album.$return_value.release_date} `.trim() let resp = await M.post('media', { file: fs.createReadStream('/tmp/cover.jpg') }); await M.post('statuses', { status: toot, media_ids: [resp.data.id] }); },})I just want to go on record as saying that this is like the third or fourth time I've used reduce without checking the docs and I'm definitely a JavaScript expert now. Definitely.I'll point out that I spent maybe thirty minutes total on this. The longest wait for was the Mastodon instance to approve my bot (maybe 1.5 hours). I also spent more than a few minutes wondering why my Python code wasn't running in a Node step, so maybe I'm not an expert. Maybe.If you want to check out the complete workflow, you can do so here: https://pipedream.com/new?h=tch_m5ofq7
From: Raymond Camden
If you've not heard, a new update has been released (March 14, 2023) for ColdFusion 2021 and 2018. Despite what you may hear, this is an URGENT (rated "Priority 1" by Adobe) update that everyone should apply ASAP, for reasons I will explain in this post. In fact, Hackernews reported yesterday (Mar 16) that the U.S. Cybersecurity and Infrastructure Security Agency (CISA) had issued an urgent warning about this, giving federal agencies a deadline to apply the update. TLDR; For some folks, the above may be all you need to hear: you may be dropping your coffee and donuts now to get the update applied. Still others will see this "huge post" and think, "crap, I don't have time for this". For you, skip to the bottom and its "concluding key points". You can then decide what you think you do or don't "need to know" and pick and choose from the sections as you like. Finally, for those who prefer because of the importance of all this to be led more carefully through understanding things (in a way that's worked for the many people I have helped so far this week, and is far more than either Adobe or Hackernews has shared), please do read on. [More]
Fortuna MySQL problem The post ProjectFortuna Beta & MySQL – No Go appeared first on ColdFusion.
From: Adobe Coldfusion Blogs
Ben Nadel describes a plan for incrementally applying Hotwire (Turbo, Stimulus) to an existing ColdFusion application....
From: Ben Nadel
Ben Nadel explores a way to package common view-rendering methods in a ColdFusion component....
From: Ben Nadel
Ben Nadel and the crew talk about all the things they should be doing; but, which they can't quite motivate to get done....
From: Ben Nadel
How to use external CSS while generating a .pdf using cfdocument. The post External CSS in cfdocument appeared first on ColdFusion.
From: Adobe Coldfusion Blogs
Back nearly a year ago (holy smokes time goes fast), one of my first articles about web components involved building a component to create a paginated/sorted table: Building Table Sorting and Pagination in a Web Component. In that example, the component looked like so in your HTML:<data-table src="https://www.raymondcamden.com/.netlify/functions/get-cats" cols="name,age"></data-table>I thought this was cool, but one big issue with it is that if JavaScript is disabled, or if something else goes wrong with the code, then absolutely nothing is rendered to the page. This got me thinking - what if I could build a web component that enhanced a regular HTML table? Here's what I came up with.First, I set up a table of simple data:<table> <thead> <tr> <th>Name</th> <th>Breed</th> <th>Gender</th> <th>Age</th> </thead> <tbody> <tr> <td>Luna</td> <td>Domestic Shorthair</td> <td>Female</td> <td>11</td> </tr> <tr> <td>Elise</td> <td>Domestic Longhair</td> <td>Female</td> <td>12</td> </tr> <tr> <td>Pig</td> <td>Domestic Shorthair</td> <td>Female</td> <td>8</td> </tr> <tr> <td>Crackers</td> <td>Maine Coon</td> <td>Male</td> <td>5</td> </tr> <tr> <td>Zuma</td> <td>Ragdoll</td> <td>Male</td> <td>8</td> </tr> <tr> <td>Lord Fluffybottom, the Third</td> <td>Domestic Longhair</td> <td>Male</td> <td>8</td> </tr> <tr> <td>Zelda</td> <td>Domestic Shorthair</td> <td>Female</td> <td>7</td> </tr> <tr> <td>Apollo</td> <td>Persian</td> <td>Male</td> <td>3</td> </tr> </tbody></table>Note that I make use of both thead and tbody. I'm going to require this for my component to work, but outside of that, there's nothing special here, just a vanilla table. Now let's look at my component. First, I'll name it table-sort:class TableSort extends HTMLElement { // stuff here..}if(!customElements.get('table-sort')) customElements.define('table-sort', TableSort);In my constructor, I'm just going to set up a few values. One will hold a copy of the table data, one will remember the last column sorted, and one will be a boolean that indicates if we're sorting ascending or descending:constructor() { super(); this.data = []; this.lastSort = null; this.sortAsc = true;}Alright, now for some real work. In my connectedCallback, I'm going to do a few things. First, I'll do a sanity check for a table, thead and tbody inside myself:connectedCallback() { let table = this.querySelector('table'); // no table? end! if(!table) { console.warn('table-sort: No table found. Exiting.'); return; } // require tbody and thead let tbody = table.querySelector('tbody'); let thead = table.querySelector('thead'); if(!tbody || !thead) { console.warn('table-sort: No tbody or thead found. Exiting.'); return; }Next, I look at the body of the table and get a copy of the data: let rows = tbody.querySelectorAll('tr'); rows.forEach(r => { let datum = []; let row = r.querySelectorAll('td'); row.forEach((r,i) => { datum[i] = r.innerText; }); this.data.push(datum); });For the next portion, I look at the head. For each column, I want to do two things. First, set a CSS style to make it more obvious you can click on the header. Then I add an event handler for sorting: // Get our headers let headers = thead.querySelectorAll('th'); headers.forEach((h,i) => { h.style.cursor = 'pointer'; h.addEventListener('click', e => { this.sortCol(e,i); }); });Finally, I copy over a reference to the body. This will be helpful later when I render the table on sort: // copy body to this scope so we can use it again later this.tbody = tbody;}Alright. At this point, the component is set up. Now let's look at the sorting event handler:sortCol(e,i) { let sortToggle = 1; if(this.lastSort === i) { this.sortAsc = !this.sortAsc; if(!this.sortDir) sortToggle = -1; } this.lastSort = i; this.data.sort((a,b) => { if(a[i] < b[i]) return -1 * sortToggle; if(a[i] > b[i]) return 1 * sortToggle; return 0; }); this.renderTable();}The event is passed a numeric index for a column which makes sorting our data simpler. The only really fancy part here is how I remember what I sorted last time, which lets me reverse the sort if you click two or more times on the same column. If you are noticing a potential issue here, good, you are absolutely right and I'll show the issue in a sec.Alright, the final part of the code is rendering the table:renderTable() { let newHTML = ''; for(let i=0;i<this.data.length;i++) { let row = '<tr>'; for(let c in this.data[i]) { row += `<td>${this.data[i][c]}</td>`; } row += '</tr>'; newHTML += row; } this.tbody.innerHTML = newHTML;}This is pretty boilerplate. It does have one issue - if the original table cells had other stuff, for example, inline styles, or data attributes, then that is lost. I could have made a copy of the DOM node itself and sorted them around, but for this simple component, I thought it was ok.Whew! The final thing to do is to wrap my table:<table-sort><table> <thead> <tr> <th>Name</th> <th>Breed</th> <th>Gender</th> <th>Age</th> </thead> <tbody> <tr> <td>Luna</td> <td>Domestic Shorthair</td> <td>Female</td> <td>11</td> </tr> <!-- more rows ---> </tbody></table></table-sort>Now let's test it out in the CodePen below: See the Pen PE Table for Sorting by Raymond Camden (@cfjedimaster) on CodePen.Hopefully, it worked fine for you. Of course, if it failed for some reason, you still saw a table right? But maybe you tried sorting on age and saw this:Oops. The age column, which is a number, is sorted as a string. So how do we fix that? Remember that my goal was to have you not touch your original table at all. I initially thought I'd maybe have you add a data- attribute to the table, but that didn't feel right. Instead, I came up with another solution - an attribute to the web component:<table-sort numeric="4">In this case, I'm specifying that the fourth column is numeric. Here's how I supported this in code. In connectedCallback, I look for the attribute:let numericColumns = [];if(this.hasAttribute('numeric')) { numericColumns = this.getAttribute('numeric').split(',').map(x => parseInt(x-1,10));}Since the value in the HTML is 1-based, I take your input (which can be comma-delimited), split it, convert each value to a real number and subtract one. The end result with my sample input is an array with one value, 3.The final bit is to check for this when I create a copy of the data:let rows = tbody.querySelectorAll('tr');rows.forEach(r => { let datum = []; let row = r.querySelectorAll('td'); row.forEach((r,i) => { if(numericColumns.indexOf(i) >= 0) datum[i] = parseInt(r.innerText,10); else datum[i] = r.innerText; }); this.data.push(datum);});And that's it. You can test that version below: See the Pen PE Table for Sorting (2) by Raymond Camden (@cfjedimaster) on CodePen.
From: Raymond Camden
Ben Nadel looks at several ways to communicate between nested Stimulus controllers in a Hotwire and ColdFusion application....
From: Ben Nadel
We are pleased to announce that we have released the updates for the following ColdFusion versions: ColdFusion (2021 release) Update 6 ColdFusion (2018 release) Update 16 In this release, we’ve addressed some security vulnerabilities and added the following jvm flags to that effect. -Dcoldfusion.cfclient.enable=true/false -Dcoldfusion.cfclient.allowNonCfc=true/false For more information, see the tech notes below: ColdFusion (2021 release) Update 6 ColdFusion (2018 release) Update 16 These updates fix security vulnerabilities that are mentioned in the security bulletin, APSB23-25. The Docker images will be […] The post RELEASED- ColdFusion 2021 and 2018 March 2023 Security Updates appeared first on ColdFusion.
From: Adobe Coldfusion Blogs
People who've been following me for a while will know that a key feature of my site has always been the dynamically generated CV button on the homepage. It's made it through multiple itterations and I'd like to think I've gotten pretty good at building that sort of thing. Something I've been asked for years however, is if I'd be willing to licence that to others so that they can have a dynamic CV on their own site.
Ben Nadel demonstrates that the CFReturn tag can be used to short-circuit the execution of a CFML template. This works in both Adobe ColdFusion and Lucee CFML....
From: Ben Nadel
Ben Nadel demonstrates how to transclude a form into a fly-out panel using Hotwire and ColdFusion....
From: Ben Nadel
Today I needed to get the time that a file was created from within some CFML code. I had first thought that cfdirectory or directoryList would return this, but it only returns the date the a file was modified, not the date that it was created. My next thought was that getFileInfo must return this, but again it only returns the date that the file was last modified. Even java.io.File only returns the last modified date, not the date the file was created. The Solution The solution is to use Java's NIO (Native IO) file API, and more specifically the java.nio.file.attribute.BasicFileAttributes implementation. Here's a function that will return the date a file was created, modified, and the date the file was last accessed. function getFileAttributes(path) { var filePath = createObject("java", "java.nio.file.Paths").get(arguments.path, []); var basicAttribs = createObject("java", "java.nio.file.attribute.BasicFileAttributes"); var fileAttribs = createObject("java", "java.nio.file.Files").readAttributes(filePath, basicAttribs.getClass(), []); return { "creationTime": fileAttribs.creationTime().toString(), "lastModifiedTime": fileAttribs.lastModifiedTime().toString(), "lastAccessTime": fileAttribs.lastAccessTime().toString() }; } Note that some linux file system implementations don't actually keep track of the last access time (or atime as they call it), so you might get the last modified date there instead.
From: Pete Freitag's Homepage
After my post yesterday about web component lifecycle events, I had an interesting conversation with Thomas Broyer on Mastodon. He brought up an issue with web components that I covered before on this blog, but as it was a very appropriate thing to discuss immediately after yesterday's post, I thought a bit of repetition would be ok. And heck, I'll take any chance to write more web component code as it gives me more practice.So as a reminder, yesterday's post specifically dealt with what code is best used in a web component's constructor versus the connectedCallback event. Specifically, it dealt with the use case of checking attributes and handling web component elements created via JavaScript. To be clear, I don't mean the definition of the web component, but creating an instance of one, like so:let mc = document.createElement('my-component');document.body.appendChild(mc); While I didn't bother setting a title in that example, I could have done so like this:let mc = document.createElement('my-component');mc.setAttribute('title','My title');document.body.appendChild(mc); And it works as expected. But here's an interesting question. What if later on I change the title? Imagine this code:setTimeout(() => { console.log('timer done, lets do this'); mc.setAttribute('title','New title'); console.log(`title for the mc is ${mc.getAttribute('title')}`);}, 3 * 1000);When run, what will it do? Check out the CodePen below to see: See the Pen WC Tests (5) by Raymond Camden (@cfjedimaster) on CodePen.As you can see, it does not work. Remember you can open your browser's console here if you want to see the messages. It will clearly say that the title attribute matches the update, but that's what you'll see reflected in the DOM.The good (?) news is that this is completely expected and easily (for the most part) addressed. When defining a web component, you need to define which attributes you care about it (in terms of them changing) and write code to listen for those changes.The first part is simple:``jsstatic get observedAttributes() { return ['title'] };The next part involves adding an event handler named `attributeChangedCallback`:```jsattributeChangedCallback(name, oldValue, newValue) { console.log(`changing the value of ${name} from ${oldValue} to ${newValue}`);}If you try this, you'll see that it's fired multiple times. I had a "hard-coded" instance of the component in the DOM and it will message that the title is changing from null to the hard-coded value, reflecting the immediate change of the web component being added to the DOM. You will also see this run with the instance of the component created in JavaScript.Now for the fun part. The event handler needs to actually update the display to reflect the new value. In the first iteration of my example component, I skipped the Shadow DOM and just wrote it out directly to the main DOM. Since I now need to (possibly) update the DOM multiple times, I made two more changes. I switched to the Shadow DOM and built a new method, updateDisplay, that handles updating the display. Here's the entire class:class MyComponent extends HTMLElement { constructor() { super(); console.log('constructor called'); const shadow = this.attachShadow({ mode: "open" }); const div = document.createElement('div'); const h2 = document.createElement('h2'); div.appendChild(h2); shadow.appendChild(div); } connectedCallback() { console.log('connected callback called'); if(!this.getAttribute('title')) this.setAttribute('title', 'No title'); this.updateDisplay(); } updateDisplay() { this.shadowRoot.querySelector('h2').innerText = `My Component: ${this.getAttribute('title')}`; } static get observedAttributes() { return ['title'] }; attributeChangedCallback(name, oldValue, newValue) { console.log(`changing the value of ${name} from ${oldValue} to ${newValue}`); this.updateDisplay(); }}Notice that updateDisplay just uses querySelector to find its h2 node and update the text. Now our code that updates the title after a few seconds will work correctly: See the Pen WC Tests (5) by Raymond Camden (@cfjedimaster) on CodePen.If you don't see the switch, just click the "Rerun" button on the bottom right. Anyway, as I said, I've discussed this before, but it definitely tripped me up the first time I ran into it so hopefully this helps others!Photo by Chris Lawton on Unsplash
From: Raymond Camden
The time is here, the time is now! CANCEL ALL YOUR WEEKEND PLANS! We are pleased to announce the opening of the CF Fortuna Open #Beta, now available on the Adobe Prerelease site! Features available in this release include: * GraphQL Client (native GQL support for consuming GraphQL endpoints) * Google Cloud Platform ( FireStore ) * Google Cloud Platform ( GCP Storage ) * Google Cloud Platform ( Pub/Sub ) * Central Configuration Service * HTML to PDF engine […] The post Adobe ColdFusion Fortuna Open Beta appeared first on ColdFusion.
From: Adobe Coldfusion Blogs