Categories

Meta

Month: November 2015

This example came up while I was trying to refactor a for loop in to a StructEach. I was getting a weird error:

can't cast [orderNumber] string to a number value

All I was getting from the TagContext was a reference to the closure of the StructEach. After trying it a few different ways, I decided to turn to the trusty TryCF.com site to see what the issue was and if it was different across the various engines.

So, here’s our example snippet:

I’ve included a “Dump by passing struct directly:” section to compare the different results to what I would have hoped to see.

Let’s look at the results in Lucee:

Yikes! The entire struct gets passed in as the key and the value is set to the index in the arguments array. You can imagine that errors out pretty quickly as you try to use the key as a string. (I was under the impression that the keyhad to be a string.)

As if this weren’t bad enough, Adobe ColdFusion implements it just a bit differently.

First off, ACF11 doesn’t pass the entire array as the third parameter to the closure. 👎

Next, we see that — when passing the struct through the arguments of another function — instead of passing the filter name (ORDERNUMBER) as the key and the array of order numbers as the value, it passes the position as the key (1) and the struct as the value.

There’s one more example I want to cover. A simple for loop. You might be hoping this one acts better.

You’d be disappointed. 😞

Here’s the code:

Here’s the results from TryCF

For Lucee:

And ACF 11:

Still not what I would hope for, but at least it’s consistent. ¯\_(ツ)_/¯ (In fact, ACF is consistent between the for loop and the structEach loop. Good for them.)

As far as I can tell, this is due to the (strange, to me) handling of the arguments scope as both indexed and named. The easy way to solve this is…don’t pass the arguments scope containing a struct something you need to loop over that struct!

Also, I got to be honest, I’m not even sure where I’d start with reporting bugs on the different platforms. What are bugs and what aren’t? Which platform is doing it right (or at least less wrong)?

So I’m not going to touch that part; I’m going to stick to just making you aware of the pitfalls with using the arguments scope and looping over structs.

We had a fun activity today as a team at O.C. Tanner. Every Friday we try to take an opportunity to train as a team. Sometimes we train individually with screencast sites like Code School. Lately we’ve been taking turns researching a topic and then teaching it to the others (like “An In Depth Intro to Arrays” or “SQL Tips”). Today, we tried a different activity — we did a training on pull requests by each submitting a pull request to cfdocs.org.

cfdocs.org is an open source website dedicated to quick access to cross-platform docs on cfml tags and functions. Highly recommended.

This isn’t a blog post about submitting pull requests (though you can read a great one here or here). This is to promote the idea that you can both (1) train your team and (2) give back to the community at the same time. I have found that my programming skills have grown in leaps and bounds even in the short time since I started really involving myself in the cfml community. Now the same can be said for our team.

The activity was a great success. Seven (7) pull requests total. Try it with your team today.

I was wanting to set up aliases for CommandBox. Here’s a few examples:

Notice Line 2? The problem was that I wanted to use bss for both box server startandbox server stop. “Wouldn’t it be nice,” I wondered, “if CommandBox would just choose the right one?”

Yes. Yes, it would.

I started with a pull request. Brad Wood helped me out with some code review and helped me realize that this was better off as a ForgeBox command.

Brad also talked about how CommandBox commands are moving to modules in the next version.
While I’m super excited for that (Modules are the best!), I wanted this now, so this is a normal CommandBox command.

The code was pretty simple:

The new toggle command is nested in a server folder. CommandBox nests commands in namespaces according to their folder structure. By nesting toggle.cfc in server, I am essentially adding the toggle command to the existing server namespace.

Also note the aliases metadata on the command of toggle. That let’s us just type toggle as opposed to server toggle.

Here’s the box.json:

One thing to note is Line 6. The value in createPackageDirectory overrides the default name for the package. Since my package’s name is server-toggle, CommandBox was installing it to a server-toggle folder. Specifiying server in the createPackageDirectory field ensures this command is under the server namespace.

That’s it! You can check out the code on GitHub or install the package yourself by running install server-toggle in CommandBox.

I was dealing with a strange bug using my QueryToArrayOfStructsConverter in a new project. The code was super simple — just a select * from table as an example for a test I was writing. I expected to get back my array of structs, but I instead got this mysterious error:

Exception mixing in AOP aspect for (TestDAO)

