/spite

Emacs Lisp REPL for interacting with web services

Primary LanguageEmacs Lisp

Spite

Spite is a REPL for interacting with web services inside Emacs.

Basic usage

Launch spite with M-x spite.

spite> (spite/req-url "https://api.github.com/repos/codahale/metrics")
((network_count . 394)
 (default_branch . "master")
 (master_branch . "master")
 (watchers . 1654)
 (open_issues . 13)
 (forks . 394)
 (open_issues_count . 13)
 (mirror_url)
 (forks_count . 394)
 (has_wiki . :json-false)
 (has_downloads . t)
 (has_issues . t)
 (language . "Java")
 (watchers_count . 1654)
 (size . 15715)
 (homepage . "http://github.com/codahale/metrics")
 (svn_url . "https://github.com/codahale/metrics")
 (clone_url . "https://github.com/codahale/metrics.git")
 (ssh_url . "git@github.com:codahale/metrics.git")
 (git_url . "git://github.com/codahale/metrics.git")
 (pushed_at . "2013-07-23T22:19:30Z")
 (updated_at . "2013-08-07T15:25:50Z")
 (created_at . "2010-02-26T19:44:42Z")
 (labels_url . "https://api.github.com/repos/codahale/metrics/labels{/name}")
 (notifications_url . "https://api.github.com/repos/codahale/metrics/notifications{?since,all,participating}")
 (milestones_url . "https://api.github.com/repos/codahale/metrics/milestones{/number}")
 (pulls_url . "https://api.github.com/repos/codahale/metrics/pulls{/number}")
 (issues_url . "https://api.github.com/repos/codahale/metrics/issues{/number}")
 (downloads_url . "https://api.github.com/repos/codahale/metrics/downloads")
 (archive_url . "https://api.github.com/repos/codahale/metrics/{archive_format}{/ref}")
 (merges_url . "https://api.github.com/repos/codahale/metrics/merges")
 (compare_url . "https://api.github.com/repos/codahale/metrics/compare/{base}...{head}")
 (contents_url . "https://api.github.com/repos/codahale/metrics/contents/{+path}")
 (issue_comment_url . "https://api.github.com/repos/codahale/metrics/issues/comments/{number}")
 (comments_url . "https://api.github.com/repos/codahale/metrics/comments{/number}")
 (git_commits_url . "https://api.github.com/repos/codahale/metrics/git/commits{/sha}")
 (commits_url . "https://api.github.com/repos/codahale/metrics/commits{/sha}")
 (subscription_url . "https://api.github.com/repos/codahale/metrics/subscription")
 (subscribers_url . "https://api.github.com/repos/codahale/metrics/subscribers")
 (contributors_url . "https://api.github.com/repos/codahale/metrics/contributors")
 (stargazers_url . "https://api.github.com/repos/codahale/metrics/stargazers")
 (languages_url . "https://api.github.com/repos/codahale/metrics/languages")
 (statuses_url . "https://api.github.com/repos/codahale/metrics/statuses/{sha}")
 (trees_url . "https://api.github.com/repos/codahale/metrics/git/trees{/sha}")
 (git_refs_url . "https://api.github.com/repos/codahale/metrics/git/refs{/sha}")
 (git_tags_url . "https://api.github.com/repos/codahale/metrics/git/tags{/sha}")
 (blobs_url . "https://api.github.com/repos/codahale/metrics/git/blobs{/sha}")
 (tags_url . "https://api.github.com/repos/codahale/metrics/tags")
 (branches_url . "https://api.github.com/repos/codahale/metrics/branches{/branch}")
 (assignees_url . "https://api.github.com/repos/codahale/metrics/assignees{/user}")
 (events_url . "https://api.github.com/repos/codahale/metrics/events")
 (issue_events_url . "https://api.github.com/repos/codahale/metrics/issues/events{/number}")
 (hooks_url . "https://api.github.com/repos/codahale/metrics/hooks")
 (teams_url . "https://api.github.com/repos/codahale/metrics/teams")
 (collaborators_url . "https://api.github.com/repos/codahale/metrics/collaborators{/collaborator}")
 (keys_url . "https://api.github.com/repos/codahale/metrics/keys{/key_id}")
 (forks_url . "https://api.github.com/repos/codahale/metrics/forks")
 (url . "https://api.github.com/repos/codahale/metrics")
 (fork . :json-false)
 (description . "Capturing JVM- and application-level metrics. So you know what's going on.")
 (html_url . "https://github.com/codahale/metrics")
 (private . :json-false)
 (owner
  (type . "User")
  (received_events_url . "https://api.github.com/users/codahale/received_events")
  (events_url . "https://api.github.com/users/codahale/events{/privacy}")
  (repos_url . "https://api.github.com/users/codahale/repos")
  (organizations_url . "https://api.github.com/users/codahale/orgs")
  (subscriptions_url . "https://api.github.com/users/codahale/subscriptions")
  (starred_url . "https://api.github.com/users/codahale/starred{/owner}{/repo}")
  (gists_url . "https://api.github.com/users/codahale/gists{/gist_id}")
  (following_url . "https://api.github.com/users/codahale/following{/other_user}")
  (followers_url . "https://api.github.com/users/codahale/followers")
  (html_url . "https://github.com/codahale")
  (url . "https://api.github.com/users/codahale")
  (gravatar_id . "87206f3bf53d403e16ec023c56e904c5")
  (avatar_url . "https://secure.gravatar.com/avatar/87206f3bf53d403e16ec023c56e904c5?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png")
  (id . 207)
  (login . "codahale"))
 (full_name . "codahale/metrics")
 (name . "metrics")
 (id . 537800))

spite>

Building requests

The spite-make-url function is the main way to build a request URL. Once built, you can use spite/req-url to fetch and display it.

The value of spite-base-url (which is buffer-local) is prepended to all output, making it easy to have multiple Spite instances targeting different APIs.

The first argument is a format string for building the URL, and the rest of the arguments are the values to substitute into it:

  (let ((user "ieure"))
    (spite-make-url "users/%s" user))
;; -> "users/ieure"

Quoted symbols and keywords are turned into strings. Normal symbols work the way you’d expect.

If you want to include query-string arguments, you may include an alist of them as the last argument:

(let ((user "ieure"))
  (spite-make-url "users" `((username ,user) (status active))))
;; -> "users?username=ieure&status=active"

The convenience function spite/req accepts the same arguments as spite-make-url, and sends the result to spite/req-url.

Response readers

Spite tries to read the response into a native elisp structure. It includes support for JSON and XML. If you request an image, it will be displayed inline in the Spite buffer.

New readers can be added in spite-response-readers, which is an alist of (pred-fun . reader-fun). The predicate function is evaluated in the HTTP response buffer, and if it returns a truthy value, reader=fun is called. This function is also evaluated in the context of the HTTP response buffer, and must return the forms to be displayed.

The parsed structures are then pretty-printed with spite-pp and inserted with spite-insert. You may need to hack these to support exotic types.

Custom REPLs

If you want a custom Spite tailored to a specific API, you can use defspite and write a few helper functions:

(defspite github "https://api.github.com/")
(defun github/repo (org repo)
  (spite/req "repos/%s/%s" org repo))

Then, you can launch it with M-x github, and do:

github> (github/repo 'codahale 'metrics)

A fuller example can be found in github-api.el.

Unfinished business

  • Support for OAuth would be nice.
  • Hook into nspace.el so you can prefix all your helper functions but avoid typing it.
  • Paren insertion if the forms lack them.