%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This file is part of Logtalk <http://logtalk.org/>
%  Copyright 1998-2015 Paulo Moura <pmoura@logtalk.org>
%
%  Licensed under the Apache License, Version 2.0 (the "License");
%  you may not use this file except in compliance with the License.
%  You may obtain a copy of the License at
%
%      http://www.apache.org/licenses/LICENSE-2.0
%
%  Unless required by applicable law or agreed to in writing, software
%  distributed under the License is distributed on an "AS IS" BASIS,
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%  See the License for the specific language governing permissions and
%  limitations under the License.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% conflict between uses/2 directive and a predicate definition

:-object(usesconflict).

	:- uses(list, [member/2]).

	member(H, [H| _]).		% an object (or category) cannot define a
	member(H, [_| T]) :-	% predicate referenced on a uses/2 directive
		member(H, T).

:- end_object.

Fix some code layout issues in example source file

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This file is part of Logtalk <http://logtalk.org/>
%  Copyright 1998-2015 Paulo Moura <pmoura@logtalk.org>
%
%  Licensed under the Apache License, Version 2.0 (the "License");
%  you may not use this file except in compliance with the License.
%  You may obtain a copy of the License at
%
%      http://www.apache.org/licenses/LICENSE-2.0
%
%  Unless required by applicable law or agreed to in writing, software
%  distributed under the License is distributed on an "AS IS" BASIS,
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%  See the License for the specific language governing permissions and
%  limitations under the License.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% conflict between uses/2 directive and a predicate definition

:- object(usesconflict).

	:- uses(list, [member/2]).

	% an object (or category) cannot define a
	% predicate referenced on a uses/2 directive
	member(H, [H| _]).
	member(H, [_| T]) :-
		member(H, T).

:- end_object.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This file is part of Logtalk <http://logtalk.org/>
%
%  Logtalk is free software. You can redistribute it and/or modify it under
%  the terms of the FSF GNU General Public License 3  (plus some additional
%  terms per section 7).        Consult the `LICENSE.txt` file for details.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- if((current_logtalk_flag(prolog_dialect, Dialect), (Dialect == swi; Dialect == yap))).

	:- initialization((
		set_logtalk_flag(report, warnings),
		logtalk_load(lgtunit(loader)),
		logtalk_load(jpl, [source_data(on), debug(on)]),
		logtalk_load(tests, [hook(lgtunit)]),
		tests::run
	)).

:- else.

	:- initialization((
		write('(not applicable)'), nl
	)).

:- endif.

Fix unit tests for the `jpl` example when using YAP as the back-end Prolog compiler

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This file is part of Logtalk <http://logtalk.org/>
%
%  Logtalk is free software. You can redistribute it and/or modify it under
%  the terms of the FSF GNU General Public License 3  (plus some additional
%  terms per section 7).        Consult the `LICENSE.txt` file for details.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- if((current_logtalk_flag(prolog_dialect, Dialect), (Dialect == swi; Dialect == yap))).

	:- use_module(library(jpl), []).

	:- initialization((
		set_logtalk_flag(report, warnings),
		logtalk_load(lgtunit(loader)),
		logtalk_load(jpl, [source_data(on), debug(on)]),
		logtalk_load(tests, [hook(lgtunit)]),
		tests::run
	)).

:- else.

	:- initialization((
		write('(not applicable)'), nl
	)).

:- endif.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This file is part of Logtalk <http://logtalk.org/>
%
%  Logtalk is free software. You can redistribute it and/or modify it under
%  the terms of the FSF GNU General Public License 3  (plus some additional
%  terms per section 7).        Consult the `LICENSE.txt` file for details.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- initialization(catch(client_b_3_variant::test(_), Error, w(riteq(Error), nl))).

:- object(library_b_3_variant).

	:- public(m/2).
	:- meta_predicate(m(2, *)).
	m(Closure, Arg) :-
		call(Closure, Arg, _).

:- end_object.

:- object(client_b_3_variant).

	:- public(test/1).
	test(X) :-
		library_b_3_variant::m(a, X).

	a(1). a(2).

	a(3, one). a(4, two).

:- end_object.

Fix syntax error in the `securemp` example

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  This file is part of Logtalk <http://logtalk.org/>
%
%  Logtalk is free software. You can redistribute it and/or modify it under
%  the terms of the FSF GNU General Public License 3  (plus some additional
%  terms per section 7).        Consult the `LICENSE.txt` file for details.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- initialization(catch(client_b_3_variant::test(_), Error, (writeq(Error), nl))).

:- object(library_b_3_variant).

	:- public(m/2).
	:- meta_predicate(m(2, *)).
	m(Closure, Arg) :-
		call(Closure, Arg, _).

:- end_object.

:- object(client_b_3_variant).

	:- public(test/1).
	test(X) :-
		library_b_3_variant::m(a, X).

	a(1). a(2).

	a(3, one). a(4, two).

:- end_object.


/*
 * NSBrowser.j
 * nib2cib
 *
 * Created by Andrew Hankinson.
 * Copyright 2013.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

@import <AppKit/CPBrowser.j>

@implementation CPBrowser (NSCoding)

- (id)NS_initWithCoder:(CPCoder)aCoder
{
    _allowsEmptySelection = YES;

    if (self = [super NS_initWithCoder:aCoder])
    {
        _isSelectable             = [aCoder decodeBoolForKey:@"NSSelectable"];
        _allowsMultipleSelection  = [aCoder decodeBoolForKey:@"NSAllowsMultipleSelection"];
    }

    return self;
}

@end

@implementation NSBrowser : CPBrowser
{
}

- (id)initWithCoder:(CPCoder)aCoder
{
    return [self NS_initWithCoder:aCoder];
}

- (Class)classForKeyedArchiver
{
    return [CPBrowser class];
}

@end


@import <Foundation/CPException.j>

@implementation CPExceptionTest : OJTestCase
{
    CPException exception;
}

- (void)setUp
{
    var exception = [CPException exceptionWithName:@"CPGenericException"
                                        reason:@"Margins must be positive"
                                      userInfo:nil];
}

- (void)testInitWithNameReasonUserInfo
{
    exception = [[CPException alloc] initWithName:@"CPUnsupportedMethodException"
                                           reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."
                                         userInfo:nil];
    [self assert:[exception name] equals:"CPUnsupportedMethodException"];
    [self assert:[exception reason] equals:"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
    [self assertThrows:function(){[exception raise]}];
}

- (void)testRaiseReason
{
    [self assertThrows:function(){[CPException raise:@"CPGenericException" reason:@"Margins must be positive"];}];
}

- (void)testName
{
    [self assert:[exception name] equals:@"CPGenericException"];
}

- (void)testReason
{
    [self assert:[exception reason] equals:@"Margins must be positive"];
}

- (void)testUserInfo
{
    [self assert:[exception userInfo] equals:null];
}

- (void)testRaise
{
    [self assertThrows:function() { [exception raise]; }];
}

@end

Fix merge error in 1f98dbd.

@import <Foundation/CPException.j>

@implementation CPExceptionTest : OJTestCase
{
    CPException exception;
}

- (void)setUp
{
    exception = [CPException exceptionWithName:@"CPGenericException"
                                        reason:@"Margins must be positive"
                                      userInfo:nil];
}

- (void)testInitWithNameReasonUserInfo
{
    exception = [[CPException alloc] initWithName:@"CPUnsupportedMethodException"
                                           reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."
                                         userInfo:nil];
    [self assert:[exception name] equals:"CPUnsupportedMethodException"];
    [self assert:[exception reason] equals:"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
    [self assertThrows:function(){[exception raise]}];
}

- (void)testRaiseReason
{
    [self assertThrows:function(){[CPException raise:@"CPGenericException" reason:@"Margins must be positive"];}];
}

- (void)testName
{
    [self assert:[exception name] equals:@"CPGenericException"];
}

- (void)testReason
{
    [self assert:[exception reason] equals:@"Margins must be positive"];
}

- (void)testUserInfo
{
    [self assert:[exception userInfo] equals:null];
}

- (void)testRaise
{
    [self assertThrows:function() { [exception raise]; }];
}

@end


Test CPResponder's interpretKeyEvents in preparation for rewrite.

@import <AppKit/CPView.j>
@import <AppKit/CPApplication.j>
@import <AppKit/CPText.j>

@import <AppKit/CPPlatformWindow+DOMKeys.j>

[CPApplication sharedApplication]

@implementation CPResponderTest : OJTestCase
{
    CPWindow    theWindow;
    CPResponder responder;
}

- (void)setUp
{
    responder = [TestResponder new];
    responder.doCommandCalls = [];
}

- (void)testInterpretKeyEvents
{
    var tests = [
        CPKeyCodes.PAGE_UP,     CPPageUpFunctionKey,        @selector(pageUp:),
        CPKeyCodes.PAGE_DOWN,   CPPageDownFunctionKey,      @selector(pageDown:),
        CPKeyCodes.LEFT,        CPLeftArrowFunctionKey,     @selector(moveLeft:),
        CPKeyCodes.RIGHT,       CPRightArrowFunctionKey,    @selector(moveRight:),
        CPKeyCodes.UP,          CPUpArrowFunctionKey,       @selector(moveUp:),
        CPKeyCodes.DOWN,        CPDownArrowFunctionKey,     @selector(moveDown:),
        CPKeyCodes.BACKSPACE,   CPDeleteCharacter,          @selector(deleteBackward:),
        CPKeyCodes.ENTER,       CPCarriageReturnCharacter,  @selector(insertLineBreak:),
        CPKeyCodes.ESC,         CPEscapeFunctionKey,        @selector(cancel:),
        CPKeyCodes.TAB,         CPTabCharacter,             @selector(insertTab:)
    ];

    for (var i=0; i<tests.length; i += 3)
    {
        var keyCode = tests[i],
            character = tests[i+1],
            selector = tests[i+2];

        responder.doCommandCalls = [];
        keyEvent = [CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:0
            timestamp:nil windowNumber:nil context:nil
            characters:character charactersIgnoringModifiers:character isARepeat:NO keyCode:keyCode];
        [responder interpretKeyEvents:[keyEvent]];
        [self assert:[selector] equals:responder.doCommandCalls];
    }

    responder.doCommandCalls = [];
    keyEvent = [CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPShiftKeyMask
        timestamp:nil windowNumber:nil context:nil
        characters:CPTabCharacter charactersIgnoringModifiers:CPTabCharacter isARepeat:NO keyCode:CPKeyCodes.TAB];
    [responder interpretKeyEvents:[keyEvent]];
    [self assert:[@selector(insertBackTab:)] equals:responder.doCommandCalls];
}

@end

@implementation TestResponder : CPResponder
{
    CPArray   doCommandCalls;
}

- (void)doCommandBySelector:(SEL)aSelector
{
    doCommandCalls.push(aSelector);
    [super doCommandBySelector:aSelector];
}

@end


@import <AppKit/CPPanel.j>

@implementation CPSavePanel : CPPanel
{
    CPURL   _URL;
}

+ (id)savePanel
{
    return [[CPSavePanel alloc] init];
}

- (CPInteger)runModal
{
    // FIXME: Is this correct???
    [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];

    if (typeof window["cpSavePanel"] === "function")
    {
        var resultObject = window.cpSavePanel(),
            result = resultObject.button;

        _URL = result ? [CPURL URLWithString:resultObject.URL] : nil;
    }
    else
    {
        // FIXME: This is not the best way to do this.
        var documentName = window.prompt("Document Name:"),
            result = documentName !== null;

        _URL = result ? [[self class] proposedFileURLWithDocumentName:documentName] : nil;
    }

    return result;
}

- (CPURL)URL
{
    return _URL;
}

@end

Add some support for setting save panel properties in desktop mode

@import <AppKit/CPPanel.j>

@implementation CPSavePanel : CPPanel
{
    CPURL   _URL;

    BOOL    _isExtensionHidden @accessors(getter=isExtensionHidden, setter=setExtensionHidden:);
    BOOL    _canSelectHiddenExtension @accessors(property=canSelectHiddenExtension);
    BOOL    _allowsOtherFileTypes @accessors(property=allowsOtherFileTypes);
    BOOL    _canCreateDirectories @accessors(property=canCreateDirectories);

    CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
}

+ (id)savePanel
{
    return [[CPSavePanel alloc] init];
}

- (id)init
{
    if (self = [super init])
    {
        _canCreateDirectories = YES;
    }

    return self;
}

- (CPInteger)runModal
{
    // FIXME: Is this correct???
    [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];

    if (typeof window["cpSavePanel"] === "function")
    {
        var resultObject = window.cpSavePanel({
                isExtensionHidden: _isExtensionHidden,
                canSelectHiddenExtension: _canSelectHiddenExtension,
                allowsOtherFileTypes: _allowsOtherFileTypes,
                canCreateDirectories: _canCreateDirectories,
                allowedFileTypes: _allowedFileTypes
            }),
            result = resultObject.button;

        _URL = result ? [CPURL URLWithString:resultObject.URL] : nil;
    }
    else
    {
        // FIXME: This is not the best way to do this.
        var documentName = window.prompt("Document Name:"),
            result = documentName !== null;

        _URL = result ? [[self class] proposedFileURLWithDocumentName:documentName] : nil;
    }

    return result;
}

- (CPURL)URL
{
    return _URL;
}

@end


/*
 * CPText.j
 * AppKit
 *
 * Created by Alexander Ljungberg.
 * Copyright 2010, WireLoad, LLC.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

@import "CPView.j"

CPTabCharacter                  = "\u0009";
CPFormFeedCharacter             = "\u000c";
CPNewlineCharacter              = "\u000a";
CPCarriageReturnCharacter       = "\u000d";
CPEnterCharacter                = "\u0003";
CPBackspaceCharacter            = "\u0008";
CPBackTabCharacter              = "\u0019";
CPDeleteCharacter               = "\u007f";

@implementation CPText : CPView
{

}

@end

Fix for nib2cib breaking with an emptry class implementation.

/*
 * CPText.j
 * AppKit
 *
 * Created by Alexander Ljungberg.
 * Copyright 2010, WireLoad, LLC.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

@import "CPView.j"

CPTabCharacter                  = "\u0009";
CPFormFeedCharacter             = "\u000c";
CPNewlineCharacter              = "\u000a";
CPCarriageReturnCharacter       = "\u000d";
CPEnterCharacter                = "\u0003";
CPBackspaceCharacter            = "\u0008";
CPBackTabCharacter              = "\u0019";
CPDeleteCharacter               = "\u007f";

/*@implementation CPText : CPView
{

}

@end*/


