Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vim-dadbod integration for applications using Ecto #481

Merged
merged 2 commits into from Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -11,6 +11,8 @@ Features:
* Syntax highlighting for Elixir and EEx files
* Filetype detection for `.ex`, `.exs` and `.eex` files
* Automatic indentation
* Integration between Ecto projects and [vim-dadbod][] for running SQL queries
on defined Ecto repositories

## Installation

Expand Down Expand Up @@ -54,3 +56,5 @@ We've decided not to include `mix format` integration into `vim-elixir`. If you'

Run the tests: `bundle exec parallel_rspec spec`
Spawn a vim instance with dev configs: `bin/spawn_vim`

[vim-dadbod]: https://github.com/tpope/vim-dadbod
20 changes: 20 additions & 0 deletions autoload/db/adapter/ecto.vim
@@ -0,0 +1,20 @@
let s:path = expand('<sfile>:h')
let s:cmd = join(['mix', 'run', '--no-start', '--no-compile', shellescape(s:path.'/get_repos.exs')])

function! s:repo_list() abort
return map(systemlist(s:cmd), 'split(v:val)')
endfunction

function! db#adapter#ecto#canonicalize(url) abort
for l:item in s:repo_list()
let l:name = get(l:item, 0)
let l:url = get(l:item, 1)
if !empty(l:name) && 'ecto:'.l:name ==# a:url
return l:url
endif
endfor
endfunction

function! db#adapter#ecto#complete_opaque(url) abort
return map(s:repo_list(), 'v:val[0]')
endfunction
54 changes: 54 additions & 0 deletions autoload/db/adapter/get_repos.exs
@@ -0,0 +1,54 @@
defmodule LoadRepos do
defp load_apps do
:code.get_path()
|> Enum.flat_map(fn app_dir ->
Path.join(app_dir, "*.app") |> Path.wildcard()
end)
|> Enum.map(fn app_file ->
app_file |> Path.basename() |> Path.rootname(".app") |> String.to_atom()
end)
|> Enum.map(&Application.load/1)
end

defp configs do
for {app, _, _} <- Application.loaded_applications(),
repos = Application.get_env(app, :ecto_repos),
is_list(repos) and repos != [],
repo <- repos,
do: {repo, Map.new(Application.get_env(app, repo))}
end

defp config_to_url(_, %{url: url}), do: url

defp config_to_url(repo, %{
hostname: hostname,
username: username,
password: password,
database: database
}) do
%URI{
scheme: adapter_to_string(repo.__adapter__),
userinfo: "#{username}:#{password}",
host: hostname,
path: Path.join("/", database)
}
|> URI.to_string()
end

defp adapter_to_string(Ecto.Adapters.Postgres), do: "postgres"
defp adapter_to_string(Ecto.Adapters.MySQL), do: "mysql"
defp adapter_to_string(mod), do: raise("Unknown adapter #{inspect(mod)}")

def main do
load_apps()

configs()
|> Enum.map(fn {repo, config} ->
[inspect(repo), ?\s, config_to_url(repo, config)]
end)
|> Enum.intersperse(?\n)
|> IO.puts()
end
end

LoadRepos.main()