Showing posts with label Sean Corfield. Show all posts
Showing posts with label Sean Corfield. Show all posts

Monday 12 July 2021

Factories, static methods, Law of Demeter and never letting Sean see my code

G'day:

I'm absolutely kidding about one of those things, btw.

Right so yesterday I wrote a coupla articles about what code should and should not go into a controller:

Within that first one, I provided some sample code to demonstrate my point. Part of it was the usage of a factory class to provide some necessary wiring for my model class, which I then had as a dependency of my controller:

// UserContentFactory.cfc
component {

    function init(
        ValidationService validationService,
        UserFactory userFactory,
        UserContentService contentService,
        TwitterService twitterService,
        FacebookService facebookService
    ) {
        variables.validationService = arguments.validationService
        variables.userFactory = arguments.userFactory
        variables.contentService = arguments.contentService
        variables.twitterService = arguments.twitterService
        variables.facebookService = arguments.facebookService
    }

    function getUserContent() {
        userContent = new UserContent()
        userContent.setValidationService(validationService)
        userContent.setUserFactory(userFactory)
        userContent.setContentService(contentService)
        userContent.setTwitterService(twitterService)
        userContent.setFacebookService(facebookService)
        
        return userContent
    }
}
// UserContent.cfc
component accessors=true invokeImplicitAccessor=true {

    property publishedContent;
    property twitterContent;
    property facebookContent;

    function init(publishedContent, twitterContent, facebookContent) {
        variables.publishedContent = arguments.publishedContent
        variables.twitterContent = arguments.twitterContent
        variables.facebookContent = arguments.facebookContent
    }
    
    function setValidationService(ValidationService validationService) {
        variables.validationService = arguments.validationService
    }
    
    function setUserFactory(UserFactory userFactory) {
        variables.userFactory = arguments.userFactory
    }
    
    function setContentService(UserContentService contentService) {
        variables.contentService = arguments.contentService
    }
    
    function setTwitterService(TwitterService twitterService) {
        variables.twitterService = arguments.twitterService
    }
    
    function setFacebookService(FacebookService facebookService) {
        variables.facebookService = arguments.facebookService
    }
    
    function loadContentByFilters(required struct filters) {
        validFilters = validationService.validate(filters, getValidationRules()) // @throws ValidationException
        
        user = userFactory.getById(validFilters.id) // @throws UserNotFoundException
        
        publishedContent = contentService.getUserContent(validFilters)
        twitterContent = twitterService.getUserContent(validFilters)
        facebookContent = facebookService.getUserContent(validFilters)
    }
    
    private function getValidationRules() { 
    	// ... elided to save space
    }
}
// UserContentController.cfc
component {

    function init(ViewService viewService, UserContentFactory userContentFactory) {
        variables.viewService = arguments.viewService
        variables.userContentFactory = arguments.userContentFactory
    }

    function getContent(rawArgs) {
        try {
            userContent = userContentFactory.getUserContent().loadContentByFilters(rawArgs)
            
            renderedResponse = viewService.renderView("userContentView", userContent)
            
            return new HtmlResponse(renderedResponse)
            
        } catch (ValidationException, e) {
            return new ClientErrorResponse(400, e)
        } catch (UserNotFoundException e) {
            return new ClientErrorResponse(404, e)
        }
    }
}

(Note that all of this is untested basically "pseudo"-code. I've never run it, it's just a theoretic approach to the issue. By all means let me know if there's anything syntactically or logically wrong with it, but it's not code intended to be run)

loadContentByFilters relied on five external services to do its stuff, and I didn't want to pass those services to UserContent as constructor arguments because then they'd get mungled up with the data values I wanted the UserContent model's constructor to focus on, so I was using setter injection to configure UserContent with those, and have that factory class and method to deal with all that in the background (via whatever dependency injection / IOC container framework I would be using). By the time the controller received the factory it'd been configured, and it had been able to configure a UserContent object, so all the controller needed to do was to call it.

All simple enough.

Sean popped a comment against the article, I responded, and he came back to me again. At which point I decided I'd write this article instead of replying again:

Sean:

Since you're being so hardcore about drawing lines between the MVC parts of your code, I'm going to call this into question:

userContentFactory.getUserContent().loadContentByFilters(rawArgs)

There's an argument often made that you shouldn't have method chaining like this since it couples the caller to the structure of the chained method calls on another object. The argument goes that you should instead have something like:

userContentFactory.getUserContentByFilters(rawArgs)

Thoughts?


Adam:

Yeah. I actually started with that! But then I didn't think it was the business of the factory to go as far as the actual data loading. But… I was not entirely convinced either way, to be completely honest.

I'm also completely down with the Law of Demeter when it comes to chaining methods like that. I did think about that at the time too.

The conclusion I drew was that the factory was just a "convenience extraction", or a utility or something that is completely "in the service" of UserContent. It serves absolutely no purpose other than a mechanism (I misspelled that as "MEHchanism" at first, perhaps fittingly) to wire-together the dependencies that loadContentByFilters needs to do its job. I did not want the controller to have to know how to do that.

The second draft of the actual code had two separate statements:

userContent = userContentFactory.getUserContent()
userContent.loadContentByFilters(rawArgs)

Which is functionally the same, and has the same Law of Demeter considerations you raised, but maybe the thinking is clearer: useContent = userContentFactory.getUserContent() is just an analogy of useContent = new UserContent(). I just slapped it into one statement because that was more convenient (ahem) for the point I was trying to make.

A simpler scenario would be if I wanted to be able to create a UserContent object via a struct that contained name/value pairs matching the constructor params it needs, I'd simply have a (probably static) method on the UserContent class which was createFromStruct which would internally grab the bits and pieces out of the struct and return a new UserContent object, passing those values to its constructor. No need for any separate factory cos there's no external dependencies to have to deal with somehow.

I knew that bit of the code would cause consternation from some quarters... and was hoping one of the quarters would be your own ;-).


Sean:

I find it interesting that you might subsume a factory for createFromStruct() as a static method but not for loadByFilters()…?

Now that is a very good point.

