README.md

Weebo
=====

Weebo is an [XML-RPC](http://wikipedia.org/wiki/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.

```elixir
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:

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

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


## 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](https://github.com/edgurgel/httpoison)

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

```elixir
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](https://github.com/edgurgel/httpoison#wrapping-httpoisonbase)
to auto-parse & format requests:

```elixir
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](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:

```elixir
post "/xmlrpc", XmlRpcController, :process
```

Then in your `xml_rpc_controller.ex`:

```elixir
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](https://github.com/stevenschobert/weebo) on
GitHub and I'll get it added to this README!


## Acknowledgements

Thank you to [@expelledboy](https://github.com/expelledboy) for releasing their
[exml](https://github.com/expelledboy/exml) package. Although I wasn't able to
use it directly, it served as a base for Weebo's internal [XML interface](/lib/xml_interface.ex).

Also thanks goes to [@seansawyer](https://github.com/seansawyer) for their awesome
[erlang_iso8601](https://github.com/seansawyer/erlang_iso8601) library, which
Weebo uses for timestamp parsing & formatting.