/*
 * CPPlatformWindow+Title.j
 *
 * Copyright (C) 2010 Antoine Mercadal <antoine.mercadal@inframonde.eu>
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

@import <AppKit/CPPlatformWindow.j>

/*! @ingroup categories
    Category that enable the get the _DOMWindow element
*/
@implementation CPPlatformWindow (DOMWindowAccessible)

- (id)DOMWindow
{
    return _DOMWindow;
}

@end

Fix crash with external VNC window

/*
 * CPPlatformWindow+Title.j
 *
 * Copyright (C) 2010 Antoine Mercadal <antoine.mercadal@inframonde.eu>
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

@import <AppKit/CPPlatformWindow.j>

/*! @ingroup categories
    Category that enable the get the _DOMWindow element
*/
@implementation CPPlatformWindow (DOMWindowAccessible)

- (id)DOMWindow
{
    return self._DOMWindow;
}

@end


/*
 * AppController.j
 * resize
 *
 * Created by You on December 24, 2012.
 * Copyright 2012, Your Company All rights reserved.
 */

@import <Foundation/CPObject.j>

@implementation AppController : CPObject
{
    @outlet CPWindow theWindow;
}

- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
}

- (void)awakeFromCib
{
    [theWindow setFullPlatformWindow:YES];
}

- (@action)resizeStyleDidChange:(id)sender
{
    [CPWindow setGlobalResizeStyle:[[sender selectedRadio] tag]];
}

@end

Add liveResize testing in CPWindowResizeStyle manual test The window title changes during live resize.

/*
 * AppController.j
 * resize
 *
 * Created by You on December 24, 2012.
 * Copyright 2012, Your Company All rights reserved.
 */

@import <Foundation/CPObject.j>

@implementation AppController : CPObject
{
    @outlet CPWindow theWindow;
}

- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
    var center = [CPNotificationCenter defaultCenter];

    [center addObserver:self
               selector:@selector(viewWillStartLiveResize:)
                   name:CPWindowWillStartLiveResizeNotification
                 object:nil];

    [center addObserver:self
               selector:@selector(viewDidEndLiveResize:)
                   name:CPWindowDidEndLiveResizeNotification
                 object:nil];
}

- (void)viewWillStartLiveResize:(CPNotification)note
{
    var w = [note object];
    w._previousStyle = [w title];
    [w setTitle:@"In Live Resize …"];
}

- (void)viewDidEndLiveResize:(CPNotification)note
{
    var w = [note object];
    [w setTitle:w._previousStyle];
}

- (void)awakeFromCib
{
    [theWindow setFullPlatformWindow:YES];
}

- (@action)resizeStyleDidChange:(id)sender
{
    [CPWindow setGlobalResizeStyle:[[sender selectedRadio] tag]];
}

@end


/*
 * AppController.j
 * __Product__
 *
 * Created by __Me__ on __Date__.
 * Copyright 2008 __MyCompanyName__. All rights reserved.
 */

@import <Foundation/CPObject.j>

@implementation AppController : CPObject
{
}

- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
    var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
        contentView = [theWindow contentView];

    var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];

    [label setStringValue:@"Hello World!"];
    [label setFont:[CPFont boldSystemFontOfSize:24.0]];

    [label sizeToFit];

    [label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin];
    [label setFrameOrigin:CGPointMake((CGRectGetWidth([contentView bounds]) - CGRectGetWidth([label frame])) / 2.0, (CGRectGetHeight([contentView bounds]) - CGRectGetHeight([label frame])) / 2.0)];

    [contentView addSubview:label];

    [theWindow orderFront:self];

    // Uncomment the following line to turn on the standard menu bar.
    //[CPMenu setMenuBarVisible:YES];
}

@end

Update the new app template.

/*
 * AppController.j
 * __Product__
 *
 * Created by __Me__ on __Date__.
 * Copyright 2008 __MyCompanyName__. All rights reserved.
 */

@import <Foundation/CPObject.j>

@implementation AppController : CPObject
{
}

- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
    var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
        contentView = [theWindow contentView];

    var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];

    [label setStringValue:@"Hello World!"];
    [label setFont:[CPFont boldSystemFontOfSize:24.0]];

    [label sizeToFit];

    [label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin];
    [label setCenter:[contentView center]];

    [contentView addSubview:label];

    [theWindow orderFront:self];

    // Uncomment the following line to turn on the standard menu bar.
    //[CPMenu setMenuBarVisible:YES];
}

@end


var OS = require("os");
var FILE = require("file");

function cleanup() {
    ["ToolsTestApp", "PressTestApp", "FlattenTestApp"].forEach(function(dir) {
        if (FILE.isDirectory(dir))
            FILE.rmtree(dir);
    });
}

@implementation ToolsTest : OJTestCase
{
}

- (void)setUp
{
    cleanup();
}

- (void)testTools
{
    var status;

    status = OS.system(["capp", "gen", "ToolsTestApp"].map(OS.enquote).join(" ") + " > /dev/null");
    [self assert:status equals:0 message:"capp gen failed"];

    status = OS.system(["press", "-f", "ToolsTestApp", "PressTestApp"].map(OS.enquote).join(" ") + " > /dev/null");
    [self assert:status equals:0 message:"press failed"];

    status = OS.system(["flatten", "-f", "ToolsTestApp", "FlattenTestApp"].map(OS.enquote).join(" ") + " > /dev/null");
    [self assert:status equals:0 message:"flatten failed"];
}

- (void)tearDown
{
    cleanup();
}

@end

Update order of variable to tets in assert

var OS = require("os");
var FILE = require("file");

function cleanup() {
    ["ToolsTestApp", "PressTestApp", "FlattenTestApp"].forEach(function(dir) {
        if (FILE.isDirectory(dir))
            FILE.rmtree(dir);
    });
}

@implementation ToolsTest : OJTestCase
{
}

- (void)setUp
{
    cleanup();
}

- (void)testTools
{
    var status;

    status = OS.system(["capp", "gen", "ToolsTestApp"].map(OS.enquote).join(" ") + " > /dev/null");
    [self assert:0 equals:status message:"capp gen failed"];

    status = OS.system(["press", "-f", "ToolsTestApp", "PressTestApp"].map(OS.enquote).join(" ") + " > /dev/null");
    [self assert:0 equals:status message:"press failed"];

    status = OS.system(["flatten", "-f", "ToolsTestApp", "FlattenTestApp"].map(OS.enquote).join(" ") + " > /dev/null");
    [self assert:0 equals:status message:"flatten failed"];
}

- (void)tearDown
{
    cleanup();
}

@end


