Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
lmajano committed Jan 22, 2020
2 parents 8a58e18 + 960a17a commit 6f3858d
Show file tree
Hide file tree
Showing 19 changed files with 271 additions and 255 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ test-harness/docbox/**
test-harness/testbox/**
test-harness/logs/**
test-harness/modules/**
test-harness/.env

# log files
logs/**
33 changes: 14 additions & 19 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ env:
- MODULE_ID=mementifier
matrix:
- ENGINE=lucee@5
- ENGINE=adobe@11
- ENGINE=adobe@2016
- ENGINE=adobe@2018

Expand All @@ -33,31 +32,31 @@ addons:
before_install:
# CommandBox Keys
- curl -fsSl https://downloads.ortussolutions.com/debs/gpg | sudo apt-key add -
- sudo echo "deb http://downloads.ortussolutions.com/debs/noarch /" | sudo tee -a
- sudo echo "deb https://downloads.ortussolutions.com/debs/noarch /" | sudo tee -a
/etc/apt/sources.list.d/commandbox.list

install:
# Install Commandbox
- sudo apt-get update && sudo apt-get --assume-yes install rsync jq commandbox
# Install CommandBox Supporting Librarires
- box install commandbox-cfconfig,commandbox-dotenv,commandbox-docbox
# If using auto-publish, you will need to provide your API token with this line:
- box config set endpoints.forgebox.APIToken=$FORGEBOX_API_TOKEN > /dev/null

before_script:
# create test database
# create database
- mysql -u root -e 'create database mementifier;'
# import database
#- mysql -u root < test-harness/tests/resources/coolblog.sql
#- mysql -u root < test-harness/tests/resources/cbsecurity.sql
#- echo "Database created and loaded with test data"
# Seed .env
- echo "Seeding database .env file at /test-harness/.env"
- touch test-harness/.env
- printf "DB_HOST=localhost\n" >> test-harness/.env
- printf "DB_DATABASE=mementifier\n" >> test-harness/.env
- printf "DB_USER=root\n" >> test-harness/.env
- printf "DB_USERNAME=root\n" >> test-harness/.env
- printf "DB_PASSWORD=\n" >> test-harness/.env

install:
# Install Commandbox
- sudo apt-get update && sudo apt-get --assume-yes install rsync jq commandbox
# Install CommandBox Supporting Librarires
- box install commandbox-cfconfig,commandbox-dotenv,commandbox-docbox
# If using auto-publish, you will need to provide your API token with this line:
- box config set endpoints.forgebox.APIToken=$FORGEBOX_API_TOKEN > /dev/null

script:
# Set Current Version
- TARGET_VERSION=`cat $TRAVIS_BUILD_DIR/box.json | jq '.version' -r`
Expand All @@ -68,12 +67,9 @@ script:
- cd test-harness
# run our dependency install to ensure the workbench is in place
- box install
- rm -Rf modules/cborm/modules/mementifier
# run our matrix server
- box server start serverConfigFile="server-${ENGINE}.json"
# If lucee 5 stop it due to stupid ORM bug that never gets fixed on first start.
- echo "Checking if lucee5 so we can do a restart due to a stupid ORM startup bug."
- if [ ${ENGINE} = 'lucee@5' ]; then box server stop; fi
- if [ ${ENGINE} = 'lucee@5' ]; then box server start serverConfigFile="[email protected]"; fi
# Startup the app
- curl http://localhost:60299
# Debugging of tests
Expand All @@ -86,7 +82,6 @@ script:
- cat build/results.json

after_failure:
- cat build/results.json
- cd $TRAVIS_BUILD_DIR/test-harness
# Display the contents of our root directory
# Spit out our Commandbox log in case we need to debug
Expand Down Expand Up @@ -132,4 +127,4 @@ after_deploy:
- cd ${TRAVIS_BUILD_DIR}/.tmp/${MODULE_ID}
- cat box.json
# Only publish once
- if [ ${ENGINE} = 'lucee@5' ]; then box forgebox publish; fi
- if [ ${ENGINE} = 'lucee@5' ]; then box forgebox publish; fi
4 changes: 3 additions & 1 deletion ModuleConfig.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ component {
// Enable orm auto default includes: If true and an object doesn't have any `memento` struct defined
// this module will create it with all properties and relationships it can find for the target entity
// leveraging the cborm module.
ormAutoIncludes = true
ormAutoIncludes = true,
// The default value for getters which return null
nullDefaultValue = ''
};

// Custom Declared Interceptors
Expand Down
2 changes: 1 addition & 1 deletion box.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name":"Mementifier : The State Maker!",
"version":"1.9.0",
"version":"2.0.0",
"location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/mementifier/@build.version@/[email protected]@.zip",
"author":"Ortus Solutions, Corp",
"homepage":"https://github.com/coldbox-modules/mementifier",
Expand Down
18 changes: 18 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 2.0.0

### Features

* Enabled wildcard default includes (*) to retrieve inherited object properties instead of doing wacky things for inherited defaults to work.
* New setting to chose a default value to expose when getters return `null`: `nullDefaultValue`
* ORM Auto includes now ONLY includes properties to avoid bi-directional recursive exceptions. This is also a compatiblity, where before EVERYTHING was included. Now, only properties are included.

### Improvements

* Updated to cborm2 for testing harness
* Updated to TestBox 3

### Compatibility

* Removed ACF 11 Support
* ORM Auto includes only marshalls properties instead of everything.

## v1.9.0

* More Adobe ColdFusion incompatibilities
Expand Down
116 changes: 97 additions & 19 deletions interceptors/Mementifier.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ component{
}

// Inject helper methods
arguments.entity.$injectMixin( "$buildNestedMementoList", variables.$buildNestedMementoList );
arguments.entity.$injectMixin( "$buildNestedMementoList", variables.$buildNestedMementoList );
arguments.entity.$injectMixin( "$getDeepProperties", variables.$getDeepProperties );
// We do simple date formatters as they are faster than CFML methods
arguments.entity.$FORMATTER_ISO8601 = createObject( "java", "java.text.SimpleDateFormat" ).init( "yyyy-MM-dd'T'HH:mm:ssXXX" );
arguments.entity.$FORMATTER_CUSTOM = createObject( "java", "java.text.SimpleDateFormat" ).init( "#settings.dateMask# #settings.timeMask#" );
Expand Down Expand Up @@ -121,19 +122,59 @@ component{
thisName = ( md.keyExists( "entityName" ) ? md.entityName : listLast( md.name, "." ) );
}

thisMemento.defaultIncludes = ormGetSessionFactory()
.getClassMetaData( thisName )
.getPropertyNames();
var ORMService = new cborm.models.BaseORMService();

var entityMd = ORMService.getEntityMetadata( this );
var typeMap = arrayReduce(
entityMd.getPropertyNames(),
function( mdTypes, propertyName ){
var propertyType = entityMd.getPropertyType( propertyName );
var propertyClassName = getMetadata( propertyType ).name;

mdTypes[ propertyName ] = propertyClassName;
return mdTypes;
}
,{});

thisMemento.defaultIncludes = typeMap.keyArray().filter( function( propertyName ){
switch( listLast( typeMap[ propertyName ], "." ) ){
case "BagType":
case "OneToManyType":
case "ManyToManyType":
case "ManyToOneType":
case "OneToOneType":
case "BinaryType":{
return false;
}
default:{
return true;
}
}
} );

// Append primary keys
if( entityMd.hasIdentifierProperty() ){
arrayAppend( thisMemento.defaultIncludes, entityMd.getIdentifierPropertyName() );
} else if( thisMemento.defaultIncludes.getIdentifierType().isComponentType() ){
arrayAppend( thisMemento.defaultIncludes, listToArray( arrayToList( entityMd.getIdentifierType().getPropertyNames() ) ), true );
}
}

// Do we have a * for auto includes of all properties in the object
if( arrayLen( thisMemento.defaultIncludes ) && thisMemento.defaultIncludes[ 1 ] == "*" ){
thisMemento.defaultIncludes = getMetadata( this ).properties

// assign the default includes to be all properties
// however, we exclude anything with an inject key and anything on the default exclude list
thisMemento.defaultIncludes = $getDeepProperties()
.filter( function( item ){
return !item.keyExists( "inject" );
return (
!item.keyExists( "inject" ) &&
!thisMemento.defaultExcludes.findNoCase( item.name )
);
} ).map( function( item ){
return item.name;
} );
} );

}

// Incorporate Defaults if not ignored
Expand Down Expand Up @@ -177,7 +218,7 @@ component{
var thisValue = invoke( this, "get#item#" );
// Verify Nullness
thisValue = isNull( thisValue ) ? (
structKeyExists( thisMemento.defaults, item ) ? thisMemento.defaults[ item ] : ""
structKeyExists( thisMemento.defaults, item ) ? thisMemento.defaults[ item ] : $mementifierSettings.nullDefaultValue
) : thisValue;
} else {
// Calling for non-existent properties, skip out
Expand Down Expand Up @@ -218,19 +259,20 @@ component{
}

// Array Collections
if( isArray( thisValue ) ){
else if( isArray( thisValue ) ){
// Map Items into result object
result[ item ] = [];
for( var thisIndex = 1; thisIndex <= arrayLen( thisValue ); thisIndex++ ){
// only get mementos from relationships that have mementos, in the event that we have an already-serialized array of structs
if( !isSimpleValue( thisValue[ thisIndex ] ) && structKeyExists( thisValue[ thisIndex ], "getMemento" ) ) {

var nestedIncludes = $buildNestedMementoList( includes, item );
result[ item ][ thisIndex ] = thisValue[ thisIndex ].getMemento(
includes = $buildNestedMementoList( includes, item ),
includes = nestedIncludes,
excludes = $buildNestedMementoList( excludes, item ),
mappers = mappers,
defaults = defaults,
ignoreDefaults = ignoreDefaults
// cascade the ignore defaults down if specific nested includes are requested
ignoreDefaults = nestedIncludes.len() ? ignoreDefaults : false
);

} else {
Expand All @@ -240,28 +282,35 @@ component{
}

// Single Object Relationships
if( isObject( thisValue ) ){
else if( isValid( 'component', thisValue ) && structKeyExists( thisValue, "getMemento" ) ){
//writeDump( var=$buildNestedMementoList( includes, item ), label="includes: #item#" );
//writeDump( var=$buildNestedMementoList( excludes, item ), label="excludes: #item#" );
var nestedIncludes = $buildNestedMementoList( includes, item );
result[ item ] = thisValue.getMemento(
includes = $buildNestedMementoList( includes, item ),
includes = nestedIncludes,
excludes = $buildNestedMementoList( excludes, item ),
mappers = mappers,
defaults = defaults,
ignoreDefaults = ignoreDefaults
// cascade the ignore defaults down if specific nested includes are requested
ignoreDefaults = nestedIncludes.len() ? ignoreDefaults : false
);
} else {
// we don't know what to do with this item so we return as-is
result[ item ] = thisValue;
}

// Result Mapper for Item Result
if( mappersKeyArray.findNoCase( item ) ){

// ACF compat
var thisMapper = thisMemento.mappers[ item ];
result[ item ] = thisMapper( result[ item ] );
}

// ensure anything left over is provided as the value
if( !structKeyExists( result, item ) ){
} else if( !structKeyExists( result, item ) ){

// ensure anything left over is provided as the value
result[ item ] = thisValue;

}

}
Expand Down Expand Up @@ -302,5 +351,34 @@ component{
variables[ arguments.name ] = arguments.target;
this[ arguments.name ] = arguments.target;
return this;
}
}

/**
* Get Deep Properties
* Returns an array of an objects properties including those inherited by base classes.
*
* @metaData (optional) The starting CFML metadata of the entity object. Defaults to the current object.
*
* @return an array of object properties
*/
private array function $getDeepProperties( struct metaData = getMetaData( this ) ) {

var properties = [];

// if this object extends another object, append any inherited properties.
if (
structKeyExists( arguments.metaData, "extends" ) &&
structKeyExists( arguments.metaData.extends, "properties" )
) {
properties.append( $getDeepProperties( arguments.metaData.extends ), true );
}

// if this object has properties, append them.
if ( structKeyExists( arguments.metaData, "properties" ) ) {
properties.append( arguments.metadata.properties, true );
}

return properties;

}
}
35 changes: 34 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ moduleSettings = {
// Enable orm auto default includes: If true and an object doesn't have any `memento` struct defined
// this module will create it with all properties and relationships it can find for the target entity
// leveraging the cborm module.
ormAutoIncludes = true
ormAutoIncludes = true,
// The default value for relationships/getters which return null
nullDefaultValue = ''
}
}
```
Expand Down Expand Up @@ -269,6 +271,37 @@ function process(
){}
```

## Running The Test Suites

In order to collaborate on this project you will need to do a few things in order to get the test harness ready for execution. The `test-harness` folder is where the ColdBox test app exists that consumes the module for testing. The `test-harness/tests/specs` is where all the specs for testing are located.

### Database

Create a database called `mementifier` in any RDBMS you like. We have mostly used MySQL for the tests.

### Environment

Copy the `.env.template` as `.env` and modify it accordingly so it can connect to your database.

### Dependencies

Go into the root of `test-harness` and run a CommandBox shell: `box`. Once in the shell install the dependencies `install`.

### Start a Server

Start a server, we have configured for you several CFML engines for you to test against, pick one from the list below:

- `server start [email protected]`
- `server start [email protected]`
- `server start [email protected]`
- `server start [email protected]`

Then you can hit the test site app at http://localhost:60299. This will create the database for you using the ColdFusion ORM.

### Running Tests

You can then run the tests at http://localhost:60299/tests/runner.cfm

********************************************************************************
Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp
www.ortussolutions.com
Expand Down
Loading

0 comments on commit 6f3858d

Please sign in to comment.