CFC Custom Tag Example
This post was inspired by Raymond Camden recent blog post about Yahoo Query Language (YQL). Back in June 2009, Michael Offner-Streit blogged about how to create CFC Custom Tags. So, I thought I'd do a little experimenting with this idea and convert Ray Camden's custom tag (cfm) over to a custom tag (cfc) that Railo enables you to use.
So, here's the CFC Equivalent to Ray's yql.cfm which is now yql.cfc:
<cfcomponent name="yql">
<!--- Meta data --->
<cfset this.metadata.attributetype="fixed">
<cfset this.metadata.attributes={
name: {required:true,type:"string",default:""}
}>
<cffunction name="init" output="no" returntype="void" hint="invoked after tag is constructed">
<cfargument name="hasEndTag" type="boolean" required="yes">
<cfargument name="parent" type="component" required="no" hint="the parent cfc custom tag, if there is one">
</cffunction>
<cffunction name="onStartTag" output="yes" returntype="boolean">
<cfargument name="attributes" type="struct">
<cfargument name="caller" type="struct">
<cfreturn true>
</cffunction>
<cffunction name="onEndTag" output="yes" returntype="boolean">
<cfargument name="attributes" type="struct">
<cfargument name="caller" type="struct">
<cfset caller[arguments.attributes.name] = queryYahoo(arguments.generatedcontent)>
<cfreturn false>
</cffunction>
<cffunction name="queryYahoo" output="yes" returntype="query" access="private">
<cfargument name="yqlrequest" required="yes" type="string">
<cfset var yql = trim(urlencodedformat(arguments.yqlrequest))>
<cfset var data = "">
<cfset var query = queryNew('blank')>
<cfset var key = "">
<cfset var totalResults = 0>
<!--- run the SQL --->
<cfhttp url="http://query.yahooapis.com/v1/public/yql?q=#yql#&format=json" result="result" getasbinary="no">
<!--- convert bin data to string --->
<cfset data = result.fileContent.toString()>
<cfset data = deserializeJSON(data)>
<cfset key = listFirst(structKeyList(data.query.results))>
<cfset totalResults = data.query.count>
<cfif totalResults GT 0>
<cfif isSimpleValue(data.query.results[key][1])>
<cfset query = queryNew(key)>
<cfloop index="x" from="1" to="#totalResults#">
<cfset queryAddRow(query)>
<cfloop index="col" list="#query.columnList#">
<cfset querySetCell(query, col, data.query.results[col][x])>
</cfloop>
</cfloop>
<cfelse>
<cfset query = queryNew(structKeyList(data.query.results[key][1]))>
<cfloop index="x" from="1" to="#totalResults#">
<cfset queryAddRow(query)>
<cfloop index="col" list="#query.columnList#">
<cfset querySetCell(query, col, data.query.results[key][x][col])>
</cfloop>
</cfloop>
</cfif>
</cfif>
<cfreturn query>
</cffunction>
</cfcomponent>
And our test.cfm to test it:
<cf_yql name="results">
select * from html(0,10) where url='http://www.dcs.gla.ac.uk/~joy/fun/jokes/TV.html' and xpath='//ul/li/p'
</cf_yql>
<cfdump eval=results>
Which pretty much has the same result as Ray's CFM Custom Tag:
Now, let's take this one step further and let's make this as if it was a Built-In-Tag native to Railo. I'm not going to make this global to the server tho, just local to the one of many hosts I have available on my server. So, take the yql.cfc and put it in the following location:
{host www folder}\WEB-INF\railo\library\tag
Once there, browse to your server context of railo ( http://localhost/railo-context/admin/server.cfm ) and login. On the left, look for Services > Restart and click the Restart Railo button. Railo, not the JEE server, needs restarted to pick up these tags / functions. Once restarted, you can now write you're code like this:
<cfyql name="results">
select * from html(0,10) where url='http://www.dcs.gla.ac.uk/~joy/fun/jokes/TV.html' and xpath='//ul/li/p'
</cfyql>
<cfdump eval=results>
There you go. Your first built-in-tag utilizing Yahoo Query Language. This is one of the many ways that the Railo community is filling in the gaps of missing tags and functions without the need to write a single line of Java. This is how CFAjaxproxy (and others) are being built by Andrea Campolonghi.
This is how you can extend the CFML language. It's your choice. Railo is just opening doors for you. :)



Any word in getting other engines to support this?
Another alternative is a to provide an xQuery parser e.g
<cf_XQuery name="results">
for $i in document("http://www.dcs.gla.ac.uk/~joy/fun/jokes/TV.html&qu...;)
return $i//ul/li/p
</CF_XQuery>
This provides more expressiveness than SQL and xpath combined.
I have developed a custom tag to provide this functionality at:
http://www.cfxquery.co.uk/CF-XQuery-tags-CFXQuery....
Many Thanks,
Will
you can easily convert it into a Railo internal tag by converting it into a CFC based custom tag...
Gert
This is one of those "do as I say, not as I do" examples because my example above clearly doesn't have any error handling at all and there could be a million things that goes wrong with <cfyql> tag above (API timeouts, bad internet connection, etc).