Код.ensure_loaded? в .iex.exs

У меня есть конфигурация консоли эликсира, хранящаяся в .iex.exs:

if Code.ensure_loaded?(MyApp.Repo) do
  alias MyApp.Repo
end

Я хочу иметь возможность запускать как iex, так и iex -S mix. У меня будет исключение, если я удалю условие на iex.

Но эти условия не работают хорошо! Даже на iex -S mix у меня возникает ошибка (module Repo is not available), если я пытаюсь вызвать Repo.get(...). Итак, мои вопросы:

  1. Почему Code.ensure_loaded? здесь не работает?
  2. Как я могу это исправить?

person asiniy    schedule 15.09.2016    source источник
comment
(1) потому что alias является лексическим и действует только внутри этого if. Насчет (2) не уверен.   -  person Dogbert    schedule 15.09.2016


Ответы (1)


Это вопрос охвата. Внутри блока у вас есть этот псевдоним:

if Code.ensure_loaded?(MyApp.Repo) do
  alias MyApp.Repo
  Repo.get(...) #⇒ available
end

Чтобы определить alias IEx-широкий, вы должны вызвать его вне любого блока:

alias MyApp.Repo

Вам не нужен условный if Code.ensure_loaded?(MyApp.Repo), когда IEx выполняется с iex -S mix, все зависимости будут загружены для вас автоматически. Для чистого iex это можно сделать более громоздким способом:

defmodule FileExt do
  def ls_all(dir, acc \\ []) do
    case File.ls(dir) do
      {:ok, list} -> list |> Enum.reduce(acc, fn f, acc ->
          fullname = dir <> "/" <> f
          if fullname |> File.dir?, do: ls_all(fullname, acc), else: acc ++ [fullname]
        end)
      {:error, e} ->
        IO.puts "Unable to list. Reason: #{e}"
        acc
    end
  end

  def require_lib do
    try do
      "lib" |> FileExt.ls_all |> Kernel.ParallelRequire.files
    rescue
      e in UndefinedFunctionError -> Code.ensure_loaded?(MyApp.Repo)
    end
  end
end

try do
  MyApp.Repo.get
rescue
  e in UndefinedFunctionError -> FileExt.require_lib
end

alias MyApp.Repo

Приведенное выше загрузит все файлы из каталога "lib".

Хотя я бы заткнулся здесь перфекционизмом и ездил бы с iex -S mix всегда, без проверки:

Code.ensure_loaded?(MyApp.Repo)
alias MyApp.Repo
person Aleksei Matiushkin    schedule 15.09.2016
comment
Я не думаю, что это решает требование, которое я хочу иметь для запуска как iex, так и iex -S mix. - person Dogbert; 15.09.2016
comment
@Dogbert действительно, я обновил ответ, чтобы явно загрузить "lib" dir. - person Aleksei Matiushkin; 15.09.2016
comment
@mudasobwa выглядит хорошо, но я получил много предупреждений, например warning: redefining module MyApp.Module. Как я могу это решить? - person asiniy; 19.09.2016
comment
Вы получили эти предупреждения при использовании FileExt.ls_all |> Kernel.ParallelRequire.files с iex -S mix, верно? Очевидно, это тот случай, потому что iex -S mix заботится о загрузке всего из самого "lib". Проверьте, определен ли Repo заранее, чтобы различать поведение между чистыми iex и iex -S mix: try do MyApp.Repo.get rescue e in UndefinedFunctionError -> LOAD_ALL_THIS_STUFF end. - person Aleksei Matiushkin; 19.09.2016
comment
@mudasobwa Я все еще вижу эти предупреждения; просто скопировал ваш код и заменил MyApp именем моего приложения - person asiniy; 21.09.2016
comment
MyApp.Repo.get/0 должен существовать с нулевой арностью, чтобы этот код работал. Я, очевидно, не могу угадать функцию real, которую вы бы вызвали, чтобы проверить, был ли уже загружен код или нет. Я думал, вы уловили суть, извините. Внутри try вызовите определенно существующую функцию, чтобы проверить, был ли загружен код. - person Aleksei Matiushkin; 21.09.2016