From c572d2e29fa6747a21fb35f8d4a83993314a81f7 Mon Sep 17 00:00:00 2001 From: Chris McCord Date: Tue, 15 Jan 2019 00:41:06 -0500 Subject: [PATCH] Drop special convention for context locations The original intent was to highlight the context file as the interface to the code contained within its directory. While a worthwhile pursuit, the convention has caused more confusion than it has helped. At the same time, we can achieve our interface goals with existing guides to drive best practices. --- guides/contexts.md | 28 ++++++++++++------------- lib/mix/phoenix/context.ex | 2 +- test/mix/tasks/phx.gen.context_test.exs | 14 ++++++------- test/mix/tasks/phx.gen.html_test.exs | 10 ++++----- test/mix/tasks/phx.gen.json_test.exs | 10 ++++----- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/guides/contexts.md b/guides/contexts.md index d605c9ce39..eb4a26eaf9 100644 --- a/guides/contexts.md +++ b/guides/contexts.md @@ -59,8 +59,8 @@ username:string:unique * creating test/hello_web/controllers/user_controller_test.exs * creating lib/hello/accounts/user.ex * creating priv/repo/migrations/20170629175236_create_users.exs -* creating lib/hello/accounts/accounts.ex -* injecting lib/hello/accounts/accounts.ex +* creating lib/hello/accounts.ex +* injecting lib/hello/accounts.ex * creating test/hello/accounts/accounts_test.exs * injecting test/hello/accounts/accounts_test.exs @@ -75,7 +75,7 @@ Remember to update your repository by running migrations: ``` -Phoenix generated the web files as expected in `lib/hello_web/`. We can also see our context files were generated inside a `lib/hello/accounts/` directory. Note the difference between `lib/hello` and `lib/hello_web`. We have an `Accounts` module to serve as the public API for account functionality, as well as an `Accounts.User` struct, which is an Ecto schema for casting and validating user account data. Phoenix also provided web and context tests for us, which we'll look at later. For now, let's follow the instructions and add the route according to the console instructions, in `lib/hello_web/router.ex`: +Phoenix generated the web files as expected in `lib/hello_web/`. We can also see our context files were generated inside a `lib/hello/accounts.ex` file and our user schema in the directory of the same name. Note the difference between `lib/hello` and `lib/hello_web`. We have an `Accounts` module to serve as the public API for account functionality, as well as an `Accounts.User` struct, which is an Ecto schema for casting and validating user account data. Phoenix also provided web and context tests for us, which we'll look at later. For now, let's follow the instructions and add the route according to the console instructions, in `lib/hello_web/router.ex`: ```elixir scope "/", HelloWeb do @@ -160,7 +160,7 @@ We've seen how controllers work in our [controller guide](controllers.html), so In the case of our `create` action, when we successfully create a user, we use `Phoenix.Controller.put_flash/3` to show a success message, and then we redirect to the `user_path`'s show page. Conversely, if `Accounts.create_user/1` fails, we render our `"new.html"` template and pass along the Ecto changeset for the template to lift error messages from. -Next, let's dig deeper and check out our `Accounts` context in `lib/hello/accounts/accounts.ex`: +Next, let's dig deeper and check out our `Accounts` context in `lib/hello/accounts.ex`: ```elixir defmodule Hello.Accounts do @@ -257,7 +257,7 @@ email:string:unique user_id:references:users * creating lib/hello/accounts/credential.ex * creating priv/repo/migrations/20170629180555_create_credentials.exs -* injecting lib/hello/accounts/accounts.ex +* injecting lib/hello/accounts.ex * injecting test/hello/accounts/accounts_test.exs Remember to update your repository by running migrations: @@ -267,7 +267,7 @@ Remember to update your repository by running migrations: This time around, we used the `phx.gen.context` task, which is just like `phx.gen.html`, except it doesn't generate the web files for us. Since we already have controllers and templates for managing users, we can integrate the new credential features into our existing web form. -We can see from the output that Phoenix generated an `accounts/credential.ex` file for our `Accounts.Credential` schema, as well as a migration. Notably, phoenix said it was `* injecting` code into the existing `accounts/accounts.ex` context file and test file. Since our `Accounts` module already exists, Phoenix knows to inject our code here. +We can see from the output that Phoenix generated an `accounts/credential.ex` file for our `Accounts.Credential` schema, as well as a migration. Notably, phoenix said it was `* injecting` code into the existing `accounts.ex` context file and test file. Since our `Accounts` module already exists, Phoenix knows to inject our code here. Before we run our migrations, we need to make one change to the generated migration to enforce data integrity of user account credentials. In our case, we want a user's credentials to be deleted when the parent user is removed. Make the following change to your `*_create_credentials.exs` migration file in `priv/repo/migrations/`: @@ -342,7 +342,7 @@ We used `Ecto.Schema`'s `has_one` macro to let Ecto know how to associate our pa ``` -We used the `belongs_to` macro to map our child relationship to the parent `User`. With our schema associations set up, let's open up `accounts/accounts.ex` and make the following changes to the generated `list_users` and `get_user!` functions: +We used the `belongs_to` macro to map our child relationship to the parent `User`. With our schema associations set up, let's open up `accounts.ex` and make the following changes to the generated `list_users` and `get_user!` functions: ```elixir def list_users do @@ -437,7 +437,7 @@ To start, let's think of a function name that describes what we want to accompli > user = Accounts.authenticate_by_email_password(email, password) -That looks nice. A descriptive name that exposes the intent of our code is best. This function makes it crystal clear what purpose it serves, while allowing our caller to remain blissfully unaware of the internal details. Make the following additions to your `lib/hello/accounts/accounts.ex` file: +That looks nice. A descriptive name that exposes the intent of our code is best. This function makes it crystal clear what purpose it serves, while allowing our caller to remain blissfully unaware of the internal details. Make the following additions to your `lib/hello/accounts.ex` file: ```elixir def authenticate_by_email_password(email, _password) do @@ -596,8 +596,8 @@ views:integer --web CMS * creating test/hello_web/controllers/cms/page_controller_test.exs * creating lib/hello/cms/page.ex * creating priv/repo/migrations/20170629195946_create_pages.exs -* creating lib/hello/cms/cms.ex -* injecting lib/hello/cms/cms.ex +* creating lib/hello/cms.ex +* injecting lib/hello/cms.ex * creating test/hello/cms/cms_test.exs * injecting test/hello/cms/cms_test.exs @@ -686,7 +686,7 @@ genre:string user_id:references:users:unique * creating lib/hello/cms/author.ex * creating priv/repo/migrations/20170629200937_create_authors.exs -* injecting lib/hello/cms/cms.ex +* injecting lib/hello/cms.ex * injecting test/hello/cms/cms_test.exs Remember to update your repository by running migrations: @@ -810,7 +810,7 @@ Next, let's add the association in the other direction in `lib/hello/cms/author. We added the `has_many` association for author pages, and then introduced our data dependency on the `Accounts` context by wiring up the `belongs_to` association to our `Accounts.User` schema. -With our associations in place, let's update our `CMS` context to require an author when creating or updating a page. We'll start off with data fetching changes. Open up your `CMS` context in `lib/hello/cms/cms.ex` and replace the `list_pages/0`, `get_page!/1`, and `get_author!/1` functions with the following definitions: +With our associations in place, let's update our `CMS` context to require an author when creating or updating a page. We'll start off with data fetching changes. Open up your `CMS` context in `lib/hello/cms.ex` and replace the `list_pages/0`, `get_page!/1`, and `get_author!/1` functions with the following definitions: ```elixir alias Hello.CMS.{Page, Author} @@ -837,7 +837,7 @@ With our associations in place, let's update our `CMS` context to require an aut We started by rewriting the `list_pages/0` function to preload the associated author, user, and credential data from the database. Next, we rewrote `get_page!/1` and `get_author!/1` to also preload the necessary data. -With our data access functions in place, let's turn our focus towards persistence. We can fetch authors alongside pages, but we haven't yet allowed authors to be persisted when we create or edit pages. Let's fix that. Open up `lib/hello/cms/cms.ex` and make the following changes: +With our data access functions in place, let's turn our focus towards persistence. We can fetch authors alongside pages, but we haven't yet allowed authors to be persisted when we create or edit pages. Let's fix that. Open up `lib/hello/cms.ex` and make the following changes: ```elixir @@ -1015,7 +1015,7 @@ Again, let's think of a function name that describes what we want to accomplish. That looks great. Our callers will have no confusion over what this function does and we can wrap up the increment in an atomic operation to prevent race conditions. -Open up your CMS context (`lib/hello/cms/cms.ex`), and add this new function: +Open up your CMS context (`lib/hello/cms.ex`), and add this new function: ```elixir diff --git a/lib/mix/phoenix/context.ex b/lib/mix/phoenix/context.ex index e9fa61d3e9..ffc45bd42f 100644 --- a/lib/mix/phoenix/context.ex +++ b/lib/mix/phoenix/context.ex @@ -29,8 +29,8 @@ defmodule Mix.Phoenix.Context do basedir = Phoenix.Naming.underscore(context_name) basename = Path.basename(basedir) dir = Mix.Phoenix.context_lib_path(ctx_app, basedir) + file = dir <> ".ex" test_dir = Mix.Phoenix.context_test_path(ctx_app, basedir) - file = Path.join([dir, basename <> ".ex"]) test_file = Path.join([test_dir, basename <> "_test.exs"]) generate? = Keyword.get(opts, :context, true) diff --git a/test/mix/tasks/phx.gen.context_test.exs b/test/mix/tasks/phx.gen.context_test.exs index 4fd82ecf43..8ca5a4d71e 100644 --- a/test/mix/tasks/phx.gen.context_test.exs +++ b/test/mix/tasks/phx.gen.context_test.exs @@ -35,7 +35,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do }} = context assert String.ends_with?(context.dir, "lib/phoenix/blog") - assert String.ends_with?(context.file, "lib/phoenix/blog/blog.ex") + assert String.ends_with?(context.file, "lib/phoenix/blog.ex") assert String.ends_with?(context.test_file, "test/phoenix/blog/blog_test.exs") assert String.ends_with?(context.schema.file, "lib/phoenix/blog/post.ex") end @@ -62,7 +62,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do }} = context assert String.ends_with?(context.dir, "lib/phoenix/site/blog") - assert String.ends_with?(context.file, "lib/phoenix/site/blog/blog.ex") + assert String.ends_with?(context.file, "lib/phoenix/site/blog.ex") assert String.ends_with?(context.test_file, "test/phoenix/site/blog/blog_test.exs") assert String.ends_with?(context.schema.file, "lib/phoenix/site/blog/post.ex") end @@ -71,7 +71,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do test "new existing context", config do in_tmp_project config.test, fn -> File.mkdir_p!("lib/phoenix/blog") - File.write!("lib/phoenix/blog/blog.ex", """ + File.write!("lib/phoenix/blog.ex", """ defmodule Phoenix.Blog do end """) @@ -122,7 +122,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do assert file =~ "field :title, :string" end - assert_file "lib/phoenix/blog/blog.ex", fn file -> + assert_file "lib/phoenix/blog.ex", fn file -> assert file =~ "def get_post!" assert file =~ "def list_posts" assert file =~ "def create_post" @@ -149,7 +149,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do Gen.Context.run(~w(Blog Comment comments title:string)) assert_received {:mix_shell, :info, ["You are generating into an existing context" <> notice]} - assert notice =~ "Phoenix.Blog context currently has 6 functions and 2 files in its directory" + assert notice =~ "Phoenix.Blog context currently has 6 functions and 1 files in its directory" assert_received {:mix_shell, :yes?, ["Would you like to proceed?"]} assert_file "lib/phoenix/blog/comment.ex", fn file -> @@ -168,7 +168,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do assert file =~ "add :title, :string" end - assert_file "lib/phoenix/blog/blog.ex", fn file -> + assert_file "lib/phoenix/blog.ex", fn file -> assert file =~ "def get_comment!" assert file =~ "def list_comments" assert file =~ "def create_comment" @@ -185,7 +185,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do refute_file "lib/phoenix/blog/post.ex" - assert_file "lib/phoenix/blog/blog.ex", fn file -> + assert_file "lib/phoenix/blog.ex", fn file -> assert file =~ "def get_post!" assert file =~ "def list_posts" assert file =~ "def create_post" diff --git a/test/mix/tasks/phx.gen.html_test.exs b/test/mix/tasks/phx.gen.html_test.exs index a563365323..3972ef210d 100644 --- a/test/mix/tasks/phx.gen.html_test.exs +++ b/test/mix/tasks/phx.gen.html_test.exs @@ -48,7 +48,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do weight:float user_id:references:users)) assert_file "lib/phoenix/blog/post.ex" - assert_file "lib/phoenix/blog/blog.ex" + assert_file "lib/phoenix/blog.ex" assert_file "test/phoenix/blog/blog_test.exs", fn file -> assert file =~ "alarm: ~T[15:01:01]" assert file =~ "alarm_usec: ~T[15:01:01.000000]" @@ -235,7 +235,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do in_tmp_project config.test, fn -> Gen.Html.run(~w(Blog Comment comments title:string --no-context)) - refute_file "lib/phoenix/blog/blog.ex" + refute_file "lib/phoenix/blog.ex" refute_file "lib/phoenix/blog/comment.ex" assert Path.wildcard("priv/repo/migrations/*.exs") == [] @@ -259,7 +259,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do in_tmp_project config.test, fn -> Gen.Html.run(~w(Blog Comment comments title:string --no-schema)) - assert_file "lib/phoenix/blog/blog.ex" + assert_file "lib/phoenix/blog.ex" refute_file "lib/phoenix/blog/comment.ex" assert Path.wildcard("priv/repo/migrations/*.exs") == [] @@ -285,7 +285,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do Application.put_env(:phoenix, :generators, context_app: nil) Gen.Html.run(~w(Accounts User users name:string)) - assert_file "lib/phoenix/accounts/accounts.ex" + assert_file "lib/phoenix/accounts.ex" assert_file "lib/phoenix/accounts/user.ex" assert_file "lib/phoenix_web/controllers/user_controller.ex", fn file -> @@ -320,7 +320,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do Gen.Html.run(~w(Accounts User users name:string)) - assert_file "another_app/lib/another_app/accounts/accounts.ex" + assert_file "another_app/lib/another_app/accounts.ex" assert_file "another_app/lib/another_app/accounts/user.ex" assert_file "lib/phoenix/controllers/user_controller.ex", fn file -> diff --git a/test/mix/tasks/phx.gen.json_test.exs b/test/mix/tasks/phx.gen.json_test.exs index 1d534f7556..cfd6d3df41 100644 --- a/test/mix/tasks/phx.gen.json_test.exs +++ b/test/mix/tasks/phx.gen.json_test.exs @@ -48,7 +48,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do weight:float user_id:references:users)) assert_file "lib/phoenix/blog/post.ex" - assert_file "lib/phoenix/blog/blog.ex" + assert_file "lib/phoenix/blog.ex" assert_file "test/phoenix/blog/blog_test.exs", fn file -> assert file =~ "use Phoenix.DataCase" @@ -142,7 +142,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do in_tmp_project config.test, fn -> Gen.Json.run(~w(Blog Comment comments title:string --no-context)) - refute_file "lib/phoenix/blog/blog.ex" + refute_file "lib/phoenix/blog.ex" refute_file "lib/phoenix/blog/comment.ex" assert Path.wildcard("priv/repo/migrations/*.exs") == [] @@ -165,7 +165,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do in_tmp_project config.test, fn -> Gen.Json.run(~w(Blog Comment comments title:string --no-schema)) - assert_file "lib/phoenix/blog/blog.ex" + assert_file "lib/phoenix/blog.ex" refute_file "lib/phoenix/blog/comment.ex" assert Path.wildcard("priv/repo/migrations/*.exs") == [] @@ -189,7 +189,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do in_tmp_umbrella_project config.test, fn -> Gen.Json.run(~w(Accounts User users name:string)) - assert_file "lib/phoenix/accounts/accounts.ex" + assert_file "lib/phoenix/accounts.ex" assert_file "lib/phoenix/accounts/user.ex" assert_file "lib/phoenix_web/controllers/user_controller.ex", fn file -> @@ -223,7 +223,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do Gen.Json.run(~w(Accounts User users name:string)) - assert_file "another_app/lib/another_app/accounts/accounts.ex" + assert_file "another_app/lib/another_app/accounts.ex" assert_file "another_app/lib/another_app/accounts/user.ex" assert_file "lib/phoenix/controllers/user_controller.ex", fn file ->