PngRenderer = require './png_renderer'

    class PngBlocks extends PngRenderer

      do_render: (ctx, path) =>
        width  = @config.width
        height = @config.height

        for step in path
          ctx.fillStyle = @config.block.fill_color

          ctx.beginPath()
          ctx.rect width * step.col, height * step.row, width, height
          ctx.fill()

    module.exports = PngBlocks

Use to path data structure (no row/col - now it's x/y)

PngRenderer = require './png_renderer'

    class PngBlocks extends PngRenderer

      do_render: (ctx, path) =>
        width  = @config.width
        height = @config.height

        for step in path
          ctx.fillStyle = @config.block.fill_color

          ctx.beginPath()
          ctx.rect width * step.x, height * step.y, width, height
          ctx.fill()

    module.exports = PngBlocks


Module = require '../../module'
    q      = require 'q'

    class PngRenderer extends Module
      deps: [ 'fs', 'canvas', 'path', 'config' ]
      pub:  [ 'render', 'flush' ]

      @draw_to: undefined
      @ctx:     undefined

      initialize: (params) =>
        deferred = q.defer()

        super()
          .then =>
            @initialize_drawing_context() unless PngRenderer.draw_to?

            deferred.resolve @

        deferred.promise

      initialize_drawing_context: =>
        width  = @config.width  * @config.grid[0].length
        height = @config.height * @config.grid.length

        PngRenderer.draw_to = new @canvas width, height
        PngRenderer.ctx     = PngRenderer.draw_to.getContext '2d'

      render: (path) => @done @do_render path

      flush: (level) =>
        stream = @fs.createWriteStream @path.join __dirname, '..', '..', '..', 'output', "#{level}.png"

        for row, r in @config.grid
          for cell, c in @config.grid[r]
            @config.grid[r][c] = false

        stream.on 'finish', => @done 'Persisted', location: level

        PngRenderer.draw_to.createPNGStream().pipe stream

    module.exports = PngRenderer

Stop performing unnecessary test when calling initialize_drawing_context

Module = require '../../module'
    q      = require 'q'

    class PngRenderer extends Module
      deps: [ 'fs', 'canvas', 'path', 'config' ]
      pub:  [ 'render', 'flush' ]

      @draw_to: undefined
      @ctx:     undefined

      initialize: (params) =>
        deferred = q.defer()

        super()
          .then =>
            @initialize_drawing_context()

            deferred.resolve @

        deferred.promise

      initialize_drawing_context: =>
        width  = @config.width  * @config.grid[0].length
        height = @config.height * @config.grid.length

        PngRenderer.draw_to = new @canvas width, height
        PngRenderer.ctx     = PngRenderer.draw_to.getContext '2d'

      render: (path) => @done @do_render path

      flush: (level) =>
        stream = @fs.createWriteStream @path.join __dirname, '..', '..', '..', 'output', "#{level}.png"

        for row, r in @config.grid
          for cell, c in @config.grid[r]
            @config.grid[r][c] = false

        stream.on 'finish', => @done 'Persisted', location: level

        PngRenderer.draw_to.createPNGStream().pipe stream

    module.exports = PngRenderer


# Batman.Model Associations

There are 3 types of assocations available on `Batman.Model`: `hasMany`, `belongsTo`, and `hasOne`.

## Batman.AssocationProxy

Association accessors won't always return a `Batman.Set`. Generally they will return a `Batman.AssocationProxy` named after the association type,
such as `Batman.BelongsToAssociationProxy` or `Batman.HasManyAssociationProxy`.

`Batman.AssociationProxy` defines useful methods such as `@load`, `@toJSON`, `@loadFromLocal`. It also defines accessors such as `%loaded` and `%target`.

## Shared Options
All assocations have a common set of options, as well as specific options for each type of relationship.

##### `saveInline[= true] : boolean`
Whether we should save the child objects as part of the parent object's JSON

##### `autoload[= true] : boolean`
 Whether we should load the child objects when loading the parent object.

##### `nestUrl[= false] : boolean`
Whether the child models are persisted beneath the parent models url structure.

##### `name : String`
The name of the child model for the association. (eg. App.Product)

##### `inverseOf : String`
The foreign association to set on load.

##### `encoderKey : String`
The key to encode and decode the assocation into/from JSON.

##### `namespace[= Batman.currentApp] : String`
The location of the model definition for the association.

##### `polymorphic[= false] : boolean`
Whether the association is polymorphic.

## @hasMany(label[, options])
`hasMany` assocations are used for expressing one-to-many relationships on a `Batman.Model`.

- `label : String`
- `options : Object`
  - `primaryKey : String`
  - `foreignKey : String`
  - `as : String`
    - the parent association name on child objects in a polymorphic association

## @hasOne(label[, options])
`hasOne` associations are used for expressing one-to-one relationships on a `Batman.Model`.

- `label : String`
- `options : Object`
  - `primaryKey : String`
  - `foreignKey : String`
  - `as : String`
    - the parent association name on child objects in a polymorphic association

## @belongsTo(label[, options])
`belongsTo` associatons are used fro expressing one-to-one child relationships on a `Batman.Model`.

- `label : String`
- `options : Object`
  - `primaryKey : String`
  - `foreignKey : String`
  - `encodeForeignKey[= true] boolean`
    - whether to encode the foreignKey value when persisting the model


PngRenderer = require './png_renderer'

    class PngBlocks extends PngRenderer

      do_render: (path) =>
        ctx    = PngRenderer.ctx
        width  = @config.width
        height = @config.height

        for step in path
          ctx.fillStyle = @config.block.fill_color

          ctx.beginPath()
          ctx.rect width * step.col, height * step.row, width, height
          ctx.fill()

    module.exports = PngBlocks

Update do_render signature to accept ctx bing passed

PngRenderer = require './png_renderer'

    class PngBlocks extends PngRenderer

      do_render: (ctx, path) =>
        width  = @config.width
        height = @config.height

        for step in path
          ctx.fillStyle = @config.block.fill_color

          ctx.beginPath()
          ctx.rect width * step.col, height * step.row, width, height
          ctx.fill()

    module.exports = PngBlocks


PngRenderer = require './png_renderer'

    class PngStepCount extends PngRenderer

      do_render: (ctx, path) =>
        width  = @config.width
        height = @config.height

        for step, i in path
          ctx.font      = "#{Math.floor height * 0.25}pt Arial"
          ctx.textAlign = @config.step_count.text.align
          ctx.fillStyle = @config.step_count.text.color

          x = (width  * 0.5) + (width  * step.col)
          y = (height * 0.5) + (height * step.row)

          ctx.beginPath()
          ctx.fillText i, x, y

    module.exports = PngStepCount

Use new path data structure (no more row/col - it's now x/y)

PngRenderer = require './png_renderer'

    class PngStepCount extends PngRenderer

      do_render: (ctx, path) =>
        width  = @config.width
        height = @config.height

        for step, i in path
          ctx.font      = "#{Math.floor height * 0.25}pt Arial"
          ctx.textAlign = @config.step_count.text.align
          ctx.fillStyle = @config.step_count.text.color

          x = (width  * 0.5) + (width  * step.x)
          y = (height * 0.5) + (height * step.y)

          ctx.beginPath()
          ctx.fillText i, x, y

    module.exports = PngStepCount


Madul = require 'madul'

    class InitExecutionContext extends Madul

      _action_exists: (input, ref, done, fail) =>
        if @[ref]?[input.ACTION]?
          input.EXECUTE = @[ref][input.ACTION]

          done()
        else
          fail "Action #{input.ACTION} not available"

      before: (input, done, fail) ->
        spec = Madul.PARSE_SPEC ".#{input.MODULE}"

        if @[spec.ref]?
          @_action_exists input, spec.ref, done, fail
        else
          Madul.DUMMY [ input.MODULE ], (dum) =>
            @[spec.ref] = dum[spec.ref]
            @_action_exists input, spec.ref, done, fail

    module.exports = InitExecutionContext

Load execution contexts as project local dependencies

Madul = require 'madul'

    class InitExecutionContext extends Madul

      _action_exists: (input, ref, done, fail) =>
        if @[ref]?[input.ACTION]?
          input.EXECUTE = @[ref][input.ACTION]

          done()
        else
          fail "Action #{input.ACTION} not available"

      before: (input, done, fail) ->
        spec = Madul.PARSE_SPEC ".#{input.MODULE}"

        if @[spec.ref]?
          @_action_exists input, spec.ref, done, fail
        else
          Madul.DUMMY [ ".#{input.MODULE}" ], (dum) =>
            @[spec.ref] = dum[spec.ref]
            @_action_exists input, spec.ref, done, fail

    module.exports = InitExecutionContext


Module = require './module'
    Carver = require './carver'
    q      = require 'q'

    class Skyll extends Module
      deps: [ 'q', 'config' ]
      pub:  [ 'craft' ]

      render_pipeline: [ ]

      initialize: (params) =>
        deferred      = q.defer()
        @starting_row = params.starting_row

        super().then =>
          for p in @config.pipeline
            @deps.push p unless @deps.includes p

          @hydrate_dependencies().then =>
            for p in @config.pipeline
              @render_pipeline.push @[p] unless @render_pipeline.includes @[p]

            deferred.resolve @

        deferred.promise

      craft: (level) =>
        new Carver()
          .initialize @starting_row
          .then (mod)  => mod.carve_path()
          .then (path) => @q.all @render_pipeline.map (p) => p.render path
          .then        => @render_pipeline[0].flush level
          .then        => @render_pipeline[0].reset()
          .then        => @done()

    module.exports = Skyll

Use madul's dependency injection instead of require for Carver

Module = require './module'
    q      = require 'q'

    class Skyll extends Module
      deps: [ 'q', 'config', 'carver', 'async' ]
      pub:  [ 'craft' ]

      render_pipeline: [ ]

      initialize: (params) =>
        deferred      = q.defer()
        @starting_row = params.starting_row

        super().then =>
          for p in @config.pipeline
            @deps.push p unless @deps.includes p

          @hydrate_dependencies().then =>
            for p in @config.pipeline
              @render_pipeline.push @[p] unless @render_pipeline.includes @[p]

            deferred.resolve @

        deferred.promise

      craft: (level) =>
        new Carver()
          .initialize @starting_row
          .then (mod)  => mod.carve_path()
          .then (path) => @q.all @render_pipeline.map (p) => p.render path
          .then        => @render_pipeline[0].flush level
          .then        => @render_pipeline[0].reset()
          .then        => @done()

    module.exports = Skyll


This job collects data from Sonar for the Sonar keys in the `SubjectSourceIds`
collection.

    class @SonarJob extends Job
      @setupCron: (parser) ->
        parser.recur().every(1).minute()

      handleJob: ->
        for subject_source_id in SubjectSourceIds.find().fetch()
          source_id = subject_source_id.source
          source = Sources.findOne({_id: source_id})
          if source.type != 'sonar'
            continue
          sonar_key = subject_source_id.title
          title = "NCLOC " + sonar_key
          try
            result = HTTP.get source.url + 'api/resources?resource=' + sonar_key + '&metrics=true'
            error_message = ''
          catch e
            error_message = e.message
          if result
            json = result.data
            for metric in json[0]['msr']
              if metric['key'] == 'ncloc'
                ncloc = metric['val']
          else
            ncloc = -1
          if error_message != ''
            description = error_message
          else
            description = ncloc + ' LOC'
          insertMeasurement
            projectId: source.projectId
            title: title
            description: description
          console.log(title, description, source.type, source.url, ncloc)

    Job.push new SonarJob

Use subject title in measurement instead of subject-source-id.

This job collects data from Sonar for the Sonar keys in the `SubjectSourceIds`
collection.

    class @SonarJob extends Job
      @setupCron: (parser) ->
        parser.recur().every(1).minute()

      handleJob: ->
        for subject_source_id in SubjectSourceIds.find().fetch()
          source = Sources.findOne({_id: subject_source_id.source})
          if source.type != 'sonar'
            continue
          subject = Subjects.findOne({_id: subject_source_id.subject})
          title = "NCLOC " + subject.title
          sonar_key = subject_source_id.title
          try
            result = HTTP.get source.url + 'api/resources?resource=' + sonar_key + '&metrics=true'
            error_message = ''
          catch e
            error_message = e.message
          if result
            json = result.data
            for metric in json[0]['msr']
              if metric['key'] == 'ncloc'
                ncloc = metric['val']
          else
            ncloc = -1
          if error_message != ''
            description = error_message
          else
            description = ncloc + ' LOC'
          insertMeasurement
            projectId: source.projectId
            title: title
            description: description
          console.log(title, description, sonar_key, source.type, source.url, ncloc)

    Job.push new SonarJob


PngRenderer = require './png_renderer'

    class PngStepCount extends PngRenderer

      do_render: (path) =>
        ctx    = PngRenderer.ctx
        width  = @config.width
        height = @config.height

        for step, i in path
          ctx.font      = "#{Math.floor height * 0.25}pt Arial"
          ctx.textAlign = @config.step_count.text.align
          ctx.fillStyle = @config.step_count.text.color

          x = (width  * 0.5) + (width  * step.col)
          y = (height * 0.5) + (height * step.row)

          ctx.beginPath()
          ctx.fillText i, x, y

    module.exports = PngStepCount

Update do_render signature to accept ctx being passed in

PngRenderer = require './png_renderer'

    class PngStepCount extends PngRenderer

      do_render: (ctx, path) =>
        width  = @config.width
        height = @config.height

        for step, i in path
          ctx.font      = "#{Math.floor height * 0.25}pt Arial"
          ctx.textAlign = @config.step_count.text.align
          ctx.fillStyle = @config.step_count.text.color

          x = (width  * 0.5) + (width  * step.col)
          y = (height * 0.5) + (height * step.row)

          ctx.beginPath()
          ctx.fillText i, x, y

    module.exports = PngStepCount


PngRenderer = require './png_renderer'

    class PngGround extends PngRenderer

      do_render: (ctx, path) =>
        cell_width  = @config.width
        cell_height = @config.height

        ground_segments = @config.ground.segments
        segment_width   = 1 / ground_segments
        ground_width    = cell_width * segment_width

        fill_segment = (block, segment, height, color) =>
          right  = segment * (cell_width * segment_width)
          left   = (cell_width * block.x) + right
          top    = (cell_height * (block.y + 1)) - height

          ctx.fillStyle = color

          ctx.beginPath()
          ctx.rect left, top, ground_width, height
          ctx.fill()

        for step in path
          for g, i in step.ground
            fill_segment step, i, g.thickness * @config.multiplier, @config.ground.fill_color

    module.exports = PngGround

Update floor thickness rendering to make more sense visually

PngRenderer = require './png_renderer'

    class PngGround extends PngRenderer

      do_render: (ctx, path) =>
        cell_width  = @config.width
        cell_height = @config.height

        ground_segments = @config.ground.segments
        segment_width   = 1 / ground_segments
        ground_width    = cell_width * segment_width

        fill_segment = (block, segment, height, color) =>
          right  = segment * (cell_width * segment_width)
          left   = (cell_width * block.x) + right
          top    = (cell_height * (block.y + 1)) - height

          ctx.fillStyle = color

          ctx.beginPath()
          ctx.rect left, top, ground_width, height
          ctx.fill()

        for step in path
          for g, i in step.ground
            fill_segment step, i, (g.thickness * @config.multiplier) * 0.5, @config.ground.fill_color

    module.exports = PngGround


Day Schedule Table Manager
==========================

We first set everything in a callback, so that it is invoked only
after the loading of the DOM.

    $ ->

Input
-----

We are interested in two key nuggets of input information: the full day
schedule, and the current item within it.

        inputStream = Bacon.combineTemplate
            schedule: Data.daySchedule
            current: CurrentDSItem

Table Management
----------------

We use D3 to set the data within the table.

        nodeHTML = (entry) ->
            dateString = FormatTime(FromUNIX(entry.start), false)
            "#{dateString}
             #{entry.displayName}	"

        inputStream.onValue (state) ->
            tableNodes = d3.select('#day-sched-table')
                           .selectAll('tr')
                           .data(state.schedule)
                           .html(nodeHTML)
                           .classed 'info', (entry) ->
                               entry.key == state.current?.key

            tableNodes.enter()
                      .append('tr')
                      .html(nodeHTML)
                      .classed 'info', (entry) ->
                          entry.key == state.current?.key

            tableNodes.exit()
                      .remove()

Convert the day sched display to use the new helper

Day Schedule Table Manager
==========================

We first set everything in a callback, so that it is invoked only
after the loading of the DOM.

    $ ->

Input
-----

We are interested in two key nuggets of input information: the full day
schedule, and the current item within it.

        inputStream = Bacon.combineTemplate
            schedule: Data.daySchedule
            current: CurrentDSItem

Table Management
----------------

We use D3 to set the data within the table.

        nodeHTML = (entry) ->
            dateString = FormatTime(FromUNIX(entry.start), false)
            "#{dateString}
             #{entry.displayName}	"

        inputStream.onValue (state) ->
            DisplayInformation '#day-sched-table',
                               state.schedule,
                               'tr', nodeHTML,
                               (nodes) ->
                                   nodes.classed 'info', (entry) ->
                                       entry.key == state.current?.key


Literal CoffeeScript
====================

This is a spec using written in Literal CoffeeScript

    describe 'Coffee.litcoffee', ->

        it 'should pass', ->
            expect(1+2).toEqual(3)

Add support for tests written in Literal CoffeeScript

Literate CoffeeScript
====================

This is a spec using written in Literate CoffeeScript

    describe 'Coffee.litcoffee', ->

        it 'should pass', ->
            expect(1+2).toEqual(3)


Clock Manager
=============

This manager controls the clock display on the *default* layout.

We first set everything in a callback, so that it is invoked only
after the loading of the DOM.

    $ ->

Time Formatting
---------------

Time must be consistently formatted in the output. We display the
time with a 24-hour clock.

Time is formatted by calling the *formatTime* function, which takes
a JavaScript *Date* object as the argument.

Before defining *formatTime*, we first define *formatDoubleDigit*,
which takes as its first argument an integer in the range 0 -- 99
inclusive, and returns a two character wide string representing it,
with a given *padding* character for left-padding given in the
second argument, which defaults to '0'.

We cast to a string in either case by exploiting JavaScript's weak
typing - concatenating a string and an integer yields an string.

        formatDoubleDigit = (value, padding = '0') ->
            return padding + value if value < 10
            '' + value

We now define *formatTime* utilising *formatDoubleDigit*.

        formatTime = (date) ->
            return formatDoubleDigit(date.getHours()) +
                   ':' +
                   formatDoubleDigit(date.getMinutes()) +
                   ':' +
                   formatDoubleDigit(date.getSeconds())

Clock Pulse Updates
-------------------

We bind to the *Clock* property, which gives the date.

        Clock.map(formatTime)
             .assign $('#clock'), 'text'

Clean up the formatting for times a bit

Clock Manager
=============

This manager controls the clock display on the *default* layout.

We first set everything in a callback, so that it is invoked only
after the loading of the DOM.

    $ ->

Time Formatting
---------------

Time must be consistently formatted in the output. We display the
time with a 24-hour clock.

Time is formatted by calling the *formatTime* function, which takes
a JavaScript *Date* object as the argument.

Before defining *formatTime*, we first define *formatDoubleDigit*,
which takes as its first argument an integer in the range 0 -- 99
inclusive, and returns a two character wide string representing it,
with a given *padding* character for left-padding given in the
second argument, which defaults to '0'.

        formatDoubleDigit = (value, padding = '0') ->
            if value < 10
                "#{padding}#{value}"
            else
                "#{value}"

We now define *formatTime* utilising *formatDoubleDigit*.

        formatTime = (date) ->
            return formatDoubleDigit(date.getHours()) +
                   ':' +
                   formatDoubleDigit(date.getMinutes()) +
                   ':' +
                   formatDoubleDigit(date.getSeconds())

Clock Pulse Updates
-------------------

We bind to the *Clock* property, which gives the date.

        Clock.map(formatTime)
             .assign $('#clock'), 'text'


PngRenderer = require './png_renderer'

    class PngStepCount extends PngRenderer

      do_render: (path) =>
        ctx    = PngRenderer.ctx
        width  = @config.width
        height = @config.height

        for step, i in path
          ctx.font      = @config.step_count.text_font height
          ctx.textAlign = @config.step_count.text_align
          ctx.fillStyle = @config.step_count.text_color

          x = (width  * 0.5) + (width  * step.col)
          y = (height * 0.5) + (height * step.row)

          ctx.beginPath()
          ctx.fillText i, x, y

    module.exports = PngStepCount

Move font config into renderer

PngRenderer = require './png_renderer'

    class PngStepCount extends PngRenderer

      do_render: (path) =>
        ctx    = PngRenderer.ctx
        width  = @config.width
        height = @config.height

        for step, i in path
          ctx.font      = "#{Math.floor height * 0.25}pt Arial"
          ctx.textAlign = @config.step_count.text_align
          ctx.fillStyle = @config.step_count.text_color

          x = (width  * 0.5) + (width  * step.col)
          y = (height * 0.5) + (height * step.row)

          ctx.beginPath()
          ctx.fillText i, x, y

    module.exports = PngStepCount


Clock Source
============

This clock source acts as a synchronisation point for anything which depends
on the time.

First, we create the Bacon.js bus. This event source is the destination to
which we push each clock pulse.

    bus = new Bacon.Bus

The *sendClockPulse* function actually injects the clock pulse into
the bus. It has two functions: not only does it get the current
date and inject it into the bus, but it also schedules itself to
be re-run in one second's time.

    sendClockPulse = ->
        bus.push new Date
        setTimeout sendClockPulse, 1000

We immediately start the clock.

    do sendClockPulse

In the future, it would be a wise plan to try to synchronise the
clock pulses, as close as possible, to the actual tick of the system
RTC. This would mean that, assuming the screens are synchronised
over NTP, they all pulse at the same time.

Finally, we create the public API to the system: a property,
initialised with the current date.

    window.Clock = bus.toProperty new Date

Add conversion functions to and from UNIX timestamps

Clock Source
============

This clock source acts as a synchronisation point for anything which depends
on the time.

First, we create the Bacon.js bus. This event source is the destination to
which we push each clock pulse.

    bus = new Bacon.Bus

The *sendClockPulse* function actually injects the clock pulse into
the bus. It has two functions: not only does it get the current
date and inject it into the bus, but it also schedules itself to
be re-run in one second's time.

    sendClockPulse = ->
        bus.push new Date
        setTimeout sendClockPulse, 1000

We immediately start the clock.

    do sendClockPulse

In the future, it would be a wise plan to try to synchronise the
clock pulses, as close as possible, to the actual tick of the system
RTC. This would mean that, assuming the screens are synchronised
over NTP, they all pulse at the same time.

Finally, we create the public API to the system: a property,
initialised with the current date.

    window.Clock = bus.toProperty new Date

Day Base
--------

The *day base* is the UNIX timestamp of midnight, today. It can be accessed via
the *DayBase* function.

In its implementation, we first get the current time as a JavaScript *Date*
object.

    window.DayBase = ->
        rightNow = new Date

We then, since the *Date* object is mutable, set its time fields to zero.

        rightNow.setHours 0
        rightNow.setMinutes 0
        rightNow.setSeconds 0
        rightNow.setMilliseconds 0

Finally, we convert to a UNIX timestamp, and divide by 1000 since the JavaScript
API is generally oriented around milliseconds rather than seconds.

        rightNow.valueOf() / 1000

As a utility function, we also provide *FromUNIX*, which converts between UNIX
timestamps and JavaScript *Date* objects.

Its implementation is trivial.

    window.FromUNIX = (unixTimestamp) -> new Date(1000 * unixTimestamp)


`#! /usr/bin/env node
    `

    Config = require './config'

    new Config()
      .initialize()
      .then (config) ->
        Skyll = require './skyll'
        next  = 0

        render = (level) ->
          new Skyll()
            .initialize starting_row: Math.floor config.grid.length / 2
            .then (mod) -> mod.craft level
            .then -> render config.levels[next] if ++next < config.levels.length

        render config.levels[next]

Refactor so that we only create one instance of Skyll

`#! /usr/bin/env node
    `

    Config = require './config'

    new Config()
      .initialize()
      .then (config) ->
        Skyll = require './skyll'
        next  = 0

        new Skyll()
          .initialize starting_row: Math.floor config.grid.length / 2
          .then (mod) ->
            render = (level) ->
              mod.craft level
                .then ->
                  console.log 'finished rendering level', next
                  render config.levels[next] if ++next < config.levels.length

            render config.levels[next]


Literate CoffeeScript Test
--------------------------

comment comment

    test "basic literate CoffeeScript parsing", ->
      ok yes

now with a...

    test "broken up indentation", ->

... broken up ...

      do ->

... nested block.

        ok yes

Code in `backticks is not parsed` and...

    test "comments in indented blocks work", ->
      do ->
        do ->
          # Regular comment.

          ###
            Block comment.
          ###

          ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

  * I

  * Am

  * A

  * List

Tabs work too:

	test "tabbed code", ->
		ok yes

Test case for literal mode: Code blocks must be preceded by blank line.

Literate CoffeeScript Test
--------------------------

comment comment

    test "basic literate CoffeeScript parsing", ->
      ok yes

now with a...

    test "broken up indentation", ->

... broken up ...

      do ->

... nested block.

        ok yes

Code must be separated from text by a blank line.

    test "code blocks must be preceded by a blank line", ->

The next line is part of the text and will not be executed.
      fail()

      ok yes

Code in `backticks is not parsed` and...

    test "comments in indented blocks work", ->
      do ->
        do ->
          # Regular comment.

          ###
            Block comment.
          ###

          ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

  * I

  * Am

  * A

  * List

Tabs work too:

	test "tabbed code", ->
		ok yes


# Using This Reference

The API Reference contains specific documentation for individual components of
the framework. In most sections you'll also find tests included, which serve as
an example for correct usage as well as an assertion that the API has not
changed.

Within the reference, types are documented by suffixing with a colon followed
by the type (e.g. `toString() : string`). This applies to function arguments,
return types, property types and accessor types. If there is no type specified,
it's either self-evident or the type is undefined (e.g. in the case of a
function with no return value).  All function/property/accessor headings are
prefixed with an `@` if they exist at the class level, otherwise they exist at
the prototype/instance level.


# Batman.config

`Batman.config` is a namespace for global batman.js settings. The affect all apps running on the page. Set these values before defining your app. For example:

```coffeescript
Batman.config.pathToHTML = '/templates'
Batman.config.usePushState = false

class MyApp extends Batman.App
  # ...
```

## @pathToApp[="/"]

Use this if your batman.js app is loaded from a path other than `/`. For example, if you load your app at `/app`, you'd use:

```coffeescript
  Batman.config.pathToApp = '/app'
```

Any generated routes will be then be prefixed with `/app`.

## @pathToHTML[="/html"`]

If the app hasn't already loaded the HTML to render a view, it will request the HTML with an AJAX request. `pathToHTML` is used as the prefix for these requests.

The `Batman.rails` extra sets `Batman.config.pathToHTML= '/assets/batman/html'`.

## @fetchRemoteHTML[=true]

batman.js automatically fetches a view's HTML if it hasn't been loaded yet. If `fetchRemoteHTML` is false, an error will be thrown instead.

## @usePushState[=true]

Set to `false` to use batman.js's hashbang navigator instead of the (default) `pushState` navigator. Note: the `pushState` navigator automatically degrades to the hashbang navigator if not supported by the browser.

## @protectFromCSRF[=false]

Used by `Batman.rails`. If `protectFromCSRF` is true, batman.js sends CSRF token as a request header (`X-CSRF-Token`). batman.js uses `metaNameForCSRFToken` to find the correct meta tag.

The `Batman.rails` extra sets `Batman.config.protectFromCSRF = true`, but you must set it yourself if you're using Rails-style CSRF tokens _without_ the `Batman.rails` extra.

## @metaNameForCSRFToken[="csrf-token"]

If `protectFromCSRF` is true, the contents of the meta tag with this name will be used as the CSRF token. This default is set in the `Batman.rails` extra.

## @cacheViews[=false]

If set to true, batman.js will cache `Batman.View` instances between `render` calls. View caching is opt-in [while its implementation is finalized](https://github.com/batmanjs/batman/issues/805).


/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * The origin of this IDL file is
 * https://dom.spec.whatwg.org/#interface=domimplementation
 *
 * Copyright:
 * To the extent possible under law, the editors have waived all copyright and
 * related or neighboring rights to this work.
 */

// [Exposed=Window]
interface DOMImplementation {
  [NewObject, Throws]
  DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId,
                                  DOMString systemId);
  [NewObject, Throws]
  XMLDocument createDocument(DOMString? namespace,
                             [TreatNullAs=EmptyString] DOMString qualifiedName,
                             optional DocumentType? doctype = null);
  [NewObject]
  Document createHTMLDocument(optional DOMString title);

  [Pure]
  boolean hasFeature(); // useless, always return true
};

Fix broken WebIDL spec link

/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * The origin of this IDL file is
 * https://dom.spec.whatwg.org/#interface-domimplementation
 *
 * Copyright:
 * To the extent possible under law, the editors have waived all copyright and
 * related or neighboring rights to this work.
 */

// [Exposed=Window]
interface DOMImplementation {
  [NewObject, Throws]
  DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId,
                                  DOMString systemId);
  [NewObject, Throws]
  XMLDocument createDocument(DOMString? namespace,
                             [TreatNullAs=EmptyString] DOMString qualifiedName,
                             optional DocumentType? doctype = null);
  [NewObject]
  Document createHTMLDocument(optional DOMString title);

  [Pure]
  boolean hasFeature(); // useless, always return true
};


