Weebo

Weebo is an XML-RPC parser/formatter for Elixir, with full data-type support.

Weebo can be combined with GenServer, Phoenix, HTTPoison (and others!) to create fully-featured XML-RPC clients & servers.

request = Weebo.parse("<?xml version=\"1.0\"?><methodCall><methodName>math.sum</methodName><params><param><value><int>1</int></value></param><param><value><int>2</int></value></param><param><value><int>3</int></value></param></params></methodCall>")
#=> %Weebo.Request{method: "math.sum", params: [1, 2, 3]}

sum = Enum.sum(request.params)
response = %Weebo.Response{error: nil, params: [sum]}
Weebo.format(response)
#=> "<?xml version=\"1.0\"?><methodResponse><params><param><value><int>6</int></value></param></params></methodResponse>"

Installation

Add Weebo to your mix dependencies:

def deps do
  [{:weebo, "~> 0.1"}]
end

Then run mix deps.get and mix deps.compile.

API Documentation

Full documentation is available on Hex &mdash; http://hexdocs.pm/weebo/

Data Type Mapping

All the following data-types are supported, and will be automatically serialized in format/1 and parse/1:

XMLRPC Elixir
<string> Bitstring "string"
<int> Integer 8
<boolean> Boolean true false
<double> Float 6.3
<array> List [1, 2, 3]
<struct> Map %{key: "value"}
<dateTime.iso8601> Tuple {{2015, 6, 7}, {16, 24, 18}}
<nil> Nil atom nil

In addition, the following extra data-types are supported only in parse/1:

XMLRPC Elixir
<base64> Bitstring "string" (will decode the base64 first)
<i4> Integer 8

Examples

Creating a client with HTTPoison

https://github.com/edgurgel/httpoison

For quick-n-dirty usage, you can just use Weebo directly encode/decode request/response bodies:

body = %Weebo.Request{method: "sample.sumAndDifference", params: [5, 3]}
       |> Weebo.format

res = HTTPoison.post!("http://some-wordpress-site.com/xmlrpc.php", body)

Weebo.parse(res.body)
#=> %Weebo.Response{error: nil, params: [%{difference: 2, sum: 8}]}

Or for more encapsulation, you can use HTTPoison's base wrapper to auto-parse & format requests:

defmodule XMLRPC do
  use HTTPoison.Base

  def process_request_body(body), do: Weebo.format(body)
  def process_response_body(body), do: Weebo.parse(body)
end

body = %Weebo.Request{method: "sample.sumAndDifference", params: [5, 3]}
res = XMLRPC.post!("http://some-wordpress-site.com/xmlrpc.php", body)
res.body
#=> %Weebo.Response{error: nil, params: [%{difference: 2, sum: 8}]}

You can then build out your XMLRPC module to handle more features like authentication, error handling, etc.

Creating a server with Phoenix

http://phoenixframework.org

Phoenix might be a little overkill for just a stand-alone XML-RPC server, but if you already have an existing application that you want to add XML-RPC support to, then you're in luck!

First add a route to your router.ex file:

post "/xmlrpc", XmlRpcController, :process

Then in your xml_rpc_controller.ex:

defmodule MyApp.XmlRpcController do
  use MyApp.Web, :controller

  def process(conn, _params) do
    {:ok, body, _conn} = read_body(conn)
    xml_req = Weebo.parse(body)

    res = case xml_req.method do
      "math.sum" ->
        sum = Enum.sum(xml_req.params)
        %Weebo.Response{params: [sum]}
      _ ->
        %Weebo.Response{error: %{faultCode: 404, faultString: "Not found."}}
    end

    render conn, Weebo.format(res)
  end
end

Used Weebo in another awesome way?

Please open an issue/pull-request on GitHub and I'll get it added to this README!

Acknowledgements

Thank you to @expelledboy for releasing their exml package. Although I wasn't able to use it directly, it served as a base for Weebo's internal XML interface.

Also thanks goes to @seansawyer for their awesome erlang_iso8601 library, which Weebo uses for timestamp parsing & formatting.