Reputation: 111
I'm relatively new to the language, and have already read the differences between the require
and import
keywords. Nevertheless I can't seem to understand the following behavior while using Ecto.
So, I want to extract a fragment
that's commonly used to get a value from a json field. Reading the Ecto docs I understand that in order to extract a fragment
I have to wrap it on a dynamic
macro. So I'm trying to implement it like this:
defmodule Foo do
require Ecto.Query
...
defp foo_fragment() do
dynamic([t], fragment("?.json->>'foo'", t))
end
end
To which I get the following warning
variable "t" does not exist and is being expanded to "t()", please use parentheses to remove the ambiguity or change the variable name
Which seems reasonable enough, but isn't mentioned on the Ecto documentation. Looking for answers I found this SO question where is recommended to use import Ecto.Query
. I also noticed that it's used that way on the Ecto docs examples (probably for aesthetic reasons though).
To my surprise upon trying it, the warning on the previous code it's gone.
Is there something I'm missing regarding these keywords? I thought both were equivalent regarding the usage of macros, require
being a more conservative approach as to keeping namespaces apart. Thanks!
Upvotes: 2
Views: 384
Reputation: 3214
While import
is probably the most standard way to work with Ecto.Query
, you can perfectly use require
as well if you prefer to avoid bringing all its macros and functions into your module namespace.
All the following are equivalent and work without any warning:
defmodule FragmentImport do
import Ecto.Query
def foo_fragment() do
dynamic([t], fragment("?.json->>'foo'", t))
end
end
With require
, dynamic/2
will be undefined so you need to use the qualified name Ecto.Query.dynamic/2
:
defmodule FragmentRequire do
require Ecto.Query
def foo_fragment() do
Ecto.Query.dynamic([t], fragment("?.json->>'foo'", t))
end
end
If it feels too verbose, you can use the :as
option to alias the module as an intermediary solutions:
defmodule FragmentRequireAs do
require Ecto.Query, as: Query
def foo_fragment() do
Query.dynamic([t], fragment("?.json->>'foo'", t))
end
end
You can pick whichever style you prefer: require
can help removing the ambiguity to know where a macro/function comes from, but when working with Ecto people are usually familiar with it enough so there is little ambiguity in practice.
Upvotes: 5
Reputation: 9628
To answer the question in the title of your post, normally you only need to specify import Ecto.Query
at the top of a module that makes use of the Ecto
queries.
To clarify the info in the body of your post: the error you are getting has nothing directly to do with Ecto
or the differences between include
and require
: the error is telling you that the t
variable is not set, so the compiler is instead treating it as a function name (remember that parentheses are optional in Elixir, so treating it like t()
is a reasonable attempt at resolving it to a usable value).
Usually when I use fragment
, the variable I am using is passed into the function, e.g.:
defmodule Foo do
import Ecto.Query
def get(name) do
from u in User,
where: fragment("lower(username) = ?", ^String.downcase(name))
end
end
Upvotes: 2