Repository: phoenixframework/phoenix
Pull request #2310 (2017-05-17 12:24:08)

Remove plural name arg from phx.gen.embedded
I was surprised reading the docs for `mix phx.gen.embedded` saying that it required a plural name which is used for the 'table name'.

Embedded schemas aren't linked to a table, and may be used to validate data coming from other sources (request params, message queues, etc.)

This PR removes the required plural name argument from `phx.gen.embedded` and de-couples `phx.gen.embedded` from `phx.gen.schema`.

So the interface is just:

```
mix phx.gen.embedded Blog.Post title:string views:integer
```


Patch:
diff --git a/lib/mix/tasks/phx.gen.embedded.ex b/lib/mix/tasks/phx.gen.embedded.ex
index cfd8a3984..37c1a0be3 100644
--- a/lib/mix/tasks/phx.gen.embedded.ex
+++ b/lib/mix/tasks/phx.gen.embedded.ex
@@ -4,10 +4,9 @@ defmodule Mix.Tasks.Phx.Gen.Embedded do
   @moduledoc """
   Generates an embedded Ecto schema for casting/validating data outside the DB.
 
-      mix phx.gen.embedded Blog.Post blog_posts title:string views:integer
+      mix phx.gen.embedded Blog.Post title:string views:integer
 
-  The first argument is the schema module followed by its plural
-  name (used as the table name).
+  The first argument is the schema module followed by the schema attributes
 
   The generated schema above will contain:
 
@@ -19,7 +18,7 @@ defmodule Mix.Tasks.Phx.Gen.Embedded do
   where type are the types supported by Ecto. Omitting
   the type makes it default to `:string`:
 
-      mix phx.gen.embedded Blog.Post blog_posts title views:integer
+      mix phx.gen.embedded Blog.Post title views:integer
 
   The following types are supported:
 
@@ -28,16 +27,17 @@ defmodule Mix.Tasks.Phx.Gen.Embedded do
   """
   use Mix.Task
 
-  alias Mix.Tasks.Phx.Gen
   alias Mix.Phoenix.Schema
 
+  @switches [binary_id: :boolean, web: :string]
+
   @doc false
   def run(args) do
     if Mix.Project.umbrella?() do
       Mix.raise "mix phx.gen.embedded can only be run inside an application directory"
     end
 
-    schema = build(args, [])
+    schema = build(args)
 
     paths = Mix.Phoenix.generator_paths()
 
@@ -47,7 +47,45 @@ defmodule Mix.Tasks.Phx.Gen.Embedded do
   end
 
   @doc false
-  def build(args, _opts), do: Gen.Schema.build(args, [embedded: true], Gen.Schema)
+  def build(args) do
+    {schema_opts, parsed, _} = OptionParser.parse(args, switches: @switches)
+    [schema_name | attrs] = validate_args!(parsed)
+    opts =
+      schema_opts
+      |> Keyword.put(:embedded, true)
+      |> Keyword.put(:migration, false)
+
+    schema = Schema.new(schema_name, nil, attrs, opts)
+
+    schema
+  end
+
+  @doc false
+  def validate_args!([schema | _] = args) do
+    cond do
+      not Schema.valid?(schema) ->
+        raise_with_help "Expected the schema argument, #{inspect schema}, to be a valid module name"
+      true ->
+        args
+    end
+  end
+  def validate_args!(_) do
+    raise_with_help "Invalid arguments"
+  end
+
+  @doc false
+  @spec raise_with_help(String.t) :: no_return()
+  def raise_with_help(msg) do
+    Mix.raise """
+    #{msg}
+
+    mix phx.gen.embedded expects a module name followed by
+    any number of attributes:
+
+        mix phx.gen.embedded Blog.Post title:string
+    """
+  end
+
 
   defp prompt_for_conflicts(schema) do
     schema
diff --git a/lib/mix/tasks/phx.gen.schema.ex b/lib/mix/tasks/phx.gen.schema.ex
index f419295eb..2949861b6 100644
--- a/lib/mix/tasks/phx.gen.schema.ex
+++ b/lib/mix/tasks/phx.gen.schema.ex
@@ -178,8 +178,8 @@ defmodule Mix.Tasks.Phx.Gen.Schema do
     Mix.raise """
     #{msg}
 
-    mix phx.gen.schema and phx.gen.embedded expect both a module
-    name and the plural of the generated resource followed by
+    mix phx.gen.schema expects both a module name and
+    the plural of the generated resource followed by
     any number of attributes:
 
         mix phx.gen.schema Blog.Post blog_posts title:string


Test patch:
diff --git a/test/mix/tasks/phx.gen.embedded_test.exs b/test/mix/tasks/phx.gen.embedded_test.exs
index f26a2a006..088e572af 100644
--- a/test/mix/tasks/phx.gen.embedded_test.exs
+++ b/test/mix/tasks/phx.gen.embedded_test.exs
@@ -13,17 +13,17 @@ defmodule Mix.Tasks.Phx.Gen.EmbeddedTest do
 
   test "build" do
     in_tmp_project "embedded build", fn ->
-      schema = Gen.Embedded.build(~w(Blog.Post posts title:string), [])
+      schema = Gen.Embedded.build(~w(Blog.Post title:string))
 
       assert %Schema{
         alias: Post,
         module: Phoenix.Blog.Post,
         repo: Phoenix.Repo,
-        migration?: true,
+        migration?: false,
         migration_defaults: %{title: ""},
-        plural: "posts",
+        plural: nil,
         singular: "post",
-        human_plural: "Posts",
+        human_plural: "Nil",
         human_singular: "Post",
         attrs: [title: :string],
         types: %{title: :string},
@@ -36,7 +36,7 @@ defmodule Mix.Tasks.Phx.Gen.EmbeddedTest do
 
   test "generates embedded schema", config do
     in_tmp_project config.test, fn ->
-      Gen.Embedded.run(~w(Blog.Post blog_posts title:string))
+      Gen.Embedded.run(~w(Blog.Post title:string))
       assert_file "lib/phoenix/blog/post.ex", fn file ->
         assert file =~ "embedded_schema do"
       end
@@ -45,7 +45,7 @@ defmodule Mix.Tasks.Phx.Gen.EmbeddedTest do
 
   test "generates nested embedded schema", config do
     in_tmp_project config.test, fn ->
-      Gen.Embedded.run(~w(Blog.Admin.User users name:string))
+      Gen.Embedded.run(~w(Blog.Admin.User name:string))
 
       assert_file "lib/phoenix/blog/admin/user.ex", fn file ->
         assert file =~ "defmodule Phoenix.Blog.Admin.User do"
@@ -56,7 +56,7 @@ defmodule Mix.Tasks.Phx.Gen.EmbeddedTest do
 
   test "generates embedded schema with proper datetime types", config do
     in_tmp_project config.test, fn ->
-      Gen.Embedded.run(~w(Blog.Comment comments title:string drafted_at:datetime published_at:naive_datetime edited_at:utc_datetime))
+      Gen.Embedded.run(~w(Blog.Comment title:string drafted_at:datetime published_at:naive_datetime edited_at:utc_datetime))
 
       assert_file "lib/phoenix/blog/comment.ex", fn file ->
         assert file =~ "field :drafted_at, :naive_datetime"