Truth be told, I simply didn't think of it at the time I was writing the blog article, but I did recall having done this in the past by the time I was writing the reply to Sean's question.

But let me back up a bit. Law of Demeter. In summary it means it's OK to call methods on a dependency, but it gets decreasingly OK to call method on objects that the methods of the dependency return, and so on. This is poor form:

function myMethod() {
    return someDependency.someMethod().someMethodOfAnotherObject().wowWeAreNowALongWayAwayFromTheDependency()
}

This is because not only is your implementation tightly coupled to its dependency - but we kinda need to live with that - it's also tightly coupled to the objects that implement someMethodOfAnotherObject and wowWeAreNowALongWayAwayFromTheDependency. You should not need to do all that. SomeDependency should take care of it for you. It should not be exposing its inner workings like that.

BTW, this does not apply to objects that implement a fluent interface. This is fine:

function myMethod() {
    return obj.someMethod().someOtherMethodOfObj().andAnotherMethodOfObj()
}

All those methods are part of the API exposed by obj.

So that's what Sean pulled me up on:

userContent = userContentFactory.getUserContent().loadContentByFilters(rawArgs)

That's a bit of a LoD violation, and worth pointing out. I did reason through this (before Sean raised it, as I said), and I'm fine with it.

But Sean's nudging of me back towards static methods solves this.

We could implement UserContent like this:

// UserContent.cfc
component accessors=true invokeImplicitAccessor=true {

    property publishedContent;
    property twitterContent;
    property facebookContent;

    function init(publishedContent, twitterContent, facebookContent) {
        variables.publishedContent = arguments.publishedContent
        variables.twitterContent = arguments.twitterContent
        variables.facebookContent = arguments.facebookContent
    }
    
    static function setValidationService(ValidationService validationService) {
         static.validationService = arguments.validationService
     }
     // etc, all the methods to set the services are static methods, setting a static property
    
    static function loadContentByFilters(required struct filters) {
        validFilters = validationService.validate(filters, getValidationRules()) // @throws ValidationException
        
        user = userFactory.getById(validFilters.id) // @throws UserNotFoundException
        
        return new UserContent(
            contentService.getUserContent(validFilters)
            twitterService.getUserContent(validFilters)
            facebookService.getUserContent(validFilters)
        )
    }
    
    // irrelevant stuff snipped
}
  • One sets the dependency services via static methods;
  • loadContentByFilters is also a static method which does its stuff, and returns a new instance of itself with the data values set.
  • Not shown here is that when the IoC framework creates the dependency container, it runs the factory method then, to set the services into the UserContent class (NB: "class", not "object") once.

Now in the controller we have a coupla options:

userContent = UserContent::loadContentByFilters(rawArgs)

No need to pass the class in as a constructor argument or anything (just as well: one cannot pass classes around as arguments), on just calls the method on the class.

Inlining static method calls like this is not so good for testing, that said, as it kinda bypasses dependency injection just like having new UserContent() in there, so this is less than ideal.

However there's nothing to stop us passing in an uninintialised instance of UserContent into the controller as a dependency. The IoC handling of this would be along the lines of:

container.set("UserContent", (container) => createObject("UserContent")) //NB: *not* using `new`, cos that calls the constructor
container.set("UserContentController", (container) => new UserContentController(container.get("ViewService"), container.get("UserContent")))

Then we use the dot operator instead of the :: operator to call the static loadContentByFilters via the object reference to the class: userContent

userContent = userContent.loadContentByFilters(rawArgs)

Better yet, perhaps, would be to just make the constructor arguments optional, and just use new UserContent() ;-)

Note: only ColdFusion 2021 has static support, and only Lucee 5.3.8 and above allows using the dot operator to call a static method on an object.


No doubt Sean will find other points of order from all this, but that's great cos it gives me (/us) an opportunity to think about stuff some more.

Right now I'm thinking about another beer, so I'm gonna do that.

Tomorrow I need to have a prod of CFML's handling of static stuff. I've already spotted something that looks like a bug in ColdFusion (quelle frickin surprise), but I need to check some stuff first, and I CBA doing it now. Because beer.

Righto.

--
Adam

Sunday 4 April 2021

TDD & professionalism: a brief follow-up to Thoughts on Working Code podcast's Testing episode

G'day:

Yer gonna need to go back and read the comments on Thoughts on Working Code podcast's Testing episode for context here. Especially as I quote a couple of them. I kinda left the comments run themselves there a bit and didn't reply to everyone as I didn't want to dominate the conversation. But one earlier comment that made me itchy, and now one comment that came in in the last week or so, have made me decide to - briefly - follow-up one point that I think warrants drawing attention to and building on.

Briefly, Working Code Pod did an episode on testing, and I got all surly about some of the things that were said, and wrote them up in the article I link to above. BTW Ben's reaction to my feedback in their follow-up episode ("Listener Questions #1) was the source of my current strapline quote: "the tone... it sounds very heated and abrasive". That should frame things nicely.

Right so in the comments to that previous article, we have these discussion fragments:

  • Sean Corfield - Heck, there's still a sizable portion that still doesn't use version control or has some whacked-out manual approach to "source code control".
  • Ben replied to that with - Yikes, I have trouble believing that there are developers _anywhere_ that don't use source-control in this day-and-age. That seems like "table stakes" for development. Period.
  • [off-screen, Adam bites his tongue]
  • Then Sean Hogge replied to the article rather than that particular comment thread. I'm gonna include a chunk of what he said here:

    18 months ago, I was 100% Ben-shaped. Testing simply held little ROI. I have a dev server that's a perfect replica of production, with SSL and everything. I can just log in, open the dashboard, delete the cache and check things with a few clicks.

    But as I started developing features that interact with other features, or that use the same API call in different ways, or present the same data with a partial or module with options, I started seeing an increase in production errors. I could feel myself scrambling more and more. When I stepped back and assessed objectively, tests were the only efficient answer.

    After about 3 weeks of annoying, frustrating, angry work learning to write tests, every word of Adam C's blog post resonates with me. I am not good at it (yet), I am not fast at it (yet), but it is paying off exactly as he and those he references promised it would.

    I recommend reading his entire comment, because it's bloody good.

  • Finally last week I listened to a YouTube video "Jim Coplien and Bob Martin Debate TDD", from which I extracted these two quotes from Martin that drew me back to this discussion:
    • (@ 43sec) My thesis is that it has become infeasible […] for a software developer to consider himself professional if [(s)he] does not practice test-driven development.
    • (@ 14min 42sec) Nowadays it is […] irresponsible for a developer to ship a line of code that [(s)he] has not executed any unit test [upon].. It's important to note that "nowadays" being 2012 in this case: that's when the video was from.
    And, yes, OK the two quotes say much the same thing. I just wanted to emphasise the words "professional" and "irresponsible".

