You can run some custom code when iex starts up by placing code in .iex.exs. I like to use this to add some per application conveniences. For example, if an app uses Ecto, it’s pretty nice to toss in a:
import Ecto.Query, only: [from: 2]
This makes it so you can call from/2 without a module prefix when tooling around in iex -S mix. The downside is that it’s now impossible to launch a bare iex in that directory. (I sometimes forget to add -S mix when I’m quickly trying experimenting with a core Elixir function.) It will toss a compile error when it can’t find Ecto.
This leads me to two questions:
Can anyone come up with a viable workaround? I’ve tried a few things that all failed.
Does anyone else think iex should just warn and launch without the customizations in this case? I can always create a patch if there’s interest.
That’s not true. Just import and require are throwing compile time errors. This check standalone is good. Inside if you can call code like Code.require_file/1 which would not raise even if you call iex and don’t have target file.
if Code.ensure_loaded?(MyApp) do
Code.require_file(".iex.project.exs")
end
# code not strictly related to project goes here …
$ cat .iex.exs
if Code.ensure_loaded?(Ecto) do
import Ecto.Query, only: [from: 2]
end
$ iex
Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help)
Error while evaluating: /Users/jeg2/Documents/icanmakeitbetter/icmu/.iex.exs
** (CompileError) .iex.exs:2: module Ecto.Query is not loaded and could not be found
(stdlib) lists.erl:1354: :lists.mapfoldl/3
(stdlib) lists.erl:1355: :lists.mapfoldl/3
(stdlib) lists.erl:1354: :lists.mapfoldl/3
(elixir) expanding macro: Kernel.if/2
Your second suggestion introduces a new scope, so you are no longer modifying the iex shell. Here’s the error from that approach:
$ cat .iex.exs
if Code.ensure_loaded?(Ecto) do
Code.require_file(".iex.project.exs")
end
$ cat .iex.project.exs
import Ecto.Query, only: [from: 2]
$ iex
Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
a
$ iex -S mix
Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help)
warning: unused import Ecto.Query
.iex.project.exs:1
iex(1)> from(r in Reporter.Response, limit: 1)
** (CompileError) iex:1: undefined function from/2
iex(1)>
It’s a total workaround, but in my current system at the root level we have an .iex.exs file something like this (SC is short for “shortcuts”):
defmodule SC do
defmacro __using__(_) do
import Ecto, only: [from: 2]
end
end
So we settle for typing use SC in iex.
You could take it a step further and pass a parameter to use and switch on that inside the macro to do different things for different people, or for different subsections of the app, etc. But then it becomes a bit more cumbersome to type use SC, :db
heh, once again I forgot that iex works a bit differently - my bad
I though similarly to @gregvaughn’s suggestion (macro under my check), but I though that it (defining macro) was a bit too tricky for such simple case.
The best what I had in mind was to define lib/my_app/iex.ex with its own macro (again similarly to @gregvaughn’s suggestion) which could be simply called only if -S option is set (as any other call i.e. MyApp.IEx.prepare()), but @wojtekmach’s solution wins without any need for further discussion! Nice find!
I did not looked so deeply in IEx application documentation (to be honest I did not even expected such API there) - looks like a perfect time to change that.