/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */

interface ClientRect;

interface ClientRectList {
  readonly attribute unsigned long length;
  getter ClientRect? item(unsigned long index);
};

/* Helpers

unsafe fn unwrap<T>(obj: *JSObject) -> *rust_box<T> {
  let val = JS_GetReservedSlot(obj, 0);
  cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val))
}

trait ToJsval {
  fn to_jsval(cx: *JSContext) -> jsval;
}

impl Option : ToJsval {
  fn to_jsval(cx: *JSContext) -> jsval {
    match self {
      Some(v) => v.to_jsval(),
      None => JSVAL_NULL
    }
  }
}

 */

/*

trait ClientRectList {
  fn getLength() -> u32;
  fn getItem(u32 index) -> Option<@ClientRect>;
}

mod ClientRectList {
mod bindings {

fn getLength(cx: *JSContext, argc: c_uint, argv: *jsval) -> JSBool unsafe {
  let obj = JS_THIS_OBJECT(cx, unsafe::reinterpret_cast(&vp));
  if obj.is_null() {
    return 0;
  }

  let conrete = unwrap<ClientRectList>(obj);
  let rval = (*concrete).getLength();

  JS_SET_RVAL(argv, rval);
  return 1;
}

fn getItem(cx: *JSContext, argc: c_uint, vp: *jsval) -> JSBool unsafe {
  let obj = JS_THIS_OBJECT(cx, unsafe::reinterpret_cast(&vp));
  if obj.is_null() {
    return 0;
  }

  let raw_arg1 = if argc < 1 {
    //XXX convert null
  } else {
    JS_ARGV(vp, 0);
  };

  let arg1 = if !RUST_JSVAL_IS_INT(raw_arg1) {
    //XXX convert to int
  } else {
    RUST_JSVAL_TO_INT(raw_arg1);
  } as u32;

  let conrete = unwrap<ClientRectList>(obj);
  let rval = (*concrete).getItem(arg1);

  JS_SET_RVAL(vp, rval.to_jsval())
  return 1;
}
}

*/

