Blog

Injecting Functions with WireBox

Brad Wood January 31, 2017

Spread the word

Brad Wood

January 31, 2017

Spread the word


Share your thoughts

This is a guest post by Eric Peterson.

I had a unique use-case the other day, and I wanted to share the solution with you all.

I use a custom Collection component to handle many higher-order functions like map, reduce, filter, etc. Basically, it wraps an array or a query and lets you continue chaining these functions, kind of like wrapping a value with lodash in Javascript.

The challenge comes in creating a Collection. There are a few options. The first one is creating it directly:

function test() {
	var numbers = new modules.cfcollection.models.Collection( [ 1, 2, 3, 4 ] );
}

Pretty verbose. Not very portable. It would break if the module ever changed the location of Collection internally. There are a lot of improvements to be made here.

With modules in ColdBox and WireBox, we can improve this code.

function test() {
	var numbers = wirebox.getInstance(
		target = "Collection@CFCollection",
		initArguments = { collection = [ 1, 2, 3, 4 ] }
	);
}

This code is still pretty verbose, but now it's portable. Plus, WireBox will automatically manage any dependencies inside Collection.

Personally, I still find this code pretty long. Also, it's not very descriptive. There's a method on the Collection class that handles this very use case called collect. It takes a query or an array and returns a new Collection. Using it looks like this:

component {

	property name="Collection" inject="Collection@CFCollection";

	function test() {
		var numbers = Collection.collect( [ 1, 2, 3, 4 ] );
	}

}

That's a nice step up, but there is also a weird thing here. We are injecting the Collection in to our component, which is a transient component, only to call a constructor method off of it. In other languages this might be a static method (which CFCollection has for you Lucee 5 folks, by the way). If we are in a handler, layout, or view in ColdBox, we can use an Application Helper. For our use case, I might add a method to our Application Helper like this:

function collect( collection ) {
	return application.wirebox.getInstance(
		target = "Collection@CFCollection",
		initArguments = { collection = arguments.collection }
	);
}

Now, in any handler, layout, or view we can use this collect function. Our handler now looks like this:

component {

	function test() {
		var numbers = collect( [ 1, 2, 3, 4 ] );
	}

}

That, to me, reads beautifully. It has a couple drawbacks, still. The dependency on collect is implicit. If you don't know how Application Helpers work, then it looks like magic. Also, it only works in handlers, layouts, and views. What if I want to use this in a different component?

With some help on the CFML Slack, I was pointed to a WireBox mapping destination of toFactoryMethod. With this mapping destination, the result of calling a factory function is what ends up being injected. Normally this would be used to wire up a component, like so:

// Color.cfc
component {

	function init( required string color ) {
		variables.color = arguments.color;
		return this;
	}

	function blueFactory() {
		return new Color( "blue" );
	}

}

// config/WireBox.cfc OR ModuleConfig.cfc
binder.map( "BlueColor" ).toFactoryMethod( "Color", "blueFactory" );

(There are other ways to inject that same combination as above. This is just an example.)

So, all I needed was a function that returned the collect function I showed off above. Something like:

component {

	// other methods....

	public Collection function collect( any items = [] ) {
        return new Collection( items );
    }

	public any function getCollectFunction() {
	    return collect;
	}

}

And a mapping to a factory method. Something like:

binder.map( "collect@CFCollection" )
    .toFactoryMethod( "#moduleMapping#.models.Collection", "getCollectFunction" );

And now, with these two pieces in place, we achieve our ultimate goal:

component {

	property name="collect" inject="collect@CFCollection";

	function test() {
		var numbers = collect( [ 1, 2, 3, 4 ] );
	}

}

We have our terse, readable method. We have a clear dependency being injected. And we can use that dependency in any injected component.

So, the next time you wish you could inject a function instead of a component, reach for this WireBox pattern.

Add Your Comment

Recent Entries

CBWIRE 3.0.0 Released

CBWIRE 3.0.0 Released

We are very excited to announce the release of version 3 of CBWIRE, our ColdBox module that makes building modern, reactive apps a breeze. This version brings with it a new component syntax, 19 enhancements and bug fixes, and improved documentation. Our biggest goal with this release was to improve the developer experience and to provide a low barrier to entry to getting started with CBWIRE.

Grant Copley
Grant Copley
May 22, 2023
ColdBox 7.0.0 Released

ColdBox 7.0.0 Released

Introducing ColdBox 7: Revolutionizing Web Development with Cutting-Edge Features and Unparalleled Performance

We are thrilled to announce the highly anticipated release of ColdBox 7, the latest version of the acclaimed web development HMVC framework for ColdFusion (CFML). ColdBox 7 introduces groundbreaking features and advancements, elevating the development experience to new heights and empowering developers to create exceptional web applications and APIs.

Designed to meet the evolving needs of modern web development, ColdBox 7 boasts a range of powerful features that streamline the development process and enhance productivity. With its robust HMVC architecture and developer-friendly tools, ColdBox 7 enables developers to deliver high-performance, scalable, and maintainable web applications and APIs with ease.

Esme Acevedo
Esme Acevedo
May 16, 2023
TestBox v5.0.0 Released!

TestBox v5.0.0 Released!


We are excited to announced the release of Testbox version 5, which brings a host of new features and improvements for developers. TestBox is a powerful and flexible tool that helps developers write comprehensive BDD/TDD tests for their applications, ensuring code quality and reducing the likelihood of bugs and errors. With TestBox v5, developers can take advantage of new features such as batch code coverage testing, improved reporting capabilities, method spies, and better integration with other tools in the Ortus suite.

These new features make TestBox even more versatile and user-friendly, and provide developers with a powerful tool for building high-quality, reliable applications.

 

Luis Majano
Luis Majano
May 11, 2023