terenceponce
I’m trying to write a @spec for this function that was made by mix phx.gen.auth:
# lib/my_app/identity/user_token.ex
@spec build_session_token(MyApp.Identity.User.t()) :: {binary(), t()}
def build_session_token(user) do
token = :crypto.strong_rand_bytes(@rand_size)
{token, %UserToken{token: token, context: "session", user_id: user.id}}
end
Unfortunately, I’m getting this error from Dialyxir:
Invalid type specification for function 'Elixir.MyApp.Identity.UserToken':build_session_token/1.
The success typing is 'Elixir.MyApp.Identity.UserToken':build_session_token(atom() | #{'id':=_, _=>_}) -> {binary(),#{'__meta__':=#{'__struct__':='Elixir.Ecto.Schema.Metadata', 'context':='nil', 'prefix':='nil', 'schema':='Elixir.MyApp.Identity.UserToken', 'source':=<<_:96>>, 'state':='built'}, '__struct__':='Elixir.MyApp.Identity.UserToken', 'context':=<<_:56>>, 'id':='nil', 'inserted_at':='nil', 'sent_to':='nil', 'token':=binary(), 'user':=#{'__cardinality__':='one', '__field__':='user', '__owner__':='Elixir.MyApp.Identity.UserToken', '__struct__':='Elixir.Ecto.Association.NotLoaded'}, 'user_id':=_}}
But the spec is 'Elixir.MyApp.Identity.UserToken':build_session_token('Elixir.MyApp.Identity.User':t()) -> {binary(),t()}
The return types do not overlap
I think it looks correct, so I’m confused why I’m getting this error. What should I do to fix this?
EDIT:
# lib/my_app/identity/user_token.ex
defmodule MyApp.Identity.UserToken do
@type t :: %__MODULE__{
__meta__: Ecto.Schema.Metadata.t(),
id: integer() | nil,
token: binary(),
context: String.t(),
sent_to: String.t(),
user_id: integer(),
inserted_at: NaiveDateTime.t() | nil
}
schema "users_tokens" do
# ...
end
end
# lib/my_app/identity/user.ex
defmodule MyApp.Identity.User do
@type t :: %__MODULE__{
__meta__: Ecto.Schema.Metadata.t(),
id: integer() | nil,
email: String.t() | nil,
password: String.t() | nil,
hashed_password: String.t() | nil,
confirmed_at: NaiveDateTime.t() | nil,
inserted_at: NaiveDateTime.t() | nil,
updated_at: NaiveDateTime.t() | nil
}
schema "users" do
# ...
end
end
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted”
Version...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 7- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
sodapopcan
Ecto doesn’t generate typespecs, you have to do that yourself or use a library to auto-generate them. I can’t remember the name of the lib but it’s probably pretty easily found!
EDIT:
Oh, I dunno then
terenceponce
Sorry, I forgot to include the typespecs that I have. I actually have typespecs for my Ecto schemas. I’ve edited them into my main post.
sbuttgereit
In the
UserTokentypespec I seeBut in the function I see the return value:
%UserToken{token: token, context: "session", user_id: user.id}If that means that
sent_tois inferred to benilin the return type, then it doesn’t match the spec that says it must be a string. I’ve only glanced through this, so its possible that I’ve missed a detail, but that’s the one thing I see that doesn’t look quite right.terenceponce
That fixed it. Thank you!
al2o3cr
Some issues with this typespec relative to the code in
build_session_token/1:as @sbuttgereit called out,
sent_toisn’t able to benilso writing%UserToken{...}withoutsent_toisn’t going to produce aUserToken.t()user.idis specced asinteger() | nil, but it’s assigned touser_idwhich is onlyinteger().sbuttgereit
I have to admit… when I’m typespecing Ecto.Schema structs I set all the fields, with the exception of
:__meta__, as typed:<some type> | nilwithout any further consideration.I end up doing this for two reasons. The first is there are enough intermediate states for the data on its way to being complete and database ready that any attempt to represent the “completed state” of the data in the struct typespec just ends up with the kinds of errors reported here… and having to sort through and trying to reconcile the additional issues you found with the examples in the original question. The second reason is that avoiding the mental overhead of figuring out workable non-nil states has a significantly higher payoff than actually finding those edges where intermediate states could be finessed for Dialyzer.
For really keeping the data integral I’m depending on the database itself anyway. Yeah, that’s runtime and not compile time, but I think that’s the best to be done since the intermediate state problem means really typing these Schema structs more strictly means typing them in a way that’s not going to match the database requirements anyway… its just not feasible (that I can see).
dimitarvp
Yeah I fully agree with you here, I do the same 99% of the time. Elixir has no static typing and as you said Ecto is basically a runtime concern, not a compile-time one, so I never even put any specs on anything Ecto-related. There’s really no point. You might catch a bug twice a year but you have spent who knows how many hours adding those specs.