perhaps you somehow looked at outdated code, but we have Hexpm.Web.Plugs
defined in lib/hexpm/web/plugs.ex
[1] and so the module name perfectly matches the filename.
[1] https://github.com/hexpm/hexpm/blob/master/lib/hexpm/web/plugs.ex#L1
perhaps you somehow looked at outdated code, but we have Hexpm.Web.Plugs
defined in lib/hexpm/web/plugs.ex
[1] and so the module name perfectly matches the filename.
[1] https://github.com/hexpm/hexpm/blob/master/lib/hexpm/web/plugs.ex#L1
Yes, that is exactly what I pointed out. What makes Plugs
privileged so it doesnāt follow this new, odd structure (base namespace module defined in the folder for the namespace)? plugs.ex
should, according to this new idea, be placed in plugs/
.
I was pointing out that itās exactly where it should be, but not where this new convention says it should be.
This is assuming I havenāt misunderstood the new convention to only apply to lib/*
directories, in which case itās actually even worse, as itās not even internally consistent.
OK, I see what you mean, I didnāt make the connection that plugs.ex could indeed be in lib/hexpm/web/plugs/, thatās a good point. It does make sense to me to move it there, Iād also like to give this module a more meaningful name, but nothing pops up yet.
I was pointing out that itās exactly where it should be, but not where this new convention says it should be.
As was mentioned in this topic a couple of times (e.g. [1] [2]), Elixir doesnāt care where you place your files. If you donāt like the new structure nobody forces you to use it. Iād say, follow this convention as much or as little as you want.
To give a concrete example from this refactoring effort, I particularly like how e.g. Hexpm.CDN*
[3] modules are currently placed. Note this context is a bit different from e.g. Accounts context (which uses Repo, Schemas etc), but similar in a way that it represents a slice of the application. This set of modules is totally independent of Hex.pm, it could easily be a separate package, and so I quite like that all files are in the same directory. Does it break conventions in file structures compared to projects like Ecto, Poison etc? Yes. Does it make it easier for me to navigate Hex.pm project? Yes!
[1] Phoenix v1.3.0-rc.0 released - #44 by josevalim
[2] Phoenix v1.3.0-rc.0 released - #45 by rafal-radziszewski
[3] hexpm/lib/hexpm/cdn at main Ā· hexpm/hexpm Ā· GitHub
I see your point and Iāll have it in mind. I feel awkward about it now, though, because itāll still feel like Iām now going against convention. The community is likely to adapt one or the other and I usually need good reasons to break convention.
Anyway, thanks for the rationale and example. Itās something to keep in mind.
@chrismccord the way generators now put the changeset functions into context file and not in the schema itself breaks the contract expected by Ectoās cast_assoc
. Since cast_assoc
is almost always expected in any most changeset, having the changesets in context file requires users to define additional changesets in schema file just for the association or use the with
option in cast_assoc
. I think moving the changeset functions to schema can reduce the noise in context file and at the same time relive developers from defining changesets for associations.
Thatās a very good point we have not considered before. Can you please open up an issue so we are sure to discuss it?
To be honest, I personally consider cast_assoc
defaulting to Schema.changeset/2
a mistake - the with
option should be obligatory.
Iām interested in this topic as Iām developing a Phoenix 1.3 application as a learning tool to get comfortable with the conventions around contexts and API boundaries. Something Iāve noticed is that embedded schemas donāt quite fit into the pattern of code thatās generated by Phoenix. For example, create_<object>
doesnāt necessarily make sense for an embedded changeset (say, to validate fields for a login form).
This is also related to the topic in https://elixirforum.com/t/clarity-needed-on-phoenix-1-3-contexts-and-schema-in-controllers/4097/7. Since itās often required to generate an empty changeset, it would be good to have a function for that purpose automatically generated on the context module. @shankardevy suggests build_<object>/1
, which I find sensible.
Furthermore, I would agree that the private functions to generate changesets (<object>_changeset/2
) may be better positioned on the schemas themselves ā as this is most likely where developers will be implementing things like required fields, validation etc.
Overall, though, I think the changes are really great - so thank you for all your hard work!
Until we can implement strong AI (Phoenix 4.0? :-P), we wonāt be able to determine the perfect name for your functions. I agree that crud functions names arenāt always what you want, and I definitely recommend picking other function names that better describe your intent; however, you very well can model a login form from an embedded schema, then persist some other table-backed schema to the DB after validation. In that case, create_user
still works, but I would prefer something like Accounts.register_user
. create/update/delete/list/get
are starting points, but by all means use naming that better describes the intent of your features.
Iām still unsure on this one since change_<type>
already serves the purpose of generating an empty changeset. The only way build_whatever
would make sense is if it returned a %Whatever{}
struct, in which case it wouldnāt be necessary.
Thanks! Glad to hear it!
Iāve been using the new phx.gen
generators and I came across a use case that left me wondering if maybe I am not grokking the new context based directory layout correctly.
I want to have a User
schema in two contexts, Account
and Blog
, so I run:
mix phx.gen.html Accounts User users email:string
mix phx.gen.html Blog User users name:string account_id:references:accounts_users
The second generator invocation tries to overwrite the web_app
resource files created by the first generator invocation:
* creating lib/phx_sandbox/web/controllers/user_controller.ex
lib/phx_sandbox/web/controllers/user_controller.ex already exists, overwrite? [Yn]
Is that intentional?
Should web_app
resources also be name spaced, according to the context? (I must admit, that feels odd)
Or should there be a clean separation between context schemas and web_app
resources, so that the above generator invocation should not be allowed, requiring instead explicitly linking a resource to a context schema?
mix phx.gen.html Account User users email:string
# => Raises exception suggesting you do the following instead
mix phx.gen.context Account User users email:string
mix phx.gen.context Blog User users name:string account_id:references:accounts_users
# Creates CRUD scaffold linked to each contexts User
mix phx.gen.html Account User SignupController
mix phx.gen.html Blog User BlogUserController
How do we associate different contexts like say user has a account( Account context) and has many orders( Order context).
@wojtekmach I was wondering what your thoughts are regarding ānot respectingā the context boundary as explained on some of the replies above? For example, I see that in hexpm (https://github.com/hexpm/hexpm49) you donāt have an accounts.ex file and the controllers reach directly into modules within the context boundary (e.g. UserController reaches into Hexpm.Accounts.User which crosses the āAccountsā context boundary). It seems to me that the phoenix core team is trying to encourage the community to ārespect the context boundariesā but looking at hexpm Iām wondering if Iām missing something. Any thoughts you can share with me on this topic would be very appreciated.
@sabondano Iām very much in favor of creating and keeping boundaries in my code. To respond to your particular example, I believe youāre referring to line: public_email = User.email(user, :public)
in the controller [1]. If so, to be honest, Iām not too concerned about it because if you look into implementation of User.email/2
[2] youād see it operates on user.emails
field on the struct. I donāt want to draw the line whatās right and whatās wrong and itās something Iām still developing an intuition on. However, what Iām sure is Iād be much more concerned if the controller would directly operate on Repo or Queries regarding the User schema.
To give more context (or to make excuses :)) weāve introduced context modules several months before weāve made the switch to Phoenix v1.3 structure and itās still definitely rough around the edges. I think thereās still room for improvement and itās something I want to pursue.
[1] https://github.com/hexpm/hexpm/blob/0ce8169739d9baf061d6be5ba9e4940a7d0515b6/lib/hexpm/web/controllers/user_controller.ex#L11
[2] https://github.com/hexpm/hexpm/blob/0ce8169739d9baf061d6be5ba9e4940a7d0515b6/lib/hexpm/accounts/user.ex#L103
With the new configuration system:
on_init: {MyApp.Web.Endpoint, :load_from_system_env, []},
It seems we are forced to use IPv6 (https://github.com/phoenixframework/phoenix/blob/438b74255e7a4d68b4eaf1a295d0fcd201c71421/installer/templates/phx_web/endpoint.ex#L52).
If the old way to do config is deprecated (https://github.com/phoenixframework/phoenix/blob/da9f7653b9daf29a4c415be52a19ee6f4473e083/lib/phoenix/endpoint/handler.ex#L54), how should I configure the port to work if I donāt have IPv6?
You can remove :inet6
from the config. Change this:
{:ok, Keyword.put(config, :http, [:inet6, port: port])}
to this:
{:ok, Keyword.put(config, :http, [port: port])}
Oh, I didnāt pay attention. I didnāt see it was actually a template used to generate a file in my project.
Thank you!