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
.
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 org-blog-call
).
(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.