Parsing CSV files with Grails

One of the arguments that I often make for my use of CFML is how you can do so much with so little code. Seemingly every time I attempt to do something that I haven’t done previously with Grails, I find that argument holds less water than I thought, as I can often do it even easier in Grails.

For a current project, we have an occasionally updated CSV document that contains codes related to the customer’s industry. Given that this file will be changing with additional codes being added, while this app is in early development, we decided that we would just keep it in our application config directory and ensure that any new codes are added during the application bootstrap routine.  Here is what I came up with:

// Insert new codes
def csv = new File("grails-app/conf/code.list.csv")
def code
csv.splitEachLine(',') { row ->
   code = Code.findByLabel(row[1]) ?: new Code(
      code: row[0],
      label: row[1]
   ).save(failOnError: true, flush: true)

Essentially, the above is saying:

  • Read the CSV file
  • Loop each line, where each line is referred to as “row” in the closure.
  • Search in the database for codes with the same label
  • If the code does not yet exist in the system, create a new instance of Code passing in property values from the row in the CSV file.
  • Save the new code to the database.

As you see, I have added line breaks for readability, but I was able to get the result I was looking for in THREE lines of code! I figured I would share in case anyone is looking for a similar solution.

CFML wishlist: All collections should extend Iterator

Have you ever really given a second thought to the fact that in ColdFusion/CFML you have to loop queries, arrays, and structures in completely different ways?   For example, in each of these things, we are essentially doing the same thing:

<!--- looping our query --->
<cfloop query="myQuery">
	<cfset doStuff() />
<!--- looping our array --->
<cfloop array="#myArray#" index="i">
	<cfset doStuff() />
<!--- or --->
<cfloop from="1" to="#ArrayLen(myArray)#" index="i">
	<cfset doStuff() />

<!--- looping our structure --->
<cfloop collection="#myStruct#" item="i">
	<cfset doStuff() />

In each of these cases, we are essentially doing the same thing, that being looping a collection that contains multiple items and acting on each iteration.  I have always liked the fact that the ColdFusion array can be converted to a Java iterator like this:

<cfset iterator = myArray.iterator() />
<cfloop condition="#iterator.hasNext()#">
	<cfset thisIteration = />
	<cfset doStuff() />

However, given the fact that <cfloop array=”#myArray#” index=”i”> is already an abstraction, it doesn’t make sense to use this in most cases.   But wouldn’t it be cool if you could call myQuery.iterator() or myStruct.iterator() and have the same functionality?  Or even better, why not have those collections all extend an iterator class so that it could be simplified even futher with myQuery.hasNext() or myStruct.hasNext().

Keep in mind, this discussion is only coming from the perspective of the programming interface itself, and I am not going to get into the differences behind the scenes of how a query result is actually a set of arrays of columns, or how an array differs from a struct.  My point is simply that if we have these abstractions, it sure would be cool if they were consistent, but we were still able to call specific functions on them like ArrayFind() , StructDelete(), etc.  If we had this ability, our loops in CFSCRIPT would be a lot more consistent as well.

With that said…. I will leave you with my wishful implementation for writing loops in CFML:

<cfloop condition="#myQuery.hasNext()#">
	<cfset thisRow = />
	<cfset doStuff() />

<cfloop condition="#myArray.hasNext()#">
	<cfset thisItem = />
	<cfset doStuff() />

<cfloop condition="#myStruct.hasNext()#">
	<cfset thisItem = />
	<cfset doStuff() />

Piecing together optional view elements at runtime with Mach-II

Often in web development, you run across a case where there is a display that contains optional view elements that are derived at runtime.  Perhaps there is a section of a form that is only available to residents of the US.  Maybe, only users with a certain level of group access have the ability to see a section of a page that can be partially viewed by other user types.  I am sure you can think of numerous cases that you have come across in your own work.   In a current project at our company, one of our developers was tasked with rewriting an old piece of legacy code in which logged in agents can select one or more of multiple reports to display.  The legacy code was a complete nightmare that would probably be worthy of an entire series of what not to do, but that is for another day!  To boil this piece down a bit, essentially the agent has a series of checkboxes of specific reports, and “to” and “from” date inputs to provide a date range.   Depending on what the agent selects, the submission page might show a single report, or a series of several reports in line.

One approach to this would be to have an event defined in which you compile each piece of data into some kind of data collection

if ( [Report1 was selected] )
     get data for Report1
if  ( [Report2 was selected] )
     get data for Report2
(... and so on...)

Then on the view, you could do something like:

if ([we have report data for Report1])
show Report1
if ([we have report data for Report2])
     show Report2
(... and so on ....)

Well, we could but it would be wrong!  Why?  For one thing, we now have conditional logic about each report built into multiple places in our application.  From a complexity and maintenance standpoint, you have just made it, at a minimum, twice as complex as it needs to be.  There is also a strong argument that could be made (and I would make it!) that your view shouldn’t be responsible for determining what it’s supposed to display.  It should simply display!

So what is another approach to this?  How could we employ MVC techniques without the individual components involved becoming intertwined, creating yet another administrative issue.  Here is the solution that I proposed to our developer:
First, let’s start with our form, which is remarkably simple:

<h3>Select the reports you would like to view</h3>
<form name="reportform" action="#buildUrl( "viewreports" )#" method="post">
<input type="checkbox" name="reportList" value="report1" /> Report 1<br />
<input type="checkbox" name="reportList" value="report2" /> Report 2<br />
<input type="checkbox" name="reportList" value="report3" /> Report 3<br />
<input type="checkbox" name="reportList" value="report4" /> Report 4<br />
<input type="checkbox" name="reportList" value="report5" /> Report 5<br />
<input type="submit" value="run reports">

As you can see, we are going to load up an event-arg on the submission named “reportList” that will be a comma separated list of reports that we will be displaying. For instance, if we make the selections you see below, on the viewreports event, event.getArg( “reportList” ) will be: report1,report3,report5

Report Selection form output

I decided that generally I wanted it to behave with a flow like this:

Reports flow diagram

It is a good goal in application development to move as much specific knowledge of the flow of the application outside of any component (view, service, or otherwise) that is not responsible for it to avoid coupling issues.  For instance, our report display page shouldn’t understand flow should it?   (hint: “no“)   Our service layer that is responsible for retrieving data shouldn’t should it? (you guessed it: “no”)

So where does that responsibility lie?  I place it squarely on the front controller framework at hand, namely Mach-II in this case.

If that is the approach we are going to follow, then how can we have freely operating pieces, and create a composite view without the individual pieces having any knowledge of each other, nor any knowledge of their role in the bigger picture?   We achieve this by creating small encapsulated pieces that are individually responsible for their limited role, and count on our framework to do the rest.

If you look at the flow diagram above, you will see that we start with a conditional statement on our submission event: “Has form output been generated?”

In our Mach-II configuration we can achieve this by doing the following:

<event-handler event="viewreports" access="public">
 <event-mapping event="noData" mapping="multi_report1" />
 <filter name="checkForReportData" />
 <view-page name="selectreports" contentArg="form" />
 <view-page name="reports" />

Let’s talk about what those pieces are doing.  First, we are defining an event-mapping “noData“.  What this means is that anywhere further in this event, if someone announces “noData“, the event that we are really going to announce is “multi_report1“.   By doing this, we don’t bury specific knowledge into our component responsible for the announcing, but more on that in a moment.  Next, we are calling a filter named checkForReportData. Filters are Mach-II components that contain a single public method filterEvent() which returns a boolean value telling Mach-II whether or not it should continue further within this event.  In the code above, if the filter returns “false”, the <view-page/> nodes will not be processed.   So let’s take a look at the filterEvent() method.

<cffunction name="filterEvent" access="public" returntype="boolean" output="false" hint="I am invoked by the Mach II framework.">
     <cfargument name="event" type="MachII.framework.Event" required="true" hint="I am the current event object created by the Mach II framework." />
     <cfargument name="eventContext" type="MachII.framework.EventContext" required="true" hint="I am the current event context object created by the Mach II framework." />

     <cfset var result = event.isArgDefined( "reportOutput" ) />

     <cfif NOT result>
          <cfset announceEvent( "noData", event.getArgs() ) />

     <cfreturn result />

Very simply, we are saying: Is there an event-arg named reportOutput defined? If there is, we are returning true, telling the event to continue.  If not we are going to announce an event noData, and returning false.   By announcing a generic event named “noData”, and then defining what “noData” means in the XML config, we have just insulated this filter from change.  For instance, right now the <event-mapping/> says that this means that we should announce “multi_report1“.  If this ever changes to another report, then we only have to change the config.  Additionally, we might be able to repurpose this filter another way in the future and announce a completely different event by using a different event-mapping.

So in our example, we have no reportOutput on our first pass through this method, so we are being rerouted to the event “multi_report1“.  Here is what it looks like:

<event-handler event="multi_report1" access="private">
     <event-arg name="reportName" value="report1" />
     <event-mapping event="nextEvent" mapping="multi_report2" />
     <filter name="checkIncludeReport" />
     <notify listener="ReportListener" method="getData" resultArg="data" />
     <view-page name="reports.report1" contentArg="reportOutput" append="true" />
     <announce event="nextEvent" copyEventArgs="true" />

On the second line, all we are doing is defining an event-arg named “reportName” and assigning a value of “report1“.   We will be using this value in a moment.  Before we get to that, and now that you understand what event-mappings are doing, the third line should be clear.  We are just telling Mach-II “if someone or something announces nextEvent within the context of this event, announce multi_report2 instead“.  Again this allows our components to announce generic events which are explicitly defined in the config.   Next, we are calling a filter to see if report1 has been selected in the form by calling a filter named checkIncludeReport.   If the report was not selected in the form, we will kick out and announce nextEvent aka multi_report2.   However, if the report is included, we will continue down the line calling a method on our listener to retrieve data, and then using that data in a view named “reports.report1“.  We take that generated HTML and append it into an event-arg named “reportOutput“.   If you look at our code above, you will be reminded that this is the argument we were testing for in the checkForReportData filter.   Here is a look at our checkIncludedReport filter which makes the decision to include this report or not.

<cffunction name="filterEvent" access="public" returntype="boolean" output="false" hint="I am invoked by the Mach II framework.">
     <cfargument name="event" type="MachII.framework.Event" required="true" hint="I am the current event object created by the Mach II framework." />
     <cfargument name="eventContext" type="MachII.framework.EventContext" required="true" hint="I am the current event context object created by the Mach II framework." />    

     <cfset var result = ListFindNoCase( event.getArg( "reportList" ), event.getArg( "reportName" ) ) />

     <cfif NOT result>
          <cfset announceEvent( "nextEvent", event.getArgs() ) />

     <cfreturn result />   

All this filter is doing is checking in the event-arg reportList, which is a comma separated list of reports, and seeing if the value of event-arg reportName (which was defined on line 2 above) exists in the list.  Based on our example of selecting reports 1, 3, and 5, the plain English translation of this comparison is:  If the list “report1,report3,report5″ contains “report1″, return true, otherwise announce “nextEvent” and return false. As you surely know by now, in this case “nextEvent” translates to “multi_report2

Essentially we just repeat this exact pattern for the next 4 events, with a minor change in the last event:

<event-handler event="multi_report2" access="private">
    <event-arg name="reportName" value="report2" />
    <event-mapping event="nextEvent" mapping="multi_report3" />
    <filter name="checkIncludeReport" />
    <notify listener="ReportListener" method="getData" resultArg="data" />
    <view-page name="reports.report2" contentArg="reportOutput" append="true" />
    <announce event="nextEvent" copyEventArgs="true" />

<event-handler event="multi_report3" access="private">
     <event-arg name="reportName" value="report3" />
     <event-mapping event="nextEvent" mapping="multi_report4" />
     <filter name="checkIncludeReport" />
     <notify listener="ReportListener" method="getData" resultArg="data" />
     <view-page name="reports.report3" contentArg="reportOutput" append="true" />
     <announce event="nextEvent" copyEventArgs="true" />

<event-handler event="multi_report4" access="private">
     <event-arg name="reportName" value="report4" />
     <event-mapping event="nextEvent" mapping="multi_report5" />
     <filter name="checkIncludeReport" />
     <notify listener="ReportListener" method="getData" resultArg="data" />
     <view-page name="reports.report4" contentArg="reportOutput" append="true" />
     <announce event="nextEvent" copyEventArgs="true" />

<event-handler event="multi_report5" access="private">
     <event-arg name="reportName" value="report5" />
     <event-mapping event="nextEvent" mapping="viewreports" />
     <filter name="checkIncludeReport" />
     <notify listener="ReportListener" method="getData" resultArg="data" />
     <view-page name="reports.report5" contentArg="reportOutput" append="true" />
     <announce event="nextEvent" copyEventArgs="true" />

As I mentioned, there is a slight change in multi_report5 in that nextEvent is defined as “viewreports“.   By doing this, we have then ended our report generation and are redirecting the flow back to the initial event that kicked this process off.  Since we now have reportOutput data, we are directed to the page that ouputs it all.  Quite simply, our big, massive, magnificent multi-form display page looks like this:

these are the reports:

<cfoutput>#event.getArg( "reportOutput" )#</cfoutput>

There is no conditional nonsense, and the view simply outputs all of the generated output that was appended into the event-arg reportOutput.   Additionally, if you reflect on the things we have done, no where are we explicitly saying “if the user selected report1, do something“.  We have left it all fairly generic and hopefully have created some potentially reusable components.    For instance, let’s say that we now have a requirement for an event that only displays report2. No problem!  All we need to do is add an additional event like this:

<event-handler event="report2" access="public">
     <notify listener="ReportListener" method="getData" resultArg="data" />
     <view-page name="reports.report2" />

Easy, huh!

Lastly,  I know that some of the more astute of you may have noticed a fatal flaw in the design above.  What happens when no reports are selected?   In the interest of keeping this example as stripped down as I could, I let that one go, but it is a very simple fix.  What would you do?  Where would you put it?   Feel free to post your fix in the comments, along with any other thoughts you have on this solution.

download fully-functional example files – NOTE: doesn’t include the Mach-II framework

Creating, Modifying, Deleting with Reactor

In my last entry, I gave a showed how easy Reactor makes accessing records in your database.  Even if that was all Reactor did I think it would be a great tool.  However, as one would expect, you can do much more.  In this entry, I will show you how to use Reactor to create new records, modify data in those records.

Let say we are again working with a UserAccount record, like our example from last time.  Our UserAccount object has the following properties: UserId PK, UserName, Password, Email, DateRegistered (and of course our table as these columns as well).

The first thing we need to do is create an instance of the Reactor Factory.  As I mentioned last time, this should probably be done in the application scope or something similar so you don’t have to continually reinitialize it.  Let’s go ahead and put that in our application scope:

// create an instance of Reactor
application.Reactor = CreateObject(“Component”,        “reactor.reactorFactory”).init(expandPath(“/config/reactor.xml”));

Once that is instantiated, anytime we need it we can copy it into the local scope of the template we are working on.   So, let’s create a new UserAccount object in our system and populate it with our new user ‘dshuck’.

// create local instance of the Reactor Factor
Reactor = application.Reactor;

// create a new empty instance of our UserAccount object
UserAccount = Reactor.createRecord(“UserAccount”);

// now populate the UserAccount object properties

So now we have a UserAccount object that whose properties hold the data that we added.  If you wanted to at this point you could output something such as the username by:


If you look in the database at this point you will notice something though.  There is no ‘dshuck’ user in the UserAccount table.   The reason is that the UserAccount is currently held in memory, but we need to actually persist it to the database.  To do so, we call the save() method in the UserAccount object like so:


Now if you look in the table you will see our new record.  Pretty cool!

But wait… there’s more!

Often times when we do some sort of insert action like this, we need to do something with the primary key value that is created on insert.  Typically this is done with some sort of “SELECT max(UserId) …” query, or doing a “SELECT @@identity….” via a stored procedure.  Well as you can probably guess, Reactor makes this easy as well.  How do you do it?

You already did!

Your UserAccount object already holds the new UserId value, so if you wanted to access it, you could call it like this:


By this point you should be able to plainly see the ease and speed at which you can not only read, but create new records as well.

So say we are now on a new page and would like to change ‘dshuck’s password to something tricky like “password”.  First we would need to load the record.  Once it is loaded we would alter just the password property, then save the document.

Here it is in code, and let’s assume the UserId PK value of UserAccount is 3:

// create local instance of the Reactor Factor
Reactor = application.Reactor;

// load the user ‘dshuck’ as an instance of the UserAccount class
UserAccount = Reactor.createRecord(“UserAccount”).load(UserId=3);

// change the password

// persist it to the database;

So there you go.  Altering property state in 4 lines.  Of course, if you are familiar with OOP in general this isn’t exceptionally groundbreaking.  What is groundbreaking however is that still, we have not coded a single CFC.  That is just ridiculous!

Well, the time has come to say goodbye to our friend ‘dshuck’.  Let’s say, we are now on a new page and have been passed ‘dshuck’s UserId.  As I am sure you are guessing by now, we are not in for an enormous workload to make that happen.   So…  here we go:

// create local instance of the Reactor Factor
Reactor = application.Reactor

And with that we have now Created, Modified, and Delete with ease.  Reactor has many more features, including Data Gateways to access multiple records and return query record sets.  I am still just scratching the surface myself, but what I have seen so far has been very impressive.

I am sure this won’t be the last time you hear me talk about it!

Fusion Reactor

We purchased a copy of Fusion Reactor last week and today I had my first chance to use it.   Right off the bat I was extremely impressed.  The GUI is very similar to the ColdFusion Administrator and is very easy to get around.

Within the past couple of weeks our production server started experiencing some strange performance issues.  We have been seeing some frequent occurrences where the request times would just get longer and longer, and eventually stop responding all together.  There are about 6 high volume applications on that server and  try as we might, we were never able to reproduce this in a controlled way and point to any particular point that was breaking.

Within about 5 minutes of installing Fusion Reactor, I was able to pinpoint a specific piece of code that was in desperate need of refactoring.    Until I could get the piece of code in place, I was able to set Crash Protection in Fusion Reactor which would gracefully kill any troublesome request if it took longer than X seconds.  This worked really flawlessly and put the application right back on its feet again when it stumbled into the code.

Fusion Reactor includes many very useful features including:

  • Requests
    • View currently running requests, including the ability to see stack traces and to kill specific threads.
    • View request history
  • Threads view current threads
  • Rich memory charts/graphs
  • Request Metrics reporting
  • Filters – This is a cool feature.  You can have Fusion Reactor constantly look for specific strings and alter them at request time if you wish.  A useful time for this might be a discovery of a typo, or a moved url in a link.
  • Crash Protection – It approaches this from several different angles.  You can set it for specific request issues, server memory levels, etc.
  • There are several more features but those are the ones that stuck in my memory after 20 minutes or so of using it.

Keep in mind the standard version of this is only $99.00 which is very undervalued in my opinion.

Helms and Peters Outloud

Anyone who knows me knows that I have a lot of respect for Hal Helms.  I took a 1 day class by him at CFUnited this year (his Domain Model OO class) and walked away with my head spinning with all the ideas that I had just tapped into.  His class is part code, part programming theory and part Hal’s philosophy.  Since then anyone who has asked me about training has received a reply “If you can find a class by Hal Helms.  You won’t regret it.”

Well, now you can for free!  (sort of..)  OK, not really a class, but using the new podcast audio series Helms and Peters Outloud, you can get a taste of what his class offers.  In fact, the first one that I listened to “Design Patterns” covered some of the subjects we covered in class, and included an excercise (employees… hourly vs. salary) that we did in class.  FYI, our group in class actually got it right    The left side of the room didn’t fair so well. :)

