Urania.ex
Efficient and elegant data access for Elixir.
NOTE: This is an experimental library until some issues with its underlying
execution model ([Pinky](https://github.com/codegram/pinky) promises) are fleshed out.
However, the public API is considered mostly stable, as the execution model is
completely separate from the semantics of constructing muses.
This is a one-to-one port of funcool/urania for Elixir.
A brief explanation blatantly stolen from Urania for Clojure’s original guide ensues:
Oftentimes, your business logic relies on remote data that you need to fetch
from different sources: databases, caches, web services, or third party APIs,
and you can’t mess things up. Urania helps you to keep your business logic clear
of low-level details while performing efficiently:
* batch multiple requests to the same data source
* request data from multiple data sources concurrently
* cache previous requests
Having all this gives you the ability to access remote data sources in a concise
and consistent way, while the library handles batching and overlapping requests
to multiple data sources behind the scenes.
Usage
First define your own data source:
defmodule MyHttpSource do
defstruct [:url, :params]
end
defimpl Urania.DataSource, for: MyHttpSource do
def identity(this) do
[this.url, this.params]
end
def fetch(this, env) do
# ... perform an actual HTTP request and return the result
end
end
Now you’re ready to construct muses, which are sort of like composable request
plans that will be carried out when you actually run the muses with Urania.run!
.
muse = [%MyHttpSource { url: "www.google.com" },
%MyHttpSource { url: "www.something.com"}]
|> Urania.collect # lay them out in parallel
|> Urania.flat_map(fn ([response_from_google response_from_something]) ->
if response_from_google[:foo] do
%MyHttpSource { url: "www.foo.com", params: response_from_google[:foo_params] }
else
Urania.value(Map.put(response_from_something, :good, :job))
end
end)
|> Urania.map(&validate_final_response/1)
# Whenever you actually want to run the request plan:
Urania.run!(muse)
{:ok, <response>}
Urania will take care of deduplicating requests and caching them within a single
run. If you implement the BatchedSource
protocol for your data source in
addition to DataSource
, Urania will batch all your parallel requests into a
single one by calling the fetch_multi
function you need to satisfy:
defimpl Urania.BatchedSource, for: MyHttpSource do
def fetch_multi(request, more_requests, env) do
# batch together request and more_requests into a single one.
# this function MUST return a map from request `identity`s to responses.
end
end
Make sure to check out Urania for Clojure’s original guide for deeper
understanding. The only difference is that flat_map
in Urania.ex is mapcat
in Clojure Urania. Everything else should have the same names and semantics.
Installation
Add
urania
to your list of dependencies inmix.exs
:def deps do [{:urania, "~> 0.1.0"}] end
Documentation
Check out the API documentation for detailed examples of each of the Urania primitives.
Acknowledgements
Urania for Clojure is an awesome project: kudos to its authors and maintainers.