Fix forward declaration in webIDL that breaks the build.

/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */

interface ClientRectList {
  readonly attribute unsigned long length;
  getter ClientRect? item(unsigned long index);
};


/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * The origin of this IDL file is
 * https://html.spec.whatwg.org/multipage/#webstorage
 *
 */

interface Storage {

  readonly attribute unsigned long length;

  DOMString? key(unsigned long index);

  getter DOMString? getItem(DOMString name);

  setter creator void setItem(DOMString name, DOMString value);

  deleter void removeItem(DOMString name);

  void clear();
};

Update spec link to point to exact section

/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * The origin of this IDL file is
 * https://html.spec.whatwg.org/multipage/#the-storage-interface
 *
 */

interface Storage {

  readonly attribute unsigned long length;

  DOMString? key(unsigned long index);

  getter DOMString? getItem(DOMString name);

  setter creator void setItem(DOMString name, DOMString value);

  deleter void removeItem(DOMString name);

  void clear();
};


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * The origin of this IDL file is
 * https://dom.spec.whatwg.org/#interface-parentnode
 */

[NoInterfaceObject]
interface ParentNode {
  [SameObject]
  readonly attribute HTMLCollection children;
  [Pure]
  readonly attribute Element? firstElementChild;
  [Pure]
  readonly attribute Element? lastElementChild;
  [Pure]
  readonly attribute unsigned long childElementCount;

