It depends. It might be easier if you explain the specific thing you are trying to achieve.
For example, if you do want to do something with all ecto schemas, a better approach might be to wrap the use Ecto.Schema with your own macro like use MyApp.Schema. That will give you a seam to be able to do whatever you need before calling Ecto’s usual schema stuff.
But it really depends what you atually want to achieve in the end
Either have them all be in a certain namespace e.g. YourApp.Schemas. or do as @D4no0 recommends: look for the functions that use Ecto.Schema injects in the modules. I’d go for the latter, it’s more reliable, though a bit slower to scan and find them all (but likely not by a lot).
AFAIK there’s no explicit “this module said use Ecto.Schema” indication; you could produce a module that behaves exactly the same way without the macros at all, by manually writing all the needed clauses of the __schema__/1 function:
Another key bit of info: do you want to know this at compile time or runtime?
If at compile time, you need to do what @Adzz is talking about. If at runtime, then what @D4no0/@dimitarvp said, though from a design perspective this is less than ideal, though in some situations it could probably be ok.
imported functions just get rewritten to their fully qualified versions when compiled so, as @al2o3cr said, there’s no (sane) way of knowing that a module definition ever declared any imports.
Thanks guys, managed to get something working like this:
defmodule YourApp.SchemaDiscover do
def has_schema_and_changeset?(module) do
functions = module.__info__(:functions)
Enum.any?(functions, fn {name, _} -> name in [:__schema__, :__changeset__] end)
end
def get_modules_using_ecto_schema() do
with {:ok, list} <- :application.get_key(:yourapp, :modules) do
list
|> Enum.filter(fn module ->
not String.contains?(inspect(module), "YourAppWeb") # ignores Web folder
end)
|> Enum.filter(&has_schema_and_changeset?/1)
end
end
def list_module_fields(schema_module) do
for field <- schema_module.__schema__(:fields) do
{field, schema_module.__schema__(:type, field)}
end
end
def get_modules_and_fields() do
modules = get_modules_using_ecto_schema()
Enum.map(modules, fn module ->
{module, list_module_fields(module)}
end)
end
end
Do not rely on inspect for programmatic logic. It can be overridden to output something entirely different than what you’d expect. For reference: the Inspect protocol.