They don’t seem to have a full library listed on their site, but there is an RSS podcast feed that lists several of their sessions.   Considering I have a 1 hour commute to work, I don’t think it will take me long to get through the ones that are there.  I hope more are on the way soon!

Nested grouping with cfouput

It seems that most of the blogs I read are dealing with a lot of the newer advanced features of ColdFusion MX7.  It is easy to forget that there are tons of people that are new to the language that need info on not-so-new features.  Someone on the DFWCFUG list asked the following question this week.

I have a query that is joining several tables.  I cannot get the group
feature to work like I need to in my <cfoutput>.  For example, the query
returns the following results

Tour 1 – Site A – Type A
Tour 1 – Site A – Type B
Tour 1 – Site B – Type A
Tour 1 – Site B – Type B
Tour 1 – Site C – Type A
Tour 1 – Site C – Type B

I would like to group by both site and type?  The group=”site” works but not
group=”type”.  What am I doing wrong?

My answer follows:
Using this hand-built query, I think this does what you are looking for.   I altered the 6th record so that both were under the same type to prove grouping.

qResult = querynew(“tour,site,type”);
queryAddRow(qResult, 6);
querySetCell(qResult, “tour”, “Tour 1″, 1);
querySetCell(qResult, “site”, “Site A”, 1);
querySetCell(qResult, “type”, “Type A”, 1);
querySetCell(qResult, “tour”, “Tour 1″, 2);
querySetCell(qResult, “site”, “Site A”, 2);
querySetCell(qResult, “type”, “Type C”, 2);
querySetCell(qResult, “tour”, “Tour 1″, 3);
querySetCell(qResult, “site”, “Site B”, 3);
querySetCell(qResult, “type”, “Type A”, 3);
querySetCell(qResult, “tour”, “Tour 1″, 4);
querySetCell(qResult, “site”, “Site B”, 4);
querySetCell(qResult, “type”, “Type B”, 4);
querySetCell(qResult, “tour”, “Tour 1″, 5);
querySetCell(qResult, “site”, “Site C”, 5);
querySetCell(qResult, “type”, “Type A”, 5);
querySetCell(qResult, “tour”, “Tour 1″, 6);
querySetCell(qResult, “site”, “Site C”, 6);
querySetCell(qResult, “type”, “Type A”, 6);