This I think is what Ben is missing. He shows incredulity that someone in 2021 would not use source control. People's reaction is going to be the same to his suggestion he doesn't put much focus on test-automatic, or practise TDD as a matter of course when he's designing his code. And Sean (Hogge) nails it for me.

(And not just Ben. I'm not ragging on him here, he's just the one providing the quote for me to start from).

TDD is not something to be framed in a context alongside other peripheral things one might pick up like this week's kewl JS framework, or Docker or some other piece of utility one might optionally use when solving a client's problem. It's lower level than that, so it's false equivalence to bracket it like that conceptually. Especially as a rationalisation for not addressing your shortcomings in this area.

Testing yer code and using TDD is as fundamental to your professional responsibilities as using source control. That's how one ought to contextualise this. Now I know plenty of people who I'd consider professional and strong pillars of my dev community who aren't as good as they could be with testing/TDD. So I think Martin's first quote is a bit strong. However I think his second quote nails it. If you're not doing TDD you are eroding your professionalism, and you are being professionalbly irresponsible by not addressing this.

In closing: thanks to everyone for putting the effort you did into commenting on that previous article. I really appreciate the conversation even if I didn't say thanks etc to everyone participating.

Righto.

--
Adam

Wednesday 22 March 2017

Code puzzle: find any match from an array of regex patterns

G'day:
I'm being slightly cheeky as I'm hijacking someone else's question / puzzle from the CFML Slack channel that we were discussing last night. Don't worry, that'll be the last mention of CFML in this article, and indeed the question itself was asking for a JS solution. But it's a generic code puzzle.

Let's say one has a string, and an array of regex patterns one wants to check for in said string. A number of solutions were offered, using a number of techniques. And it even branched away from JS into Clojure (thanks Sean).

How would you go about doing it?

The input data is:

patterns = ["Savvy", "Smart", "Sweet"]
testForPositive = "Super Savvy"
testForNegative = "Super Svelt"

And in these cases we want true and false to be returned, respectively.

Note that the patterns there need to be considered regular expressions, not simple strings. The example data is just using simple strings for the sake of clarity.

Initially I was gonna do something like this:

function matchesOne(str, patterns) {
    var onePattern = new RegExp(patterns.join("|"));
    return str.search(onePattern) > -1;
}

Here I'm joining all the patterns into one big pattern, and searching for that. I was slightly hesitant about this as it's loading the complexity into the regex operation, which I felt perhaps wasn't ideal. Also it's not very clever: if any of the individual patterns had a | in them: this'd break. Scratch that.

Ryan Guill seeded the idea of approaching it from the other direction. Using the array as an array, and using one of JS's array higher-order functions to do the trick. We saw examples using find and filter, but I reckoned some was the best option here:

function matchesOne(str, patterns) {
    return patterns.some(pattern => str.search(pattern) > -1);
}


I'm happy with that.

The ante was raised next: write a function which returned the position of the first match (not just a boolean as to whether there was a match or not).

To me this was a reduction operation: take an array and - given some algorithm applied to each element - return a final single value.

I tried, but I could not make it very clean. This is what I got:

function firstMatch(str, patterns) {
    return patterns.reduce(function(first, pattern){
        var thisMatch = str.search(pattern);
        return first == -1 ? thisMatch : thisMatch == -1 ? first : Math.min(first, thisMatch);
    }, -1);
}


I'm happy with the reduce approach, but I'm really not happy with the expression to identify the lowest match (or continue to return -1 if no match is found):

first == -1 ? thisMatch : thisMatch == -1 ? first : Math.min(first, thisMatch)


To be clear, if we expanded that out, the logic is:

if (first == -1) {
    return thisMatch;
}
if (thisMatch == -1) {
    return first;
}
return Math.min(first, thisMatch);

That's actually "cleaner", but I still think I'm missing a trick.

So the puzzle to solve here is: what's a clever (but also Clean - in the RC Martin sense) way returning the lowest non -1 value of first and thisMatch, or fall back to -1 if all else fails.

Using this code (or some approximation thereof):

function firstMatch(str, patterns) {
    return patterns.reduce(function(first, pattern){
        var thisMatch = str.search(pattern);
        // return result of your code here
    }, -1);
}

console.log(firstMatch("Super Savvy", ["Savvy", "Smart", "Sweet"]));
console.log(firstMatch("Super Svelt", ["Savvy", "Smart", "Sweet"]));


One should get 6 and -1 for the respective "tests".

Use any language you like.

Oh and hey... if you have any interesting solutions for the two functions themselves, feel free to post 'em too.


Righto.

--
Adam

Thursday 18 August 2016

Breaking: Groovy and Clojure answers for that array-look-up code puzzle

G'day:
Well it's not really that breaking really... what I mean is a coupla other people posted some answers to last week's code puzzle after I wrote up the results ("Looking at the code from that code puzzle last week"). I was gonna just append them to the bottom of that earlier article, but hen no-one would see them, and that seemed a bit disrespectful. Also for this blog which is still mostly read by CFML devs, it's some exposure to other languages you might want to take the time to look at, and these are good examples why.

Sean

I was wondering what happened to Sean's Clojure example, but he was off leading his life instead of clearing away the cobwebs from my blog, so didn't notice the code puzzle initially. But here's his Clojure code (fully annotated for us non-Clojurians):