  [Throws]
  void prepend((Node or DOMString)... nodes);
  [Throws]
  void append((Node or DOMString)... nodes);

  //Element? query(DOMString relativeSelectors);
  //[NewObject]
  //Elements queryAll(DOMString relativeSelectors);
  [Pure, Throws]
  Element? querySelector(DOMString selectors);
  [NewObject, Throws]
  NodeList querySelectorAll(DOMString selectors);
};

Remove some commented-out methods that were removed from spec

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * The origin of this IDL file is
 * https://dom.spec.whatwg.org/#interface-parentnode
 */

[NoInterfaceObject]
interface ParentNode {
  [SameObject]
  readonly attribute HTMLCollection children;
  [Pure]
  readonly attribute Element? firstElementChild;
  [Pure]
  readonly attribute Element? lastElementChild;
  [Pure]
  readonly attribute unsigned long childElementCount;

  [Throws]
  void prepend((Node or DOMString)... nodes);
  [Throws]
  void append((Node or DOMString)... nodes);

  [Pure, Throws]
  Element? querySelector(DOMString selectors);
  [NewObject, Throws]
  NodeList querySelectorAll(DOMString selectors);
};


/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://html.spec.whatwg.org/multipage/#htmlfieldsetelement
interface HTMLFieldSetElement : HTMLElement {
           attribute boolean disabled;
  readonly attribute HTMLFormElement? form;
  //         attribute DOMString name;

  //readonly attribute DOMString type;

  //readonly attribute HTMLFormControlsCollection elements;
  readonly attribute HTMLCollection elements;

  //readonly attribute boolean willValidate;
  readonly attribute ValidityState validity;
  //readonly attribute DOMString validationMessage;
  //boolean checkValidity();
  //boolean reportValidity();
  //void setCustomValidity(DOMString error);
};

Add tracking issue for HTMLFormControlsCollection

/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://html.spec.whatwg.org/multipage/#htmlfieldsetelement
interface HTMLFieldSetElement : HTMLElement {
           attribute boolean disabled;
  readonly attribute HTMLFormElement? form;
  //         attribute DOMString name;

  //readonly attribute DOMString type;

  //HTMLFormControlsCollection: https://github.com/servo/servo/issues/8566
  //readonly attribute HTMLFormControlsCollection elements;

  readonly attribute HTMLCollection elements;

  //readonly attribute boolean willValidate;
  readonly attribute ValidityState validity;
  //readonly attribute DOMString validationMessage;
  //boolean checkValidity();
  //boolean reportValidity();
  //void setCustomValidity(DOMString error);
};


[Constructor(MutationCallback callback),
 Exposed=Window]
interface MutationObserver {
  void observe(Node target, optional MutationObserverInit options);
  void disconnect();
  sequence<MutationRecord> takeRecords();
};

callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);

dictionary MutationObserverInit {
  boolean childList = false;
  boolean attributes;
  boolean characterData;
  boolean subtree = false;
  boolean attributeOldValue;
  boolean characterDataOldValue;
  sequence<DOMString> attributeFilter;
};


component extends="wheels.Test" {

	/*
	 * Executes once before the test suite runs.
	 * Populates the test database on reload or if there are no tables in the database.
	 */
	function beforeAll() {
		local.tables = $dbinfo(datasource=application.wheels.dataSourceName, type="tables");
		if ((StructKeyExists(url, "reload") && url.reload == true) || !local.tables.recordCount) {
			include "populate.cfm";
		}
	}

	/*
	 * Executes before every test case if called from the package via super.superSetup().
	 */
	function setup() {

	}

	/*
	 * Executes after every test case if called from the package via super.superTeardown().
	 */
	function teardown() {

	}

	/*
	 * Executes once after the test suite runs.
	 */
	function afterAll() {

	}

}

Check for authors table instead of just any table to check whether to populate the test database.

component extends="wheels.Test" {

	/*
	 * Executes once before the test suite runs.
	 * Populates the test database on reload or if the authors table does not exist.
	 */
	function beforeAll() {
		local.tables = $dbinfo(datasource=application.wheels.dataSourceName, type="tables");
		local.tableList = ValueList(local.tables.table_name);
		if ((StructKeyExists(url, "reload") && url.reload == true) || !FindNoCase("authors", local.tableList)) {
			include "populate.cfm";
		}
	}

	/*
	 * Executes before every test case if called from the package via super.superSetup().
	 */
	function setup() {

	}

	/*
	 * Executes after every test case if called from the package via super.superTeardown().
	 */
	function teardown() {

	}

	/*
	 * Executes once after the test suite runs.
	 */
	function afterAll() {

	}

}


component extends="wheels.Test" {

	/*
	 * Executes once before the test suite runs.
	 * Populates the test database on reload or if the authors table does not exist.
	 */
	function beforeAll() {
		local.tables = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables");
		local.tableList = ValueList(local.tables.table_name);
		local.populate = StructKeyExists(url, "populate") ? url.populate : true;
		if (local.populate || !FindNoCase("authors", local.tableList)) {
			include "populate.cfm";
		}
	}

	/*
	 * Executes before every test case if called from the package via super.superSetup().
	 */
	function setup() {
	}

	/*
	 * Executes after every test case if called from the package via super.superTeardown().
	 */
	function teardown() {
	}

	/*
	 * Executes once after the test suite runs.
	 */
	function afterAll() {
	}

}

Reset application scope after core tests

component extends="wheels.Test" {

	/*
	 * Executes once before the test suite runs.
	 * Populates the test database on reload or if the authors table does not exist.
	 */
	function beforeAll() {
		application.$$$wheels = duplicate(application.wheels);
		local.tables = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables");
		local.tableList = ValueList(local.tables.table_name);
		local.populate = StructKeyExists(url, "populate") ? url.populate : true;
		if (local.populate || !FindNoCase("authors", local.tableList)) {
			include "populate.cfm";
		}
	}

	/*
	 * Executes before every test case if called from the package via super.superSetup().
	 */
	function setup() {
	}

	/*
	 * Executes after every test case if called from the package via super.superTeardown().
	 */
	function teardown() {
	}

	/*
	 * Executes once after the test suite runs.
	 */
	function afterAll() {
		application.wheels = application.$$$wheels;
		structDelete(application, "$$$wheels");
	}

}


component extends="testbox.system.BaseSpec" {
	function run(testResults, testBox) {
		dataDir = ExpandPath("../data/en");
		files = directoryList(dataDir, false, "array");
		filesWithErrors = [];

		describe("JSON Format Tests", function() {
			it("should be valid isJSON==true", function() {
				for (filePath in files) {
					var json = fileRead(filePath);
					var isItJson = isJSON(json);
					if (!isItJSON) {
						filesWithErrors.append(getFileFromPath(filePath));
					}
					expect(isItJSON).toBeTrue();

				}

			});

			it("should have key: name, type", function() {
				for (filePath in files) {
					var json = fileRead(filePath);
					var isItJson = isJSON(json);
					var fileName = getFileFromPath(filePath);
					if (isItJSON && fileName != "index.json") {
						json = deserializeJSON(json);
						if (!json.keyExists("name") || !json.keyExists("type")) {
							filesWithErrors.append(fileName);
							debug(json, "Missing key name or type in #fileName#");
						}
						expect(json).toHaveKey("name");
						expect(json).toHaveKey("type");
					}

				}
				debug(arguments,"inside it args");
			});

		});

		describe("There should be no errors", function(){
			it("filesWithErrors should be empty", function() {
				expect(filesWithErrors).toBe([]);
			});
		});

	}
}

Add custom messages to test expects

component extends="testbox.system.BaseSpec" {
	function run(testResults, testBox) {
		dataDir = ExpandPath("../data/en");
		files = directoryList(dataDir, false, "array");

		describe("JSON Format Tests", function() {
			it("should be valid isJSON==true", function() {
				for (filePath in files) {
					var json = fileRead(filePath);
					var isItJson = isJSON(json);
					var fileName = getFileFromPath(filePath);
					expect(isItJSON).toBeTrue("#fileName# was NOT valid JSON");

				}

			});

			it("should have key: name, type", function() {
				for (filePath in files) {
					var json = fileRead(filePath);
					var isItJson = isJSON(json);
					var fileName = getFileFromPath(filePath);
					if (isItJSON && fileName != "index.json") {
						json = deserializeJSON(json);
						expect(json).toHaveKey("name", "#fileName# did not have key: name");
						expect(json).toHaveKey("type", "#fileName# did not have key: name");
					}

				}
			});

		});

	}
}


<cfcomponent output="false">
	<cfscript>
		this.name = "cfstatictests_" & hash(GetCurrenttemplatepath());

		root = GetDirectoryFromPath(GetCurrentTemplatePath());

		this.mappings['/mxunit']        = '#root#../mxunit';
		this.mappings['/org/cfstatic']  = '#root#../org/cfstatic';
		this.mappings['/tests']         = root;
	</cfscript>
</cfcomponent>

Fix for OpenBD ExpandPath() issue with trailing slashes in mappings