I decided to take the time to dig in to the AOP portion of ColdBox to try to (1) learn about how AOP is implemented in ColdBox and (2) find my problem here.

Side Note:

Whenever I dig in to these packages and frameworks, I’m assuming I’m going to find that I was doing something wrong.
It’s more time consuming than finding an answer in the documentation, but also more enlightening about how the tool I’m using works.

AOP is a fascinating pattern. I was interested to find that ColdBox accomplishes the pattern by writing a dyanmic function to a file and then including that file in the in-memory class using a <cfinclude />. Very smart.

I mention the <cfinclude /> because it was here that I was getting the error. You see, ColdBox is just catching any error and reporting it back as the error message above. By commenting out the try-catch block, we see a different and, in this case, more useful error message:

Looks like Lucee is using find as a function name under the hood. The method I was trying to join to was also called find. Easy enough fix — I renamed the method to findAll.

There’s probably other reserved words, as it were, that you can’t join an aspect to, but I don’t have a list. What I do have now is a good guess at the problem when I see this kind of error message and a better understanding of how ColdBox puts together AOP.

I was excited for the conference, but it exceeded my expectations. Especially when compared with some other conferences I’ve attended recently, this conference has had a huge impact on my skills and career.

I think that’s partially because it reminded me how much I love programming. There are a lot of different kinds of projects back at work, and not all of them programming ones. This conference reminded me that I really like the programming ones.

I think it’s partially because the conference was well organized. The conference area was nice. Wifi worked well. The sessions were very informative. The food was excellent. This was probably my favorite Vegas conference location yet.

But mostly, I think it’s because of the community. I decided that I was going to be very prolific on Twitter. (Really prolific. Over 100 of my 500+ tweets happened in the last two days!) That led me to interact with a lot of smart people. The CFML community is very friendly and helpful. I already knew this from the CFML Slack (which you definitely should be on), but it was nice to see in yet another venue. I was able to meet some people in person that I had only interacted with online. Their interaction helped build my confidence — confidence to write more and even submit a couple proposals to dev.Objective() 2016.

I think a key step in keeping a language and community alive is to participate. Participate on Slack and on Twitter. Help out where you can. Submit bug reports and pull requests. No community will die when we’re participating.

This conference was absolutely a success. I can’t wait to come back next year for CFSummit 2016.

There, I said it. I don’t like to look foolish or stupid or, really, less than perfect.

And it’s held me back. Especially from participating more in conferences.

I’ve wanted to submit a proposal to a conference for a while, but I was waiting around until I had a talk that was (1) completed, (2) awesome, (3) completely unique, and (4) wanted by the community.

But something clicked last night.

This is a proposal.

And being a proposal, I don’t need my talk to be (1) completed. I just need the outline.

The conference organizers will decide if my talk is (3) unique enough and (4) wanted by the community. And if it isn’t, that doesn’t mean the talk is bad, necessarily; just not the right time and place.

Basically, I was taking on myself the judgement that I should leave up to conference organizers.

So I just submitted a proposal to dev.Objective() 2016. I’m excited. I have no idea if it will be accepted, but I’m glad I finally submitting a proposal.

Now, all I need to focus on and all I should focus on is (2) make the talk awesome.

This would be an awesome addition. Arrays of structs have better collection methods with each(), map(), reduce(), etc. I’ve been told arrays of structs are faster. They definitely interact with Javascript better on the frontend.

In the meantime, let’s solve this using Aspect Oriented Programming (AOP).

What is Aspect Oriented Programming (AOP)?

As with most programming topics, there is a bit of jargon that comes with it. Don’t let this jargon scare you away. A list of definitions is available on the WireBox site, but we’re going to try and define it very simply here.

What is an aspect?

One of the dictionary definitions of “aspect” is a “part; feature; phase”. In AOP, an aspect is similar — it is a feature of your application. What makes an aspect different from any other object is that an aspect can spans multiple objects. It is more concerned with its job or feature than what it is. So while your normal beans model real world “things” like BankAccount, User, or even Transaction, an aspect will model a feature or job with names like MethodLogger, DBTransaction, or (in our case) QueryToArrayOfStructsConverter.

When would I use this?

If you find yourself writing the same code or method over and over (especially in different components), an aspect might be the way to go.
One other place an aspect shines is adding a feature to existing code without touching the existing code.

Examples can include:

Logging every method that is called