<cfoutput query=”qResult” group=”site”>
<cfoutput group=”type”>
<!— list all tours under type —>

Problem: Javascript single quotes from CF variable

On the DFWCFUG list someone asked if anyone had a solution regarding a problem with passing a ColdFusion variable into an agument of a Javascript call when single quotes might exist in the variable, causing the function call to barf.

Someone suggested replacing all single quotes with double quotes.  This however might not be optimal if you are dealing with someone’s name like Conan O”Brian, of if the text was Don”t do this.   I suggested doing the following to avoid this problem.  Assume the following code:


If you wanted to make that same function call safe from single quotes, you could do this:

<cfoutput> onclick=”doIt(‘#replace(ourColdFusionString,”‘”,”‘”,”all”)#’);” </cfoutput>

Of course in most cases, it would make more sense to create a user defined function to manage this so you don’t have to continually type the same replace code. Additionally, it would easily allow you to change the rules for what might be safe for your Javascript function.

using INI files for site settings

Just as most of us do, when creating a site in ColdFusion, I tend to put most of my site-wide variables in the application.cfm. Just the typical stuff like:

// load database variables
// load sitewide settings
request.siteName=”My Cool Site!!!”;

Recently though, I have discovered how easy it is to use INI files to manage site settings. My reason for this is three-fold. First, INI files are widely used in a lot of different types of applications. It doesn’t take a programmer to be able to look in an INI file and make changes. It is just a list of names and values. Once I hand my applications off to a client, I would just really rather them keep their hands out of the CFM files for the most part! Secondly, I have been storing this ini file in a place separate and away from my public web root. My logic behind this is that if someone was somehow able to get to your files through FTP they might end up with code, but they wouldn’t end up with some key settings that would allow them access to stuff like your database, your encryption keys, etc. Yes, of course you could do the same thing by just cfincluding CFM file from the application.cfm, but again, I refer to Joe Client being able to change it simply on his own. Oh yeah, and third… because it is kind of cool. :) So for the settings above, let’s see how we would do that with an INI file. Let’s create an INI in your webroot called siteSettings.ini.


siteName=My Cool Site!!!

As you can see, we have broken the INI file up into sections. This is only for readabilty and ease of maintenance of the file. You can have as few (at least 1) or many sections as makes sense for your application. Now, how do I get those values into my application? This is where ColdFusion makes it easy on you. Let’s recreate the request variables that we did in the first section, but this time let’s load them from our INI file. For our purposes, we will assume the INI file exists in the webroot.

(please excuse the word wrap!)

// load database variables
request.dsn=getProfileString(    expandpath(‘/siteSettings.ini’),’Database’,'dsn’);
request.dbUser=getProfileString(    expandpath(‘/siteSettings.ini’),’Database’,'dbUser’);
request.password=getProfileString(    expandpath(‘/siteSettings.ini’),’Database’,'password’);
// load sitewide settings
request.timeOut=getProfileString(    expandpath(‘/siteSettings.ini’),’SiteSettings’,'timeOut’);
request.siteName=getProfileString(    expandpath(‘/siteSettings.ini’),’SiteSettings’,'dsn’);

If for you want programmatically update any of these values, the solution is equally simple. All we do is change getProfileString to setProfileString, then add a fourth attribute containing the new value. For example:


If anyone has any thoughts/corrections/additions regarding this information, I welcome it as always.