Although I don’t have any immediate plans to implement a back-end for anything other than WordPress, I want to make sure it’s possible, so I have created a list of operations I want to be able to do on posts (well, OK, I haven’t actually written it down or anything, but it’s in my head. Somewhere). Each back-end needs to implement each call (even if it’s just a no-op).
If that was the only abstraction, though, we would end up with a lot of boilerplate code—or we would define some macros, which I’m not yet familiar with—because each operation would have to take care of pulling out whatever information it needed from the arguments it was handed—which will always include a
blog structure as the first item.
Instead, I opted for a level of indirection. Instead of calling a back-end function directly, any time the back-end-agnostic code wants to perform an operation on an article, it goes through
org-blog-call figures out what engine the
blog structure is using, constructs the name of the operation for the back-end associated with the blog, and then invokes it.
So it’s really pretty short—pulling out a single bit of info and checking for a function’s existence.
(defun org-blog-call (blog call &rest args) "Make the specified call to the appropriate blog engine. This allows us to maintain multiple engines, with a set of operations common to all, and call the appropriate function based on the engine specification in the entry in `org-blog-alist'." (let ((entry (intern (concat "org-blog-" (cdr (assq :engine blog)) "-call")))) (if (fboundp entry) (apply entry blog call args) (error (format "Can't find function %s" entry)))))
What this is invoking, in the case of a WordPress blog, is
org-blog-wp-call. It doesn’t do whole heck of a lot, either, other than pull out the parameters that are common to each XML-RPC request, and invoking the actual function (whose name it has constructed, much like
(defun org-blog-wp-call (blog call &rest args) "Easy calls to WordPress API functions. This routine will take whatever information a user has available, fill in the rest (if the user is willing to cooperate) and then call the specified function and return the results." (let ((blog-id (cdr (assq :blog-id blog))) (func (intern (concat "org-blog-wp-" call))) (password (cdr (assq :password blog))) (username (cdr (assq :username blog))) (xmlrpc (cdr (assq :xmlrpc blog)))) (if (fboundp func) (apply func xmlrpc blog-id username password args) (error "Can't find function %s" func))))
At this point, the actual XML-RPC call is pretty anticlimactic:
(defun org-blog-wp-post-create (xmlrpc blog-id username password wp) "Create a post on the blog." (xml-rpc-method-call xmlrpc 'wp.newPost blog-id username password (org-blog-post-to-wp wp)))
Hmmm. Looking back on this, I can see that I’m going to end up changing this at some point. I feel certain there are more direct, cleverer ways to implement this pattern—ones that would even allow me to pull out the open-coded XML-RPC call in
org-blog-wp-params. So I’ll bee looking into this, though I still want to get through the initial implementation first.