;; simplest solution to find first match -- note that `filter` returns
;; a lazy chunked sequence so it will not search the entire vector
;; however, the chunks are 32 elements in size so it will search up
;; to 31 elements beyond the first match
(first (filter #(re-find #".+at" %) ["at" "cat" "scat" "scratch"]))

;; Clojure has `some` but it returns the result of applying the predicate
;; not the original element so we need to write a "smarter" predicate:

;; will not work in all cases:
(some #(re-find #".+at" %) ["at" "cat" "scat" "scratch"])
;; this: (some #(re-find #".+at" %) ["scratch" "at" "cat" "scat"])
;; produces this: "scrat" -- oops!

;; will work with extended predicate:
(some #(when (re-find #".+at" %) %) ["at" "cat" "scat" "scratch"])

;; or we can use reduce with an early return -- the `reduced` value:
(reduce (fn [_ s] (when (re-find #".+at" s) (reduced s))) nil ["at" "cat" "scat" "scratch"])

;; a note about notation: #(.. % ..) is shorthand for (fn [x] (.. x ..))
;; i.e., an anonymous function with one argument

That looks like a bunch of code, but it's also four examples:

(first (filter #(re-find #".+at" %) ["at" "cat" "scat" "scratch"]))

(some #(re-find #".+at" %) ["at" "cat" "scat" "scratch"])

(some #(when (re-find #".+at" %) %) ["at" "cat" "scat" "scratch"])

(reduce (fn [_ s] (when (re-find #".+at" s) (reduced s))) nil ["at" "cat" "scat" "scratch"])


Now I've only had the most superficial look at Clojure, but even I can just read what's going on in that code. So that's cool. I've been off my game recently with my out-of-hours tech stuff - in case you hadn't noticed - and I really want to finish finding my motivation to get back to it, and look at more Clojure. I think it's a good thing to look at for a perennial CFMLer or PHPer as its quite the paradigm shift, but still seems pretty easy to get at least a superficial handle on, and then work from there.

Tony

Tony's done a Groovy example. Every time I see Groovy, it just seems cool. Check this out:

print( ['a', 'at', 'cat', 'scat', 'catch'].find { it ==~ '.+at' } )

That's it. Done. 67 characters, most of it data. 25 characters of actually "doing stuff", including more whitespace than I'd usually use for this sort of thing. Doesn't that make you want to use Groovy?

Anyway, that's that. I just wanted to share that code with y'all.

Righto.

--
Adam

Sunday 24 January 2016

Lucee 5: Platform modularity in CFML

G'day:
I had a long (and often protracted) conversation with Denny - who seems to do some work on the Lucee project, but I really don't quite get where he fits into things - and later in the piece Geoff, who we all know as Daemon's representative on the LAS "board", currently holding the role of President. The topic was "WTF is OSGi, what does it mean to Lucee, why the hell is causing such delays in Lucee 5's release and why do I - as a CFML developer - care?". It wasn't a formal topic, but it just happened to be what we were yapping about.

It was a very frustrating conversation at times because Denny - nice bloke that he is - doesn't seem to be able to empathise with other people: everything he says is always positioned from his own perspective, and he struggles to "get" that other people have different perspectives which are not the same as his. So the conversation always crept back to how it would benefit him, or someone identical to him. Irrelevant.

Geoff came along later in the piece and made a lot more sense.

Sean came in at the very end and had his own commentary to make, most of which was over night for my perspective so I've just caught up with it now.

The challenge here is it's easy to "get" what OSGi is all about... and as one researches I think even more questions about it - especially in the context of Lucee - arise, but... yeah... bottom line it compartmentalises packages so they can have different dependencies, which has been a bit of a blight in the Java world in the past. OSGi is one of the ways to work around that. I am taking a very high level position on this cos I ain't a Java dev, don't want to be one, and dependency management is all a bit yawn-inducing. It's an important consideration, sure, but not terribly interesting. But we all get it, yeah? Say Lucee's using one module that requires SomeDependency v1.0.0; for my code, I need to use SomeDependency v1.2.0 because it introduces a new feature I want to leverage. This is currently a bit of a challenge given the way the JVM loads its libraries: one can have version v1.0.0 of SomeDependency, or v1.2.0, but not both. And what if something in 1.2.0 happens to break something Lucee needs? Splat. There are ways around all this, but they're not streamlined. Oh... and the way OSGi manages modules means they can be (un)loaded on the fly. Yep... lovely... impacts almost no-one in the course of their day-to-day work.

Lucee 5 is being rewritten to follow / implement the OSGi specification, which will solve all this. Lovely. It's not - on the face of it - hugely exciting for most CFML developers. Most CFML developers don't use Java modules, and even ones like me who do occasionally... this version clash situation has simply never been much of a problem. Obviously it'll be more of a problem for people who do this stuff more often, but that is not most Lucee users. So I've always been asking "why do I care about this in the context of Lucee? Who's the target audience? How big is the target audience? Is it really worth all these bloody delays with Lucee 5 to accommodate a very niche audience?" Up until last night I never got a satisfactory explanation, and it really just seemed like something Micha decided to do because Micha felt like doing it. Or maybe an self-indulgence so that it can be claimed Lucee is OSGi compliant, even if it's mostly an unhelpful exercise to almost all users of Lucee. None of the messaging from LAS justified the effort to me. They don't need to justify themselves to me, sure, but I'm part of the community, and I'm sure I'm not the only person wondering.

And then Geoff explained it: modularisation of the platform.

You know how for years we've been asking Adobe to modularise ColdFusion? Obviously the same applies to Lucee, except in a smaller way. ColdFusion's got a huge footprint, most of which one doesn't need most of the time, so why should one need to install it? Lucee's footprint is much smaller, but the same still applies.

That's interesting-ish in itself, but it has a very compelling knock-on effect. If everything is a module, then enhancements and bugfixes become a lot easier to deal with. One can mess around within the scope of the module's codebase, and provided one doesn't do anything daft, it won't impact the outside world (it needs to maintain its touchpoints to the outside world, obviously: its API). This means the testing and footprint for work on a given module is much smaller. And it makes the job just... easier. And whre things are easier, things progress faster, and more smoothly.

It will also mean that organisations that like to stay up to date and can implement changes expediently can do so. And it means that organisations which need to move more slowly with such things can probably round out their checks and balances a lot faster. It's less bureaucratic overhead to say "we're updating Lucee's PDF module" than it is to say "we're updating our Lucee installation". This in turn means that there doesn't need to be as much hesitation on the part of LAS if they do want to make breaking changes to modules... organisations who aren't ready for the breaking changes don't need to implement them. This means there can be a more dynamic attitude to moving things forward, as the "no person left behind" idea isn't so entrenched in whatever work is done. This goes back to Adobe's glacial approach to doing anything: the whole system is implemented and released as one huge blob, and everything has backwards compatibility issues, and even the smallest backwards compat challenge in one distant corner of the ColdFusion system will prevent the rest of it moving forward at a pace appropriate to 2016.

Update:

Another good example where this sort of thing pays off is detailed in Mary Jo's comment below: she needed to run an up-to-date version of Solr cos CF's one is so woefully out of date, but it wasn't really doable on CF due to its monolithic approach to packaging, and dependency spaghetti. This sort of thing will be much easier with Lucee 5 (not that they'd be so slack at keeping their libs up to date anyhow, but you know what I mean).


This is bloody great news for Lucee. I'm quite sure why they're not messaging it this way. Here's what they say about the OSGI work on their website (this is the page promoting it):

OSGi

Lucee 5 is completely OSGi based, OSGi is the defacto standard in most Java enterprise environments, to manage bundles (jar libraries) used by the environment.

This means all libraries used are managed by Lucee itself as OSGi bundles, but this does not end with 3rd party libraries. Lucee handles it's own core as an OSGi bundle, Lucee archives (.lar files) and even our Java based extensions are all OSGi based.

So Lucee 5 is an OSGi engine that manages all libraries used.

What is the benefit?

OSGi is a framework used in most major enterprise environments, it is well tested and stable.

OSGi allows for the running of different versions of the same library at the same time. Lucee for example is not using internally the latest version of the HSQLDB data source, but this does not stop you from using whichever version of this data source you like. OSGi allows you to use whichever version of a library you like, independent of what version of the same library Lucee itself is using or any other applications in the same environment.

We can update every library Lucee is using at any time without issue and without the need to restart the JVM. This means that Lucee patches can include updated libraries, making Lucee a lot more flexible than it was in the past.

Can I benefit from OSGi somehow?

Yes you can, Lucee 5.0 also comes with some new build in functions to use OSGi features directly.

Today you use createObject('java',"my.class.Path") to create Java Objects, but with the power of OSGi under the hood we can do more!

Lucee 5 introduces the function javaProxy() to load Java objects, this functions interface is optimized to OSGi, so you can do something like this:

dtf=javaProxy("org.joda.time.format.DateTimeFormat", "org-joda-time", "2.1.0");

The function syntax is:
javaProxy(string className [, string bundleName, string bundleVersion]);

Only the class name is required all other arguments are optional. Lucee will also download the bundles from the update provider automatically if they are not available locally.

The Lucee update provider already provides a wide range of bundles in various versions and this will be added to continuously overtime.

You can therefore load a certain class in a specific version, even, for example, if the Lucee core is using the same class with a different version.

OSGi is the biggest change in the Lucee 5.0 release that brings Lucee to a completely new level. Whilst it maybe not a dazzling new feature, it is a necessary one for an engine to be used in the enterprise segment.


Zzzzzzzzz. Oh sorry, you got to the end of that did you? Well done. I mean one can perhaps infer the bigger picture there if one takes the time, but - even as a reasonable adept technical person - I read that, my eyes glaze over, and I'm going "so the f*** what?". Sorry, but I am.

But if one frames it in the context of real world CFML devs/admins, dispense with the buzzwords and start it with "we've listened to the community and Lucee 5's platform is now completely modular [etc]", then you would have kept my interest all along.

I think the issue is that these docs are written by people so far up the Jefferies Tubes of Lucee they have lost sight of the fact they're not the core audience of Lucee. This was where I was struggling with Denny last night, and where I struggle in general with a bunch of the messaging from Lucee. In short, I guess, the technical people shouldn't be writing it or presenting it.

Now Geoff didsay he pointed out all this at the keynote of Dev.Objective(), and - yeah - he's the sort of person to understand how to message these things, but a presentation at Dev.Objective() is no use to most people, because most people weren't there. And to message didn't seem to trickle back out anyhow, so I wonder if it hit home anyway?

I really think this is a potentially excellent feature of Lucee, but not in the way they are currently positioning it. The message should not be about OSGi, it should be about modularity of the Lucee platform, and what immediate benefit it brings to Lucee's CFML community. Not to the few engineers behind the scenes who care about the implementation (we shouldn't need to care about the implementation for one thing), but how it's going to impact my life, as a Lucee CFML developer. It's not that OSGi has anything to do with it. It's that it modularises the platform.

I think there's a better story for LAS to be telling here.

Also in closing: thanks Denny, Geoff and Sean for the interesting discussion last night.

Righto. Time for breakfast and - more importantly - coffee.

--
Adam

Monday 14 December 2015

ColdFusion: more on this encodeForHtml() crap

G'day:
This is a follow-up to last week's ColdFusion: please help me discourage Adobe from releasing bad code article; my inspiration drawing from Brad and Sean's comment on same. I found myself writing too much in reply, so have extracted it into an article instead.

First Brad:
[...] the word "encode" is noise in the method if the class is already called "Encode". I kind of like "Encode.forHTML()" to cut down on verboseness. I'm not too much of a fan of Ruby's uber-succinct shortcuts [methods like to_s() instead of toString() ] but I think there's something to be said for trimming down on unnecessary fluff typing.
I initially started with the same thought (see my comment on the ticket):
So they should be in something like:

String Encode.forHtml(String stringToEncode)
Sorry, was being hasty before. That class / method name example I gave was rubbish: Encode is a verb, so makes no sense as a class name; and the method suggestion had similar grammatical shortcomings. Something more like Encoder or StringEncoder or something for the class, and the methods themselves should be the same as their procedural counterparts, so:

static String StringEncoder.encodeForHtml(String stringToEncode)
The name of the method needs to stand on its own two feet (methods have two feet, just like you and I, apparently ;-). "forHtml()" does not adequately describe what the method does. This is a bit semantic given it'll generally be referenced in context with its class, but I still think a method name should describe what it does. "forHtml()" sounds like one is coming in half-way through the description.

Of course at that point, the only difference (to the average programmer) between

Encode.forHTML()

and

EncodeForHTML()

is one character and I think the latter is easier to remember and easier to type since it reads the same but isn't peppered with punctuation.
Well I'd be advocating StringEncode.encodeForHtml(), so that makes even more typing still. I think an appropriate level of brevity is a factor in language design, but not the chief one. And I programming is not an exercise in typing: it's an exercise in thinking. I am perpetually bemused by these people who think programming is some sort of typing race. In the bigger scheme of things, typing a few extra characters each time one wants to encode something? So what?

Also in equivocating over Encode.forHTML() and EncodeForHTML() you're arguing a case for something I'm not contesting. Or even talking about. This conversation is not about whether we should have a headless function or an OO method: that horse has already bolted.

I'm kind of torn here, because I like the highly-academic idea that a language does things "properly" and according to best practices (or at least the precedent set by other languages at the time) but I also like the idea that CF (and many other dynamic, loosely typed languages) bend rules here and there with an end result of making things as simple and easy for the developer as possible in order to get code out quickly and efficiently. (Push complexity on the compiler, not the programmer) At the end of the day, I'm not quite sure what real-world difference there is between a collection of related functions that differentiate themselves by starting with "Encode." and a collection of related functions that differentiate themselves by starting with "Encode".
This is - again - misdirected equivocation. This is not part of the discussion. The ticket is specifically about the member-function implementation of the encoding functions.

Thursday 29 October 2015

CFML community working well together

G'day:
I've not much to say here, but I'm just really pleased how the CFML community - especially Dom Watson and Sean Corfield - have been chipping away and planning out and discussing a ColdFusion enhancement request Alex Skinner raised last night (Allow additional attributes to be added to CFPARAM tags).

I put my usual cynical / jaded oar in (that said, I stand by what I say!), but Dom and Alex have been having a lot of discussion today (not just on the bug tracker, but on the CFML Slack channel too), and it's great work.

Well done, community.

--
Adam


Thursday 30 July 2015

ColdFusion: can't tell its null from its... elbow

G'day:
Blimey: Acker is being of some use! He threw me this code last night, and asked what to make of it (well, OK, Sean asked me, but still).

found something unexpected:

a = isnull(request) && isnull(server) && isnull(form) && isnull(url) && isnull(33);

a = true ... meaning all are null
What the actual f*ck?

So I knocked together some test code to make things more clear:

// acker.cfm    
areAllNull = isnull(request) && isnull(server) && isnull(form) && isnull(url) && isNull({}) && isnull(33);

writeOutput("
isnull(request): #isnull(request)#<br>
isnull(server): #isnull(server)#<br>
isnull(form): #isnull(form)#<br>
isnull(url): #isnull(url)#<br>
isnull(33): #isnull(33)#<br>
isNull({}): #isNull({})#<br>
areAllNull: #areAllNull#<br>
");

And on ColdFusion, this yields:

isnull(request): YES
isnull(server): YES
isnull(form): YES
isnull(url): YES
isnull(33): YES
isNull({}): NO
areAllNull: NO


Adobe you dicks. How can an entire scope be null? How can 33 be null??? I looked in the docs ("IsNull()") and that didn't say anything like "oh, we just make sh!t up about what's null or not. To be honest we just don't know. I mean... how can one actually tell? What is null, anyhow?", which might explain it. But no.

Lucee are not lunatics:

isnull(request): false
isnull(server): false
isnull(form): false
isnull(url): false
isnull(33): false
isNull({}): false
areAllNull: false


I despair, Adobe. I really do. Chris Blackwell could not have put this better:


Raised as bug 4028246.

Update:

Just an update on this. In a moment reminiscent of the Orwellian position that 2+2=5, Adobe had this to say on the topic of the nullness of 33:


Are you having a f***ing laugh, mate?


And now these're something about Elvis to investigate too...

--
Adam

Saturday 27 June 2015

CFML: Slack

G'day:

Ages ago I encouraged people to join us on the ##coldfusion IRC channel ("I… aaaah… see"). Thanks to the likes of Ryan, Dan and Sean, I was introduced to the new #cfml slack channel last night, which seems to be mostly like IRC, but given a C21st treatment to the UI & UX. So I think I'll probably migrate over there.

If you're a CFML dev of any stripe, get yerself over there. There's separate sub-channels for ColdFusion and Lucee (and FW/1, and a few dozen others now (I'm updating this in 2023 as the URL below has been updated)), and - trust me - it's the most expedient way of a) getting help with CFML issues; b) networking with other CFML community members. If yer a CFML dev you should be wanting to do both of those things.

[obsolete guidance deleted]

Update

Use this link: http://cfml-slack.net/.

[obsolete guidance deleted]

See you there.

Righto.

--
Adam

CFML: I did not realise one cannot nest threads in CFML

G'day:
This is peer-group pressure at work. On IRC, Sean mentioned that in CFML (ColdFusion, Railo, Lucee... I have to admit I didn't think to look at OpenBD), one cannot nest thread calls. I was really surprised by this for a coupla reason:
  1. I was sure I would have - at some point - tried to do this. Even by accident.
  2. I couldn't see a good reason why.
Not that I doubted Sean, but he sugggested I did some investigation and write it up. I'm en route to Galway right now (ie: as I type I'm flying over Ireland), and I've had enough time to finish my previous article ("PHP: getting PHP 5 and PHP 7 running side by side on the same machine (a better way)"), and write the code for this article. So here goes.

Tuesday 23 June 2015

Epicaricacy

G'day:
This is a lazy-arse article - not least of all because I just point you back to one of my other articles from a few days ago - but in case you missed all this due to not following the comments on said article, the comments on "What I'd like to see in ColdFusion 12 (redux, as is happens)" are... well... something to behold as it all unfolds.

As Dan Skaggs said on IRC:

It's like a train wreck...I can't tear my self away from the page waiting for the "New Reply" message to pop up somewhere

Heh.

It starts slowly, but stick with it.

Anyway, yeah I'm a big meany for drawing attention to it. But hey... the headline warned you.

--
Adam

Monday 25 May 2015

Some CFML code that doesn't work

G'day:
I was sitting at Lord's yesterday watching England v NZ (and, um, we'll have no comments about that, thank-you very much), and a sudden thought popped into my head "Adam Presley might've been onto something there... if I leverage that....I wonder if I could get that code down to one statement?"

And that of course will mean nothing to anyone.

Thursday 29 January 2015

Lucee launch: thanks

G'day:
I was just thinking about this evening's Lucee launch. I'd just like to say that Micha gave an excellent presentation this evening, especially for a fella who was giving his first presentation ever. And also in a language that isn't his primary one. And not just some flippant "I fancy giving a presentation at a conference" sort of affair, but annoucing the release of something quite significant and controversial in our wee community, and career defining for him personally. No pressure then, eh?

Also I'd like to say thanks to Alex for providing the venue, beer, and general logistics for the thing. Nice one mate.

And to Mark Drew who sat there in the front row fielding all the questions coming in from a variety of online channels - the online meeting message feed, Twitter, IRC and my blog, and relaying them to Micha so he could answer them all.

I was out of the loop for a while during the presentation - unavoidable family stuff - and Brad stepped up and also fielded all the questions coming into my blog, interpreting, relaying, and intuiting answers on the fly.

Sean also fielded questions on IRC.

And other people helping out with the launch of Lucee this evening, including the meatspace attendees who also gave the thing a vibe and kept Micha honest.

Good work everyone.

--
Adam

Saturday 10 January 2015

Getting Clojure installed and running and "G'day World"-ing

G'day:
This is just another one of those article about my experience with working out how to get a language operational in my dev environment. All it really serves to do is to demonstrate there's not much to it, so it should not be a "barrier to entry" if you decide to have a look at Clojure. I am way too much a newbie with it to be offering anything actually insightful here though.

Obviously that said if one's just wanting to give it a bash, there's online REPLs about the place (eg: Try Clojure) so no need to install it, but their scope will be limited, and for some reason I prefer to have stuff running locally.

I've worked through the Try Clojure tutorial before - ages ago - but other than that know nothing about it other than it seems to be overly in love with parentheses. Here goes...

Thursday 1 January 2015

Saturday 11 October 2014

The received wisdom of TDD [etc]: Sean's feedback

G'day:
During the week I solicited feedback on my assessment of "The received wisdom of TDD and private methods". I got a small amount of good, usefull feedback, but not as much as I was hoping for.

However Sean came to the fore with a very long response, and I figure it's worth posting here so other people spot it and read it.


'ere 'tis, unabridged:

There are broadly two schools of thought on the issue of "private" methods and TDD:

1. private methods are purely an implementation detail that arise as part of the "refactor" portion of the cycle - they're completely irrelevant to the tests and they never need testing (because they only happen as part of refactoring other methods when your tests are already passing).

2. encapsulation is not particularly helpful and it's fine to just make things public if you want to add tests for new behavior within previously private methods.

The former is the classical position: classes are a black box except for their public API, and it's that public API that you test-drive.

The latter is an increasingly popular position that has gained traction as people rethink OOP, start to use languages that don't have "private", or start working in a more FP style. Python doesn't really have private methods (sure, you can use a double underscore prefix to "hide" a function but it's still accessible via the munged name which is '_TheClass__theFunction' for '__theFunction' inside 'TheClass'). Groovy has a 'private' keyword (for compatibility with Java) but completely ignores it. Both languages operate on trust and assume developers aren't idiots and aren't malicious. In FP, there's a tendency toward making everything public because there are fewer side-effects and some helper function you've created to help implement an API function might be useful to users of your code - and it's safe when it has no side-effects!

When I started writing Clojure, coming from a background of C++, Java, and CFML, I was quite meticulous about private vs public... and in Clojure you can still easily access a "private" function by using its fully-qualified name, e.g., `#'some.namespace/private-function` rather than just `private-function` or `some.namespace/private-function`. Using `#'` bypasses the access check. And the idiom in Clojure is generally to just make everything public anyway, possibly dividing code into a "public API" namespace and one or more "implementation" namespaces. The latter contain public functions that are only intended to be used by the former - but, again, the culture of trust means that users _can_ call the implementation functions if they want, on the understanding that an implementation namespace might change (and is likely to be undocumented).

My current position tends to be that if I'm TDD-ing code and want to refactor a function, I'll usually create a new test for the specifics of the helper I want to introduce, and then refactor into the helper to make all the tests pass (the original tests for the existing public function and the new test(s) for the helper function). Only if there's a specific reason for a helper to be private would I go that route (for example, it isn't a "complete" function on its own, or it manages side-effects that I don't want messed with outside of the calling function, etc). And, to be honest, in those cases, I'd probably just make it a local function inside the original calling function if it was that critical to hide it.

Google for `encapsulation harmful` and you'll see there's quite a body of opinion that "private by default" - long held to be a worthy goal in OOP - is an impediment to good software design these days (getters and setters considered harmful is another opinion you'll find out there).

That was longer than I intended!

Yeah Sean but it was bloody good. It all makes sense, and also goes a way to make me think I'm not a lunatic (at least not in this specific context).

And I have a bunch of reading to do on this whole "encapsulation harmful" idea. I'd heard it mentioned, screwed my nose up a bit, but didn't follow up. Now I will. Well: when I have a moment.

Anyway, there you go. Cheers for the effort put in to writing this, Sean. I owe you a beer or two.

Righto.

--
Adam

Tuesday 23 September 2014

OK, there's a precedent contradicting my PHP "Null" gripe from the other day

G'day:
What would I do without my guardian angel Sean? ;-)

Over the weekend I wrote an article "PHP: a fractal of [etc]... yeah, I'm now getting where you're coming from", which I - in passing - derided PHP's choice of null being a boolean false.

Friday 8 August 2014

More on <cfcatch>: Java exceptions

G'day:
This is a very quick appendix to this morning's article "<cfcatch>: my ignorance is reduced. Over a decade after it should have".

How about java exceptions? Well ColdFusion and Railo behave differently here.

Wednesday 23 July 2014

CFML: Finally had a chance to look at Sean's version of case()

G'day:
This took way longer to revisit than I intended. Apologies to Sean, as it might have seemed like I solicited some code from him and then ignored it. And, indeed, that was mostly the case other than an initial peek and a "hang on... what? How the? But... oh, I think I see..." and a conclusion my assessment of it needed more than 2min.

Right, so a coupla weeks back I wrote and article "Some more TestBox testing, case/when for CFML and some dodgy code". This demonstrated some code (and its tests) that I was messing around with that implemented a case/when/then/else function in CFML. That'll make more sense if you read the article.

Sean had a look at it and could see some simplifications which could be made, and popped a revised version through to me via Github ("adamcase / case.cfm"). As I alluded to above, my initial parsing of the solution just made my eyebrow raise - Spock-like - and other than that I saw what he was doing, but not how it worked (and it did work; the relevant tests still passed). Today I sat down and had a look at the code, and made myself understand it. And I like it.

Here it is:

Tuesday 15 July 2014

2

G'day:
I'm frickin' lousy with dates (as in "calendar", not as in "romance". Although the same applies, from memory ;-). Well: remembering dates is never a problem, but remembering what the current date is is something I'm not so good at. I forgot to touch base with my big sister on her birthday over the weekend... and there's another anniversary on the same day.

I've been doing this bloody blog for two years now. Which is approximately 23 months longer than I expected it to last.


Last year I gave you some stats ("1"). I'll try to do the same now.

  • I've now published 750 (this'll be the 751st) articles. I still have about a dozen in progress. The same ones as last year, funnily enough. The topics just don't have legs, I think.
  • And the word tally is now up around 600000 words. So in the second year I didn't write quite as much as the first year (350000), but spread over more articles (428 in the last 12 months vs 322 in the first year).
  • I've had another 3000 comments since the previous year's 2000. That's pretty cool. Thanks for the contributions everyone. Often the comments are more interesting than the articles, I find.
  • Google Analytics claims I've had 86000 visitors over the last year (up from 25k in the first year). So this thing is getting more popular. The average per day is 230-odd. It was around 120/day in year one. It's still not a huge amount of traffic, but I guess my potential audience is pretty small too.
  • The busiest day in the last 12 months was 5 March 2014, with 593 visitors. That was towards the end of the isValid() saga, with this article: "ColdFusion 11: Thank-you Carl, Mary-Jo, many other community members and indeed Rupesh", and a click-chasing one entitled "CFML is dying. Let's drop it off at Dignitas". Looking at the analytics, that was the bulk of it, plus I was writing a lot about new features in ColdFusion 11 around about then, which boosted things. That was also my biggest week ever, by quite a margin.
  • The most popular article last year was the one about me migrating from "ColdFusion Builder to Sublime Text 2". That's had 2200 visitors. The next most popular were as follows:
  • The most +1'ed article was "I am one step closer to being unshackled from ColdFusion". It's interesting that that was the one that people liked the most. It had 13 +1s. Most articles get none or maybe one, so that's quite a lot.
  • Last year I worked out which article had the most comments. I have no idea how I did that, and I can't be bothered working it out again. So erm... that'll remain a mystery.
I've blogged a lot about ColdFusion 11 during the year... what with it being in public beta and then being released. I've also compared its functionality to Railo's equivalents. I've shifted my primary dev platform at home to Railo now. I've done a lot of JavaScript over the last 12 months (I've spared you most of the detail), but haven't progressed in other languages as much as I'd like to. That's my mission for the next year.

I battered Adobe a lot about how they (don't) handle their bugs. I will continue to do this. They're long overdue for an updater to ColdFusion 10, for one thing; plus we should have had at least a coupla small updates to ColdFusion 11 by now.

The biggest shift in my coding practices in the last year has been down to reading Clean Code, and adopting a lot of its suggestions. My code is better for it. I've got my colleagues Chris and Brian to thank for this... both the encouragement to read the book, but also keeping at me about it. Sometimes to great irritation on my part. If you have not read that book: do so. Especially if you're either of the two members of our team who still haven't read it. Ahem.

Another thing I've been fascinated with this year gone is TestBox. I love it. I am looking forward to shifting off ColdFusion 9 at work so we can start converting our MXUnit styled tests to BDD ones. Brad and Luis are dudes.

I've bitched a lot about Stack Overflow, but contrary to what I threatened ("Not that it will really matter in the bigger scheme of things..."), I still answer questions there every day (if I can find questions I can answer, that is).

Railo continues to rock. As do Gert, Micha, Igal from Railo. They really have done brilliant work keeping CFML alive and interesting.

A bunch of people have motivated me to write this year... it's too difficult to pull out a list of the main miscreants, but Sean would be the top. And the list of my various muses (or adversaries!) is - as always - on the right hand side of the screen, over there.

Gavin deserves special mention, as he very kindly tried to raise money to get me across to CF.Objective() ("Shamelessful plug"), but we had to kill that plan just as it was getting started ("Do not sponsor me to go to CF.Objective()"). But happily Gert stumped up with a ticket at the last minute ("Well that was unexpected"), so I made it anyhow. I really am taken aback by you guys. Seriously.

And of course Mike from CFCamp paid for my entire conference last year too ("CFCamp 2013"). That was amazing. And I mean both Mike's generousity, and the conference itself. Go to it this year if you can: CFCamp.

Ray's done most of the work for ColdFusion UI the Right Way, but I've helped out a bit. I'm glad we got going with that project.

Thanks for your participation in this blog, everyone. If you weren't reading it or commenting on it, I'd've chucked it in. But you keep coming back. Cheers.

Oh and let's not forget: <cfclient> sucks arse. And I can tell that without using it, Dave Ferguson ;-)

--
Adam