Skip to content

Commit

Permalink
Allow render type to be a function #328
Browse files Browse the repository at this point in the history
This allows custom renderers for built-in types by overriding the
render_{type}() function or supplying a function or closure for the
renderData() type argument.
  • Loading branch information
seancorfield committed Nov 28, 2015
1 parent 5681a65 commit e8f8c0f
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 53 deletions.
138 changes: 85 additions & 53 deletions framework/one.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,13 @@ component {
out = internalLayout( request._fw1.layouts[i], out );
}
}
writeOutput( out );
if ( isSimpleValue( out ) ) {
writeOutput( out );
} else if ( structKeyExists( out, 'writer' ) ) {
out.writer( out.output );
} else {
writeOutput( out.output );
}
setupResponseWrapper();
}

Expand Down Expand Up @@ -2088,61 +2094,87 @@ component {
return resourceCache[ cacheKey ];
}

private string function renderDataWithContentType() {
var out = '';
var contentType = '';
var type = request._fw1.renderData.type;
var data = request._fw1.renderData.data;
private struct function render_json( struct renderData ) {
return {
contentType = 'application/json; charset=utf-8',
output = serializeJSON( renderData.data ),
};
}

private struct function render_jsonp( struct renderData ) {
if ( !structKeyExists( renderData, 'jsonpCallback' ) || !len( renderData.jsonpCallback ) ){
throw( type = 'FW1.jsonpCallbackRequired',
message = 'Callback was not defined',
detail = 'renderData() called with jsonp type requires a jsonpCallback' );
}
return {
contentType = 'application/javascript; charset=utf-8',
output = renderData.jsonpCallback & "(" & serializeJSON( renderData.data ) & ");"
};
}

private struct function render_rawjson( struct renderData ) {
return {
contentType = 'application/json; charset=utf-8',
output = renderData.data
};
}

private struct function render_html( struct renderData ) {
structDelete( request._fw1, 'renderData' );
return {
contentType = 'text/html; charset=utf-8',
output = renderData.data
};
}

private struct function render_xml( struct renderData ) {
var output = '';
if ( isXML( renderData.data ) ) {
if ( isSimpleValue( renderData.data ) ) {
// XML as string already
output = renderData.data;
} else {
// XML object
output = toString( renderData.data );
}
} else {
throw( type = 'FW1.UnsupportXMLRender',
message = 'Data is not XML',
detail = 'renderData() called with XML type but unrecognized data format' );
}
return {
contentType = 'text/xml; charset=utf-8',
output = output
};
}

private struct function render_text( struct renderData ) {
return {
contentType = 'text/plain; charset=utf-8',
output = renderData.data
};
}

private struct function renderDataWithContentType() {
var out = { };
var renderType = request._fw1.renderData.type;
var statusCode = request._fw1.renderData.statusCode;
var statusText = request._fw1.renderData.statusText;
switch ( type ) {
case 'json':
contentType = 'application/json; charset=utf-8';
out = serializeJSON( data );
break;
case 'jsonp':
contentType = 'application/javascript; charset=utf-8';
if ( !len(request._fw1.renderData.jsonpCallback) ){
throw( type = 'FW1.jsonpCallbackRequired',
message = 'Callback was not defined',
detail = 'renderData() called with jsonp type requires a jsonpCallback' );
}
out = request._fw1.renderData.jsonpCallback & "(" & serializeJSON( data ) & ");";
break;
case 'rawjson':
contentType = 'application/json; charset=utf-8';
out = data;
break;
case 'html':
contentType = 'text/html; charset=utf-8';
out = data;
structDelete( request._fw1, 'renderData' );
break;
case 'xml':
contentType = 'text/xml; charset=utf-8';
if ( isXML( data ) ) {
if ( isSimpleValue( data ) ) {
// XML as string already
out = data;
} else {
// XML object
out = toString( data );
}
if ( isSimpleValue( renderType ) ) {
var fn_type = 'render_' & renderType;
if ( structKeyExists( variables, fn_type ) ) {
renderType = variables[ fn_type ];
// evaluate with no FW/1 context!
out = renderType( request._fw1.renderData );
} else {
throw( type = 'FW1.UnsupportXMLRender',
message = 'Data is not XML',
detail = 'renderData() called with XML type but unrecognized data format' );
throw( type = 'FW1.UnsupportedRenderType',
message = 'Only HTML, JSON, JSONP, RAWJSON, XML, and TEXT are supported',
detail = 'renderData() called with unknown type: ' & renderType );
}
break;
case 'text':
contentType = 'text/plain; charset=utf-8';
out = data;
break;
default:
throw( type = 'FW1.UnsupportedRenderType',
message = 'Only HTML, JSON, JSONP, RAWJSON, XML, and TEXT are supported',
detail = 'renderData() called with unknown type: ' & type );
break;
} else {
// assume it is a function
out = renderType( request._fw1.renderData );
}
// in theory, we should use sendError() instead of setStatus() but some
// Servlet containers interpret that to mean "Send my error page" instead
Expand All @@ -2153,7 +2185,7 @@ component {
} else {
resp.setStatus( statusCode );
}
resp.setContentType( contentType );
resp.setContentType( out.contentType );
return out;
}

Expand Down
20 changes: 20 additions & 0 deletions tests/frameworkRenderTest.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,25 @@ component extends="mxunit.framework.TestCase" {
assertEquals( output, "custom trace render" );
}

public void function testRenderFunction() {
request.fw.onApplicationStart();
variables.fw.renderData().type( function( renderData ) {
return {
contentType = "text/html; charset=utf-8",
output = "string",
writer = function( out ) {
writeOutput( "my written " & out );
}
};
} ).data( "myteststring" );
var output = "";
savecontent variable="output" {
request.fw.onRequest("/index.cfm");
request.fw.onRequestEnd();
}
assertTrue( output contains "my written string" );
assertFalse( output contains "framework lifecycle trace" );
}


}

0 comments on commit e8f8c0f

Please sign in to comment.