Skip to content
Browse files

Cleaned up caching. Added namespace tagging and caching. Added implic…

…it calling (via onMissingMethod). Added installation to a scope from a configuration. Updated main example to show three usage patterns. Added advanced / automated example using Application.cfc to automatically install Clojure and namespaces based on configuration.
  • Loading branch information...
1 parent 2701e7c commit 5603015b31b6a5cf158c61e8cf8b4d936b04362e @seancorfield committed Sep 11, 2010
Showing with 248 additions and 48 deletions.
  1. +3 −0 Application.cfc
  2. +22 −0 advanced/Application.cfc
  3. +31 −0 advanced/index.cfm
  4. +3 −0 basic/Application.cfc
  5. +113 −0 basic/index.cfm
  6. +71 −14 cfmljure.cfc
  7. +5 −34 index.cfm
View
3 Application.cfc
@@ -0,0 +1,3 @@
+component {
+ this.name = hash( getBaseTemplatePath() );
+}
View
22 advanced/Application.cfc
@@ -0,0 +1,22 @@
+component {
+ this.name = hash( getBaseTemplatePath() );
+ // cfmljure configuration:
+ config = {
+ project = 'cfml',
+ files = 'cfml/examples',
+ ns = 'cfml.examples, clojure.core'
+ };
+
+ // magic that auto-installs Clojure stuff into variables scope:
+ function onRequestStart() {
+ if ( !structKeyExists( application, 'clj') ||
+ ( structKeyExists( URL, 'reload' ) && isBoolean( URL.reload ) && URL.reload ) ) {
+ application.clj = new cfmljure();
+ }
+ application.clj.install( config, variables );
+ }
+
+ function onRequest( string targetPath ) {
+ include targetPath;
+ }
+}
View
31 advanced/index.cfm
@@ -0,0 +1,31 @@
+<cfset start = getTickCount() />
+<cfscript>
+
+ writeOutput( '<h1>Calls via implicit method lookup after implicit installation</h1>' );
+
+ // call functions:
+
+ // simple function call:
+ writeOutput( '(greet "World") = ' & cfml.examples.greet( 'World' ) & '<br />' );
+
+ // pass CFML array to Clojure and loop over Clojure sequence that comes back:
+ list = cfml.examples.twice( [ 1, 2, 3 ] );
+ writeOutput( '(twice [ 1 2 3 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+ // simple function call (times2 is def'd to an anonymous function literal:
+ writeOutput( '(times2 42) = ' & cfml.examples.times2( 42 ) & '<br />' );
+
+ // call built-in Clojure function, passing raw definition of times2 function:
+ list = clojure.core.map( cfml.examples.get( 'times2' ).defn, [ 4, 5, 6 ] );
+ writeOutput( '(map times2 [ 4 5 6 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+</cfscript>
+<cfset end = getTickCount() />
+<cfoutput>
+ Time taken: #end - start#ms.<br />
+ <a href="?reload=true">Reload the runtime</a>.
+</cfoutput>
View
3 basic/Application.cfc
@@ -0,0 +1,3 @@
+component {
+ this.name = hash( getBaseTemplatePath() );
+}
View
113 basic/index.cfm
@@ -0,0 +1,113 @@
+<cfset start = getTickCount() />
+<cfscript>
+// load Clojure runtime (for cfml project - search path root is clj/cfml/src/):
+clj = new cfmljure( 'cfml' );
+// load scripts (from project source folder - that's clj/cfml/src/cfml/examples.clj):
+clj.load( 'cfml/examples' );
+
+// we can either get handles on specific functions like this:
+
+ writeOutput( '<h1>Calls via explicit handles on methods</h1>' );
+
+ // get handle on individual functions (from namespace cfml.examples):
+ greet = clj.get( 'cfml.examples.greet' );
+ twice = clj.get( 'cfml.examples.twice' );
+ times2 = clj.get( 'cfml.examples.times2' );
+ // get handle on built-in map function (from namespace clojure.core):
+ map = clj.get( 'clojure.core.map' );
+
+ // call functions:
+
+ // simple function call:
+ writeOutput( '(greet "World") = ' & greet.call( 'World' ) & '<br />' );
+
+ // pass CFML array to Clojure and loop over Clojure sequence that comes back:
+ list = twice.call( [ 1, 2, 3 ] );
+ writeOutput( '(twice [ 1 2 3 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+ // simple function call (times2 is def'd to an anonymous function literal:
+ writeOutput( '(times2 42) = ' & times2.call( 42 ) & '<br />' );
+
+ // call built-in Clojure function, passing raw definition of times2 function:
+ list = map.call( times2.defn, [ 4, 5, 6 ] );
+ writeOutput( '(map times2 [ 4 5 6 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+</cfscript>
+<cfset end = getTickCount() />
+<cfoutput>Time taken: #end - start#ms.<br /></cfoutput>
+<cfset start = getTickCount() />
+<cfscript>
+// or we can get the namespaces and then call methods directly:
+
+ // setup my namespaces:
+ cfml.examples = clj.ns( 'cfml.examples' );
+ clojure.core = clj.ns( 'clojure.core' );
+
+ writeOutput( '<h1>Calls via implicit method lookup (lowercase only, no -)</h1>' );
+
+ // call functions:
+
+ // simple function call:
+ writeOutput( '(greet "World") = ' & cfml.examples.greet( 'World' ) & '<br />' );
+
+ // pass CFML array to Clojure and loop over Clojure sequence that comes back:
+ list = cfml.examples.twice( [ 1, 2, 3 ] );
+ writeOutput( '(twice [ 1 2 3 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+ // simple function call (times2 is def'd to an anonymous function literal:
+ writeOutput( '(times2 42) = ' & cfml.examples.times2( 42 ) & '<br />' );
+
+ // call built-in Clojure function, passing raw definition of times2 function:
+ list = clojure.core.map( cfml.examples.get( 'times2' ).defn, [ 4, 5, 6 ] );
+ writeOutput( '(map times2 [ 4 5 6 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+</cfscript>
+<cfset end = getTickCount() />
+<cfoutput>Time taken: #end - start#ms.<br /></cfoutput>
+<cfset start = getTickCount() />
+<cfscript>
+// by configuration and installation:
+
+ config = {
+ project = 'cfml',
+ files = 'cfml/examples',
+ ns = 'cfml.examples, clojure.core'
+ };
+ target = { }; // normally you'd target a scope - this is just an example
+
+ // install the configuration to the target 'scope':
+ new cfmljure().install( config, target );
+
+ writeOutput( '<h1>Calls via implicit method lookup after installation to a target scope on every request</h1>' );
+
+ // call functions:
+
+ // simple function call:
+ writeOutput( '(greet "World") = ' & target.cfml.examples.greet( 'World' ) & '<br />' );
+
+ // pass CFML array to Clojure and loop over Clojure sequence that comes back:
+ list = target.cfml.examples.twice( [ 1, 2, 3 ] );
+ writeOutput( '(twice [ 1 2 3 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+ // simple function call (times2 is def'd to an anonymous function literal:
+ writeOutput( '(times2 42) = ' & target.cfml.examples.times2( 42 ) & '<br />' );
+
+ // call built-in Clojure function, passing raw definition of times2 function:
+ list = target.clojure.core.map( target.cfml.examples.get( 'times2' ).defn, [ 4, 5, 6 ] );
+ writeOutput( '(map times2 [ 4 5 6 ]) = ' );
+ for ( n in list ) writeOutput( n & ' ' );
+ writeOutput( '<br />' );
+
+</cfscript>
+<cfset end = getTickCount() />
+<cfoutput>Time taken: #end - start#ms.</cfoutput>
View
85 cfmljure.cfc
@@ -15,38 +15,60 @@
limitations under the License.
*/
- variables.clj = { };
-
- public any function init( string project = '' ) {
- variables.project = project;
- variables.rt = createObject( 'java', 'clojure.lang.RT' );
+ // constructor
+ public any function init( string project = '', any rt = 0, string ns = '' ) {
+ variables._project = project;
+ variables._ns = ns;
+ variables._files = '';
+ variables._refCache = { };
+ variables._nsCache = { };
+ if ( isObject( rt ) ) {
+ variables._rt = rt;
+ } else {
+ variables._rt = createObject( 'java', 'clojure.lang.RT' );
+ }
return this;
}
+ // load a list of files
public void function load( string fileList ) {
- var prefix = variables.project == '' ? '' : variables.project & '/src/';
+ // clear the reference cache if we load any files
+ variables._refCache = { };
+ var prefix = variables._project == '' ? '' : variables._project & '/src/';
var files = listToArray( fileList );
var file = 0; // CFBuilder barfs on for ( var file in files ) so declare it separately!
for ( file in files ) {
- variables.rt.loadResourceScript( 'clj/' & prefix & file & '.clj' );
+ variables._rt.loadResourceScript( 'clj/' & prefix & trim( file ) & '.clj' );
}
}
+ // get a specific Clojure function
public any function get( string ref ) {
- if ( !structKeyExists( variables.clj, ref ) ) {
- var fn = listLast( ref , '.' );
- var ns = left( ref, len( ref ) - len( fn ) - 1 );
- var r = variables.rt.var( ns, fn );
- variables.clj[ref] = createObject( 'component', 'cfmljure' ).def( r );
+ var fqRef = listAppend( variables._ns, ref, '.' );
+ if ( !structKeyExists( variables._refCache, fqRef ) ) {
+ var fn = listLast( fqRef , '.' );
+ var ns = left( fqRef, len( fqRef ) - len( fn ) - 1 );
+ var r = variables._rt.var( ns, fn );
+ variables._refCache[ref] = new cfmljure( variables._project, variables._rt, variables._ns ).def( r );
}
- return variables.clj[ref];
+ return variables._refCache[ref];
}
-
+
+ // set up a context for a Clojure namespace
+ public any function ns( string ref ) {
+ if ( !structKeyExists( variables._nsCache, ref ) ) {
+ variables._nsCache[ref] = new cfmljure( variables._project, variables._rt, ref );
+ }
+ return variables._nsCache[ref];
+ }
+
+ // tag this instance with a specific Clojure function definition so it can be called
public any function def( any defn ) {
this.defn = defn;
return this;
}
+ // explicit call method with up to five positional arguments
public any function call() {
switch ( arrayLen( arguments ) ) {
case 0:
@@ -66,4 +88,39 @@
}
}
+ // support dynamic calling of any method in the current namespace
+ public any function onMissingMethod( string missingMethodName, any missingMethodArguments ) {
+ var ref = get( lCase( missingMethodName ) );
+ return ref.call( argumentCollection = missingMethodArguments );
+ }
+
+ // install from a configuration into a target
+ public void function install( struct config, struct target ) {
+ var project = structKeyExists( config, 'project' ) ? config.project : '';
+ var fileList = structKeyExists( config, 'files' ) ? config.files : '';
+ var namespaceList = structKeyExists( config, 'ns' ) ? config.ns : '';
+ var clj = this;
+ if ( project != variables._project ) clj = new cfmljure( project, variables._rt, variables._ns );
+ clj.load( fileList );
+ target.clj = clj;
+ var namespaces = listToArray( namespaceList );
+ var ns = 0; // CFBuilder barfs on for ( var ns in namespaces ) so declare it separately!
+ for ( ns in namespaces ) {
+ var _ns = trim( ns );
+ var pair = _makePath( _ns, target );
+ pair.s[pair.key] = clj.ns( _ns );
+ }
+ }
+
+ // helper for installing namespace paths
+ private struct function _makePath( string path, struct target ) {
+ var head = listFirst( path, '.' );
+ if ( listLen( path, '.' ) == 1 ) {
+ return { s = target, key = head };
+ } else {
+ if ( !structKeyExists( target, head ) ) target[head] = { };
+ return _makePath( listRest( path, '.' ), target[head] );
+ }
+ }
+
}
View
39 index.cfm
@@ -1,34 +1,5 @@
-<cfscript>
-// load Clojure runtime (for cfml project - search path root is clj/cfml/src/):
-clj = new cfmljure( 'cfml' );
-// load scripts (from project source folder - that's clj/cfml/src/cfml/examples.clj):
-clj.load( 'cfml/examples' );
-
-// get handle on individual functions (from namespace cfml.examples):
-greet = clj.get( 'cfml.examples.greet' );
-twice = clj.get( 'cfml.examples.twice' );
-times2 = clj.get( 'cfml.examples.times2' );
-// get handle on built-in map function (from namespace clojure.core):
-map = clj.get( 'clojure.core.map' );
-
-// call functions:
-
-// simple function call:
-writeOutput( '(greet "World") = ' & greet.call( 'World' ) & '<br />' );
-
-// pass CFML array to Clojure and loop over Clojure sequence that comes back:
-list = twice.call( [ 1, 2, 3 ] );
-writeOutput( '(twice [ 1 2 3 ]) = ' );
-for ( n in list ) writeOutput( n & ' ' );
-writeOutput( '<br />' );
-
-// simple function call (times2 is def'd to an anonymous function literal:
-writeOutput( '(times2 42) = ' & times2.call( 42 ) & '<br />' );
-
-// call built-in Clojure function, passing raw definition of times2 function:
-list = map.call( times2.defn, [ 4, 5, 6 ] );
-writeOutput( '(map times2 [ 4 5 6 ]) = ' );
-for ( n in list ) writeOutput( n & ' ' );
-writeOutput( '<br />' );
-
-</cfscript>
+<h1>cfmljure examples</h1>
+<h2>Basic example</h2>
+<p><a href="basic/index.cfm" target="_blank">Three low-level examples of using cfmljure</a></p>
+<h2>Advanced example</h2>
+<p><a href="advanced/index.cfm" target="_blank">Integrated / automated cfmljure example</a></p>

0 comments on commit 5603015

Please sign in to comment.
Something went wrong with that request. Please try again.