Phoenix v1.3.0-rc.0 released

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

1 Like

@chrismccord What do you think about this idea?

1 Like

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.

1 Like

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
[2] Phoenix v1.3.0-rc.0 released
[3] https://github.com/hexpm/hexpm/tree/master/lib/hexpm/cdn

3 Likes

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.

2 Likes

@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.

2 Likes

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?

1 Like

To be honest, I personally consider cast_assoc defaulting to Schema.changeset/2 a mistake - the with option should be obligatory.

4 Likes

Opened an issue here: https://github.com/phoenixframework/phoenix/issues/2180

1 Like

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!

2 Likes

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!

3 Likes

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
4 Likes

How do we associate different contexts like say user has a account( Account context) and has many orders( Order context).

2 Likes

@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. :slight_smile:

1 Like

@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

2 Likes

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!