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:

Results

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. :)

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Henry Ho's Gravatar It'd be cool if there's an app that can convert .cfm custom tag into .cfc style.

Any word in getting other engines to support this?
# Posted By Henry Ho | 11.09.09 20:11
William Greenly's Gravatar I take it this is dependent on anticipating well formed xml at the end of the url?

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
# Posted By William Greenly | 02.10.09 14:00
Gert Franz's Gravatar @William,

you can easily convert it into a Railo internal tag by converting it into a CFC based custom tag...

Gert
# Posted By Gert Franz | 02.10.09 14:45
Todd Rafferty's Gravatar @William: Yes, all of this is highly dependent on you thinking out of the box and providing the appropriate error handling. Anytime that you're making a 'tag' whether it's custom or built-in, you should provide high quality error handling.

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).
# Posted By Todd Rafferty | 04.01.10 19:25
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.002. Contact Blog Owner