Wrapping all query methods in a transaction

Implementing a security system around certain handler methods

Transforming a result in some way <– this is our example

Note: Whenever you are injecting code in to your components, you risk running in to tough to debug problems. Use your best judgement.

Our Setup

I’m going to show this using ColdBox and WireBox. (I’m sure you can do the same thing with FW/1 and AOP/1).
You can recreate the same skeleton by running coldbox create app query-converter-aspect in CommandBox.

Next, we’ll hijack the index method of the Main handler.

We’re injecting the DAO directly just for this example. When we hit this route we will dump out the data that we get back from the database.

Here’s a look at the DAO.

Very simple. Just querying a test database I set up for this example. Note that we are returning a query.

That’s all of the setup. Let’s move on to the AOP portion of this example.

Building the Aspect

Aspects need to implement coldbox.system.aop.MethodInterceptor (or wirebox.system.aop.MethodInterceptor for standalone). There is only one required method, invokeMethod.

Gotcha!

When implementing the interface, don’t forget to include all of the method definition, including the output=false metadata.

Here is QueryToArrayOfStructsConverter:

Let’s deconstruct this example:

Line 7: This is our implementation of the required method from coldbox.system.aop.MethodInterceptor.

Line 8: By calling invocation.proceed(), we call the matched method. It will return any value it would normally return. In our case, it should return our query object we defined in TestDAO.cfc (once we match it in our WireBox.cfc later).

Line 9: We transform the query in to an array of structs and return it.

Our aspect is ready to go. Now we need to tell WireBox when to inject it.

Registering the Aspect

Gotcha!

Don’t forget to activate the AOP listener! If you can’t figure out why your aspect isn’t running, even after setting your matcher to .any(), you probably forget to add the coldbox.system.aop.Mixer to your listeners array in WireBox.cfc.

You can register aspects in WireBox.cfc, the same place you register your other bindings. Here are the bindings I added:

Now, this is overkill. I probably should pick just one bindAspect, but I wanted to show options. Let’s go over this in detail:

Line 18: First, we bind our aspect to a named key.

By Filename

Line 22: This binding matches components that end in “DAO” for every method and runs our aspect. This is the binding that will match our original TestDAO.cfc file.

By Method Annotation

Line 29: This binding matches all components for methods that have the annotation convertToArrayOfStructs and runs our aspect.

The annotatedWith will match both inline annotations and doc block annotations. Here are those examples:

By Component Annotation

Line 36: This binding matches all components that have the annotation convertToArrayOfStructs for all methods and runs our aspect.

The annotatedWith will match both inline annotations and doc block annotations. Here are those examples:

Gotcha!

You may think you can bind your aspects on returns('query'), but then you have to return a query. Instead, use a different AOP matcher and don’t type hint the methods you want to transform in this way.

Note: this may be a bug. I haven’t really dived in to this use case yet.

Wrap Up

Running this code before, we saw this query:

Running the code with our aspect in place, we see this array:

Now, thanks to AOP, we have an easy way to bring back our queries as arrays of structs every time.

To learn more you can refer to the WireBox docs here or the AOP/1 docs here.

Bonus! — ColdBox Module

This kind of use case bundles real well into a module.

There’s a little magic in this one to activate the AOP listener if it hasn’t been activated and then add the aspects in automatically. Since it is automatic, I’ve chosen to go with component and method annotation bindings only.

The Problem

Don’t use columnlist if you need to worry about the column order. Use the query’s metadata.

- adam_cameron

I was caught off guard by this statement because I had some tests regarding query column ordering in one of my open projects that were passing. Checking the tests, I was worried because the columns were already in alphabetical order. I changed the order of the columns in the query, but the test still passed.

The Solution

I figured this might be an engine-specific bug. TryCF lets me test this on multiple environments without setting them each up on my system.

Looks like ACF decides that sorting your column names is helpful, while Lucee respects the order you provided. If your code needs to be cross-platform, you can, like Adam Cameron suggested, use the query metadata. Here’s an example:

You could use ArrayMap (or, even better, getMetadata(data).map(...)) if you are on Lucee or ACF 11, but this example was about better cross platform compatibility.

The Wrap Up

Going forward, I look forward to the day when CommandBox has built in servers for ACF. It will make testing cross-platform libraries a lot easier. Until then, a good CI strategy (like cfml-ci) will have to do.