Multiple Repo's and how to select them dynamically given a string?

ecto

#1

I have mulitple Repo’s.

# 1
use Mix.Config

config :my_app, MyApp.OneRepo,
  adapter: Ecto.Adapters.MySQL,
  database: "legacy_db",
  username: "username",
  password: "password",
  hostname: "something.com"

config :my_app, MyApp.TwoRepo,
  adapter: Ecto.Adapters.Postgres,
  username: "username",
  password: "password",
  database: "some_db_two",
  hostname: "example.com"

config :my_app, ecto_repos: [MyApp.OneRepo, MyApp.TwoRepo]

# 2

defmodule Databases do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      supervisor(MyApp.OneRepo, []), # <-- our addition
      supervisor(MyApp.TwoRepo, [])     # <-- our addition
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

# 3 [............]

In my routes I have a string corresponding to a Repo.

I can do this statically;

some_table= MyApp.OneRepo.get_by(SomeTable, %{username: "Alpha"})

other_table= MyApp.TwoRepo.all from ot in OtherTable,
             where: ot.user_id == ^user.id

How can I do this dynamically, depending on a parameter in a URL? That is, if a URL is "/repos/repo1/articles"

def my_action(conn, params) do
  rp_name = params["repo_name"] # repo1


  repo = get_repo_from_url_param(rp_name) # ???? how to return MyApp.{RepoOne | RepoTwo | RepoN} ???
 
  # ??? some_table = MyApp.?? repo ?? .get_by(SomeTable, %{username: "Alpha"})

end

Assume that all the Repo’s have the identical database schemas.

All the Repo’s of my app are declared in the config file - statically.


#2

The safe way is just a dispatch lookup, either something like string -> Repo mapping in function heads, or iterate the registered repo’s and compare their string value (sans “Elixir.” prefix) to the params, etc… etc… There are tons and tons of style useful depending on what security you need, how trustworthy the incoming parameter is (probably isn’t), etc… etc… :slight_smile:


#3

show me an example


#4
def repo_getter("repo1"), do: MyApp.OneRepo
def repo_getter("repo2"), do: MyApp.TwoRepo

And then elsewhere, like in your example:

repo = repo_getter(rp_name)
repo.all(...)

#5

Interesting. What’s the type of “repo_getter()” ?


#6

What you get back is technically an atom. So MyApp.OneRepo is equivalent to :"Elixir.MyApp.OneRepo":

iex(1)> MyApp.OneRepo == :"Elixir.MyApp.OneRepo" 
true
iex(2)> List == :"Elixir.List"
true

This article explains a little bit more: https://medium.com/@tylerpachal/why-do-erlang-modules-look-like-atoms-in-elixir-9fb2f964dd2b