<cfcomponent output="false">
	<cfscript>
		this.name = "cfstatictests_" & hash(GetCurrenttemplatepath());

		root = GetDirectoryFromPath(GetCurrentTemplatePath());

		this.mappings['/mxunit']        = '#root#../mxunit';
		this.mappings['/org/cfstatic']  = '#root#../org/cfstatic';
		this.mappings['/tests']         = Left(root, Len(root)-1); // remove trailing slash - breaks openBDs ExpandPath() method...
	</cfscript>
</cfcomponent>


component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
			apiKey = '', // Required
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendgrid@sendgridcfc" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
	}

}

Use directory mapping to add helpers to Wirebox

component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
			apiKey = '', // Required
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendgrid@sendgridcfc" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
    binder.mapDirectory(
      packagePath = "#moduleMapping#/helpers",
      namespace = "@sendgridcfc"
    );
	}

}


component  implements="system.interfaces.com.irCMS.cms.cmsCore" {
    public cmsCore function init(required string tablePrefix, required string datasource) {
        variables.datasource  = arguments.datasource;
        variables.tablePrefix = arguments.tablePrefix;

        return this;
    }

    public string function renderTemplate(required string themeName) {

    }

    public query function getThemes() {
        try {
            return new Query().setDatasource(variables.datasource)
                              .setSQL("SELECT themeId, themeName FROM #variables.tablePrefix#_theme WHERE active=:active")
                              .addParam(name="active", value=true, cfsqltype="cf_sql_bit")
                              .execute()
                              .getResult();
        }
        catch(any e) {
            return queryNew('themeName', 'varchar').addRow(1)
                                                   .setCell('themeName', 'irBootstrap');
        }
    }
}

Throw an error when no theme is installed.

component  implements="system.interfaces.com.irCMS.cms.cmsCore" {
    public cmsCore function init(required string tablePrefix, required string datasource) {
        variables.datasource  = arguments.datasource;
        variables.tablePrefix = arguments.tablePrefix;

        return this;
    }

    public string function renderTemplate(required string themeName) {

    }

    public query function getThemes() {
        try {
            return new Query().setDatasource(variables.datasource)
                              .setSQL("SELECT themeId, themeName FROM #variables.tablePrefix#_theme WHERE active=:active")
                              .addParam(name="active", value=true, cfsqltype="cf_sql_bit")
                              .execute()
                              .getResult();
        }
        catch(any e) {
            variables.errorHandler.processError(themeName='irBootstrap', message=e.message, detail=e.detail);
            abort;
        }
    }
}


component {
	function onRequestStart(){
		location(url="../index.cfm/admin:main", addtoken="false");
	}
}

Add `this.name` to the admin application.

component {

	// ------------------------ APPLICATION SETTINGS ------------------------ //

	this.applicationroot = getDirectoryFromPath(getCurrentTemplatePath());
	this.name = ListLast(this.applicationroot, "\/") & "_" & Hash(this.applicationroot);

	// ------------------------ CALLED WHEN PAGE REQUEST STARTS ------------------------ //

	function onRequestStart(){
		location(url="../index.cfm/admin:main", addtoken="false");
	}
}


component {

	function get(required string class) {
		return createObject("java", arguments.class, [expandpath("/cashbox/lib/vindicia_NOAXIS.jar")]);
	}

	function getClassVersion() {
		if (isnull(variables.classVersion)) {
			var ClientConstants = get("com.vindicia.client.ClientConstants");
			variables.classVersion = "v#replace(ClientConstants.getVersion(),'.','_','all')#";
		}

		return variables.classVersion;
	}

}

Use standard Java client instead of NOAXIS

component {

	function get(required string class) {
		return createObject("java", arguments.class, [expandpath("/cashbox/lib/vindicia.jar")]);
	}

	function getClassVersion() {
		if (isnull(variables.classVersion)) {
			var ClientConstants = get("com.vindicia.client.ClientConstants");
			variables.classVersion = "v#replace(ClientConstants.getVersion(),'.','_','all')#";
		}

		return variables.classVersion;
	}

}


component {

    this.name = "ColdBoxTestingSuite" & hash(getCurrentTemplatePath());
    this.sessionManagement  = true;
    this.setClientCookies   = true;
    this.sessionTimeout     = createTimeSpan( 0, 0, 15, 0 );
    this.applicationTimeout = createTimeSpan( 0, 0, 15, 0 );

    testsPath = getDirectoryFromPath( getCurrentTemplatePath() );
    this.mappings[ "/tests" ] = testsPath;
    rootPath = REReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" );
    this.mappings[ "/root" ] = rootPath;
    this.mappings[ "/testingModuleRoot" ] = expandPath( rootPath & "../" );
    this.mappings[ "/app" ] = testsPath & "resources/app";
    this.mappings[ "/coldbox" ] = testsPath & "resources/app/coldbox";
    this.mappings[ "/testbox" ] = rootPath & "/testbox";
}

Fix mapping on Adobe CF

component {

    this.name = "ColdBoxTestingSuite" & hash(getCurrentTemplatePath());
    this.sessionManagement  = true;
    this.setClientCookies   = true;
    this.sessionTimeout     = createTimeSpan( 0, 0, 15, 0 );
    this.applicationTimeout = createTimeSpan( 0, 0, 15, 0 );

    testsPath = getDirectoryFromPath( getCurrentTemplatePath() );
    this.mappings[ "/tests" ] = testsPath;
    rootPath = REReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" );
    this.mappings[ "/root" ] = rootPath;
    this.mappings[ "/testingModuleRoot" ] = listDeleteAt( rootPath, listLen( rootPath, '\/' ), "\/" );
    this.mappings[ "/app" ] = testsPath & "resources/app";
    this.mappings[ "/coldbox" ] = testsPath & "resources/app/coldbox";
    this.mappings[ "/testbox" ] = rootPath & "/testbox";
}


component extends="coldbox.system.Coldbox" output="false"{
	setting enablecfoutputonly="yes";
	this.name = hash(getCurrentTemplatePath());
	this.sessionManagement = true;
	this.sessionTimeout = createTimeSpan(0,0,30,0);
	this.setClientCookies = true;

	COLDBOX_APP_ROOT_PATH = getDirectoryFromPath(getCurrentTemplatePath());
	COLDBOX_APP_MAPPING   = "";
	COLDBOX_CONFIG_FILE   = "";
	COLDBOX_APP_KEY       = "";

	this.ormEnabled = true;
	this.datasource = "botfly";
	this.ormSettings 	= {
		flushAtRequestEnd		= false,
		automanageSession		= false,
		autogenmap				= true,
		eventHandling			= true,
		logSQL					= true,
		secondaryCacheEnabled	= true,
		useDBForMapping			= false,
		savemapping				= false,
		skipCFCWithError		= true,
		cacheprovider			= "ehCache",
		dbcreate				= "none",
		eventHandler			= "botfly.model.EventHandler",
		cfclocation				= "model"
	};

	public boolean function onRequestStart(required string targetPage){

		if(findNoCase('index.cfm', listLast(arguments.targetPage, '/'))){
			reloadChecks();
			processColdBoxRequest();
		}

		return true;
	}

}

Replace {botfly} with correct datasource

component extends="coldbox.system.Coldbox" output="false"{
	setting enablecfoutputonly="yes";
	this.name = hash(getCurrentTemplatePath());
	this.sessionManagement = true;
	this.sessionTimeout = createTimeSpan(0,0,30,0);
	this.setClientCookies = true;

	COLDBOX_APP_ROOT_PATH = getDirectoryFromPath(getCurrentTemplatePath());
	COLDBOX_APP_MAPPING   = "";
	COLDBOX_CONFIG_FILE   = "";
	COLDBOX_APP_KEY       = "";

	this.ormEnabled = true;
	this.datasource = "{botfly}";
	this.ormSettings 	= {
		flushAtRequestEnd		= false,
		automanageSession		= false,
		autogenmap				= true,
		eventHandling			= true,
		logSQL					= true,
		secondaryCacheEnabled	= true,
		useDBForMapping			= false,
		savemapping				= false,
		skipCFCWithError		= true,
		cacheprovider			= "ehCache",
		dbcreate				= "none",
		eventHandler			= "botfly.model.EventHandler",
		cfclocation				= "model"
	};

	public boolean function onRequestStart(required string targetPage){

		if(findNoCase('index.cfm', listLast(arguments.targetPage, '/'))){
			reloadChecks();
			processColdBoxRequest();
		}

		return true;
	}

}


component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
			apiKey = '', // Required
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendgrid@sendgridcfc" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
    binder.mapDirectory(
      packagePath = "#moduleMapping#/helpers",
      namespace = "@sendgridcfc"
    );
	}

}

Remove leading slash for modulemapping

component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
			apiKey = '', // Required
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendgrid@sendgridcfc" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
    binder.mapDirectory(
      packagePath = "#REreplace( moduleMapping, '^/', '' )#/helpers",
      namespace = "@sendgridcfc"
    );
	}

}


component extends="org.lucee.cfml.test.LuceeTestCase"{
	function run( testResults , testBox ) {
		describe( title="Test suite for LDEV-1467", body=function() {
			it(title="Checking REMatch() is not support with line breaks ", body = function( currentSpec ) {
				REMatchTest = ArrayLen( REMatch( '.', Chr( 10 ) ) );
				expect(REMatchTest).toBe(1);
			});
			it(title="Checking REFind() is support with line breaks", body = function( currentSpec ) {
				REFindTest = REFind( '.',Chr( 10 ) ) ;
				expect(REFindTest).toBe(1);
			});
		});
	}
}

Rename the test suite iterations

component extends="org.lucee.cfml.test.LuceeTestCase"{
	function run( testResults , testBox ) {
		describe( title="Test suite for LDEV-1467", body=function() {
			it(title="Checking REMatch() with line breaks ", body = function( currentSpec ) {
				REMatchTest = ArrayLen( REMatch( '.', Chr( 10 ) ) );
				expect(REMatchTest).toBe(1);
			});
			it(title="Checking REFind() with line breaks", body = function( currentSpec ) {
				REFindTest = REFind( '.',Chr( 10 ) ) ;
				expect(REFindTest).toBe(1);
			});
		});
	}
}


component displayName="Leap Tests" extends="testbox.system.BaseSpec" {

   // executes before all tests
   function beforeTests(){
      leapObj = createObject("leap");
   }

   function testLeapYear() {
      $assert.isTrue(leapObj.isLeapYear(1996));
   }

   function testNonLeapYear() {
      $assert.isFalse(leapObj.isLeapYear(1997));
   }

   function testNonLeapEvenYear() {
      $assert.isFalse(leapObj.isLeapYear(1998));
   }

   function testCentury() {
      $assert.isFalse(leapObj.isLeapYear(1900));
   }

   function testfourthCentury() {
      $assert.isTrue(leapObj.isLeapYear(2400));
   }

   function testY2K() {
      $assert.isTrue(leapObj.isLeapYear(2000));
   }

   // executes after all tests
   function afterTests(){}

}

