Recently we’ve been working on our own Java API for Berkeley DB XML, of course on top of existing one.

For stored functions we have something like this (please note that’s the API invocation):

DbStoredFunction function = dbApi.getStoredFunction("rmutils:getFolderId");
function.setVariable("fatName", fatName) 
function.setVariable("ownerId", NavioId) 
List result = fucntion.getResults();

//another function invocation
DbStoredFunction function = dbApi.getStoredFunction("rmutils:getUserPass");
function.setVariable("ownerId", NavioId) 
List result = fucntion.getResults();

Functions are usual xQueries which are defined in a separate file.

declare function rmutils:getFolderId($ownerId as xs:string, $fatName as xs:string)
{
    for $a in collection($FAT_COLLECTION)/Fat[@userId=$ownerId]/Folder[@parentFatId="0"]
    where string($a/@name) = $fatName
    return string($a/@id)
};

declare function rmutils:getUserPass($pass_seq, $ownerId as xs:string)
{
    
    {
        for $a in collection($TITLE_COLLECTION)/Record[string(@ownerId)=$ownerId]
            let $id := utils:document-name($a)
            let $cT := string($a/tf:TitleDocument/tf:Title/Content/@contentType)
        where some $b in pass_seq satisfies ($b = $cT) and
                   string($a/@isDeleted) = "false" and
                   empty(string($a/@tradeLock))
        return {$a/tf:TitleDocument}
    }
    
};

I was thinking could it be possible to have list of available functions and their parameters as API’s methods at runtime?

I wanted to see something like this:

	result = dbApi.getFolderId(NavioId, fatName)
# and
	result = dbApi.getUserPass(NavioId)

It can be any other method which is derived from function name in xQuery module file. I want the queries to be part of my API.

Ruby can provide us mechanism to do so, it’s called metaprogramming.
From Wikipedia:

Metaprogramming is the writing of programs that write or manipulate other programs (or themselves) as their data or that do part of the work that is otherwise done at compile time during runtime. In many cases, this allows programmers to get more done in the same amount of time as they would take to write all the code manually.

I decided to have two classes one for xQuery parsing and another one which represent our API (class with available methods let’s call it DbApi).
I’ll show you just the second one.

class DbApi
	self.module_eval do
		# get all function names from xquery file
		XqParser.getMethodsNames.each do |methodName|
			define_method eval(":#{methodName}") do |*args|
				# here can be anything you want your method to do
					query = XqParser.parse (methodName, args)
					runQuery(query)        
				end
			end
		end
	
		def runQuery (query)
			# run the query on database instance
			...
		end
end

Eventually it’s very simple. Your method could contain anything you want what corresponds to your problem.

What does it offer? Oh… boy a lot. You have API which just grows as you add more functions in a simple text-based file. Your code looks cool and is easy to read. You DON’T repeat yourself. You write less. And it’s just fun.

*I didn’t implement actual db invocation logic as well as some methods in the xuery parser class, if you want you can do this on your own. I just wanted to show some nice features of Ruby. Have fun.

Advertisements