Convert to BDD style tests

component extends="testbox.system.BaseSpec" {

	function beforeAll(){
	  leapObj = createObject( 'leap' );
	}

	function run(){

		describe( "My Leap year calculator", function(){

			it( "should detect a leap year", function(){
      			expect( leapObj.isLeapYear( 1996 ) ).toBeTrue();
			});

			it( "should detect a non-leap year", function(){
      			expect( leapObj.isLeapYear( 1997 ) ).toBeFalse();
			});

			it( "should detect a non-leap even year", function(){
      			expect( leapObj.isLeapYear( 1998 ) ).toBeFalse();
			});

			it( "should detect a century", function(){
      			expect( leapObj.isLeapYear( 1900 ) ).toBeFalse();
			});

			it( "should detect a fourth century", function(){
      			expect( leapObj.isLeapYear( 2400 ) ).toBeTrue();
			});

			it( "should detect Y2K", function(){
      			expect( leapObj.isLeapYear( 2000 ) ).toBeTrue();
			});

		});

	}

}


component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
			apiKey = '', // Required
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		parseParentSettings();
		var sendGridAPISettings = controller.getConfigSettings().sendGrid;

		binder.map( "sendGrid@sendGrid" )
			.to( "#moduleMapping#.sendgrid" )
			.initArg( name="apiKey", value=sendGridAPISettings.apiKey )
			.initArg( name="baseUrl", value=sendGridAPISettings.baseUrl )
			.initArg( name="forceTestMode", value=sendGridAPISettings.forceTestMode )
			.initArg( name="httpTimeout", value=sendGridAPISettings.httpTimeout )
			.initArg( name="includeRaw", value=sendGridAPISettings.includeRaw );
	}

	private function parseParentSettings(){
		var oConfig      = controller.getSetting( "ColdBoxConfig" );
		var configStruct = controller.getConfigSettings();
		var sendGridDSL  = oConfig.getPropertyMixin( "sendGrid", "variables", structnew() );

		//defaults
		configStruct.sendGrid = variables.settings;

		// incorporate settings
		structAppend( configStruct.sendGrid, sendGridDSL, true );
	}

}

Simplify onLoad(), eliminating need for private method

component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
			apiKey = '', // Required
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendGrid@sendGrid" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
	}

}


<cfcomponent>
  <cfset this.name = "idealcfcexample" />

  <cffunction name="onApplicationStart">
    <cfset application.ideal = createObject( "lib/cfc/ideal" ).init( config = expandPath( "./config/production.cfm" )) />
  </cffunction>

  <cffunction name="onRequestStart">
    <cfif not structKeyExists( application, "ideal" ) or
          (
            structKeyExists( url, "reload" ) and
            isBoolean( url.reload ) and
            url.reload
          )>
      <cfset onApplicationStart() />
      <cfset application.ideal.reload = true />
    </cfif>

    <cfset request.ideal = application.ideal />
  </cffunction>

  <cffunction name="onError">
    <cfargument name="error" />

    <cfdump var="#application#" />
    <cfdump var="#error#" />
  </cffunction>
</cfcomponent>

Use test config file instead of production

<cfcomponent>
  <cfset this.name = "idealcfcexample" />

  <cffunction name="onApplicationStart">
    <cfif structKeyExists( application, "ideal" )>
      <cfset structDelete( application, "ideal" ) />
    </cfif>

    <cfset application.ideal = createObject( "lib/cfc/ideal" ).init( config = expandPath( "./config/test.cfm" )) />
  </cffunction>

  <cffunction name="onRequestStart">
    <cfif not structKeyExists( application, "ideal" ) or
          (
            structKeyExists( url, "reload" ) and
            isBoolean( url.reload ) and
            url.reload
          )>
      <cfset onApplicationStart() />
      <cfset application.ideal.reload = true />
    </cfif>

    <cfset request.ideal = application.ideal />
  </cffunction>

  <cffunction name="onError">
    <cfargument name="error" />
    <cfoutput>

#error.message#

#error.detail#

    </cfoutput>
    <cfdump var="#error#" />
  </cffunction>
</cfcomponent>


component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
      apiKey = '', // Required
      emailValidationApiKey = '', // Optional
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = true // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendgrid@sendgridcfc" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        emailValidationApiKey = settings.emailValidationApiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
    binder.mapDirectory(
      packagePath = "#REreplace( moduleMapping, '^/', '' )#/helpers",
      namespace = "@sendgridcfc"
    );
	}

}

Update module config default to match component for include raw param

component {

	this.title = "SendGrid Web API v3";
	this.author = "Matthew J. Clemente";
	this.webURL = "https://github.com/mjclemente/sendgrid.cfc";
	this.description = "A wrapper for the SendGrid Web API v3";

	function configure(){
		settings = {
      apiKey = '', // Required
      emailValidationApiKey = '', // Optional
			baseUrl = 'https://api.sendgrid.com/v3', // Default value in init
			forceTestMode = false, // Default value in init
			httpTimeout = 60, // Default value in init
			includeRaw = false // Default value in init
		};
	}

	function onLoad(){
		binder.map( "sendgrid@sendgridcfc" )
			.to( "#moduleMapping#.sendgrid" )
      .asSingleton()
      .initWith(
        apiKey = settings.apiKey,
        emailValidationApiKey = settings.emailValidationApiKey,
        baseUrl = settings.baseUrl,
        forceTestMode = settings.forceTestMode,
        httpTimeout = settings.httpTimeout,
        includeRaw = settings.includeRaw
      );
    binder.mapDirectory(
      packagePath = "#REreplace( moduleMapping, '^/', '' )#/helpers",
      namespace = "@sendgridcfc"
    );
	}

}


component extends="Model" {

	function config() {
		hasMany(name="galleries");
		hasOne(name="combikey");
		hasOne(name="author", foreignKey="firstName", joinKey="firstName");
		hasMany(name="authors", foreignKey="firstName", joinKey="firstName");
		hasMany(name="combikeys");
		hasMany(name="outerjoinphotogalleries", modelName="gallery", jointype="outer");
		validatesPresenceOf("username, password, firstname, lastname");
		validatesUniquenessOf("username");
		validatesLengthOf(property="username", minimum="4", maximum="20", when="onCreate", message="Please shorten your [property] please. [maximum] characters is the maximum length allowed.");
		validatesLengthOf(property="password", minimum="4", when="onUpdate");
		validate("validateCalled");
		validateOnCreate("validateOnCreateCalled");
		validateOnUpdate("validateOnUpdateCalled");
		property(name="salesTotal", sql="SUM(birthDayMonth)", select=false, dataType="int");
	}

	function validateCalled() {
		this._validateCalled = true;
	}

	function validateOnCreateCalled() {
		this._validateOnCreateCalled = true;
	}

	function validateOnUpdateCalled() {
		this._validateOnUpdateCalled = true;
	}

}

Use number instead of int for Oracle.

component extends="Model" {

	function config() {
		hasMany(name="galleries");
		hasOne(name="combikey");
		hasOne(name="author", foreignKey="firstName", joinKey="firstName");
		hasMany(name="authors", foreignKey="firstName", joinKey="firstName");
		hasMany(name="combikeys");
		hasMany(name="outerjoinphotogalleries", modelName="gallery", jointype="outer");
		validatesPresenceOf("username, password, firstname, lastname");
		validatesUniquenessOf("username");
		validatesLengthOf(property="username", minimum="4", maximum="20", when="onCreate", message="Please shorten your [property] please. [maximum] characters is the maximum length allowed.");
		validatesLengthOf(property="password", minimum="4", when="onUpdate");
		validate("validateCalled");
		validateOnCreate("validateOnCreateCalled");
		validateOnUpdate("validateOnUpdateCalled");
		local.db_info = $dbinfo(datasource=application.wheels.dataSourceName, type="version");
		local.db = LCase(Replace(local.db_info.database_productname, " ", "", "all"));
		if (local.db IS "oracle") {
			property(name="salesTotal", sql="SUM(birthDayMonth)", select=false, dataType="number");
		} else {
			property(name="salesTotal", sql="SUM(birthDayMonth)", select=false, dataType="int");
		}
	}

	function validateCalled() {
		this._validateCalled = true;
	}

	function validateOnCreateCalled() {
		this._validateOnCreateCalled = true;
	}

	function validateOnUpdateCalled() {
		this._validateOnUpdateCalled = true;
	}

}


component {
	pgSQL = getCredentials();
    this.name = "test3426";
    this.datasources["LDEV_3426"] = pgSQL;
	this.datasource = "LDEV_3426"
    public function onRequestStart(){
		query{
			echo("DROP TABLE IF EXISTS LDEV3426_Primary");
		}
        query{
			echo("DROP TABLE IF EXISTS LDEV3426_test");
		}
		query{
			echo("CREATE TABLE LDEV3426_Primary( id SERIAL NOT NULL PRIMARY KEY, test VARCHAR(50))");
		}
	}
    private struct function getCredentials() {
    	return server.getDatasource("postgres");
	}
    public function onRequestEnd(){
		query{
			echo("DROP TABLE IF EXISTS LDEV3426_Primary");
		}
	}
}

Update the testcase to check datasource credentials available or not

component {
    pgSQL = getCredentials();
    this.name = "test3426";
    this.datasources["LDEV_3426"] = pgSQL;
    this.datasource = "LDEV_3426"
    public function onRequestStart(){
        if( StructIsEmpty(pgSQL) ){
            writeoutput("Datasource credentials was not available"); // Datasource credentials was not available means need to stop the iterations.
            abort;
        }
        query{
        echo("DROP TABLE IF EXISTS LDEV3426_test");
        }
        query{
            echo("DROP TABLE IF EXISTS LDEV3426_Primary");
        }
        query{
            echo("CREATE TABLE LDEV3426_Primary( id SERIAL NOT NULL PRIMARY KEY, test VARCHAR(50))");
        }
    }
    private struct function getCredentials() {
        return server.getDatasource("postgres");
    }
    public function onRequestEnd(){
        if( StructIsEmpty(pgSQL) ) return;
        query{
            echo("DROP TABLE IF EXISTS LDEV3426_test");
        }
        query{
            echo("DROP TABLE IF EXISTS LDEV3426_Primary");
        }
    }
}