Phoenix v1.3からのディレクトリ構成をもう一度調べる
はいこんにちは。
Phoenix v1.3.0-rc.0がリリースされました。前リリースからの大きな変更として、Phoenixプロジェクトのディレクトリ構成がガラッと変わります。新たな構成に対応したジェネレータは、phoenix.*
ではなくphx.*
というタスクになっています。ついで感がありますが、phoenix.server
やphoenix.routes
もdeprecatedになり、phx.server
、phx.routes
になっていくようです。
以前v1.3以降でどんな構成になるのか調べたのですが、改めて調べておきましょう。
phx.new
まずはv1.2以前のphoenix.new
と、v1.3から入るphx.new
でつくられるプロジェクト構成を比較してみます。Phoenixのリポジトリをcloneして、installer
ディレクトリの中で以下のコマンドを発行。Phoenixのリビジョンは1.3.0-rc.0
タグのついた4d608bf
です。
$ mix phoenix.new app_with_phoenix_new $ mix phx.new app_with_phx_new --dev
phoenix.new
に--dev
つけたら--dev projects must be generated inside Phoenix directory
と怒られたのですが、ディレクトリ構成が見たいだけなので--dev
無しで。つくられたディレクトリをtree
で見るとこんな形式。一部省略してあります。
app_with_phoenix_new ├── README.md ├── config ├── lib │ ├── app_with_phoenix_new │ │ ├── endpoint.ex │ │ └── repo.ex │ └── app_with_phoenix_new.ex ├── mix.exs ├── priv │ ├── gettext │ └── repo ├── test │ ├── channels │ ├── controllers │ ├── models │ ├── support │ ├── test_helper.exs │ └── views └── web ├── channels ├── controllers ├── gettext.ex ├── models ├── router.ex ├── static ├── templates ├── views └── web.ex
app_with_phx_new ├── README.md ├── assets │ ├── brunch-config.js │ ├── css │ ├── js │ ├── package.json │ ├── static │ └── vendor ├── config ├── lib │ └── app_with_phx_new │ ├── application.ex │ ├── repo.ex │ └── web │ ├── channels │ ├── controllers │ ├── endpoint.ex │ ├── gettext.ex │ ├── router.ex │ ├── templates │ ├── views │ └── web.ex ├── mix.exs ├── priv │ ├── gettext │ └── repo └── test ├── support ├── test_helper.exs └── web ├── channels ├── controllers └── views
以前書いた
web
がlib
配下に入るmodel
という概念がなくなる
はそのままのようですが、加えて、これまでweb/static
配下にあったcss
、js
や、package.json
、brunch-config.js
がまるっとトップレベルのassets
に移っています。見慣れないapplication.ex
というファイルがありますが、これは上の例だとapp_with_phoenix_new.ex
に当たるもので、名称が変更されるようです。全体的にディレクトリ構成とモジュール名の対応関係がキチンとしていて、生成されたlib/app_with_phx_new/web/page_controller.ex
ではAppWithPhxNew.Web.PageController
モジュールが定義されていました。
defmodule AppWithPhxNew.Web.PageController do use AppWithPhxNew.Web, :controller def index(conn, _params) do render conn, "index.html" end end
ただ、よく見るとweb.ex
が入る場所が一段深くなっていて、lib/app_with_phx_new/web/web.ex
となっています。ここはモジュール名とファイル配置の対応が取れていませんが、Elixir ForumのJoséのコメントによると、意図したものであるようです。
phx.gen.json
次はphx.gen.json
を試してみます。まずはhelp
から。
$ mix help phx.gen.json mix phx.gen.json Generates controller, views, and context for an JSON resource. mix phx.gen.json Accounts User users name:string age:integer The first argument is the context name followed by the schema module and its plural name (used for resources and schema). The above generated resource will add the following files to lib/your_app: • a context module in accounts.ex, serving as the API boundary to the resource • a schema in accounts/user.ex, with an accounts_users table • a view in web/views/user_view.ex • a controller in web/controllers/user_controller.ex • default CRUD templates in web/templates/user As well as a migration file for the repository and test files for generated context and controller features. ## Schema table name By deault, the schema table name will be the plural name, namespaced by the context name. You can customize this value by providing the --table option to the generator. Read the documentation for phx.gen.schema for more information on attributes and supported options. Location: _build/dev/lib/phoenix/ebin
これまではmix phoenix.gen.json User users name:string age:integer
のように、スキーマのモジュール名とテーブル名、テーブル構造を引数に与えていましたが、Accounts
という第一引数が増えています。help
にある例をそのまま実行してみます。
$ mix phx.gen.json Accounts User users name:string age:integer * creating lib/app_with_phx_new/web/controllers/user_controller.ex * creating lib/app_with_phx_new/web/views/user_view.ex * creating test/web/controllers/user_controller_test.exs * creating lib/app_with_phx_new/web/views/changeset_view.ex * creating lib/app_with_phx_new/web/controllers/fallback_controller.ex * creating test/accounts_test.exs * creating lib/app_with_phx_new/accounts/user.ex * creating priv/repo/migrations/20170302235526_create_accounts_user.exs Add the resource to your api scope in lib/app_with_phx_new/web/router.ex: resources "/users", UserController, except: [:new, :edit] Remember to update your repository by running migrations: $ mix ecto.migrate
引数に与えたAccounts
がどこに効いているかというと、lib/app_with_phx_new/accounts/user.ex
のようにuser
のひとつ上の名前空間になっています。テーブル名も、特に指定しないとaccounts_users
となるようです。また、コンソールに出ていませんが、しれっとlib/app_with_phx_new/accounts/accounts.ex
も作られていました。このAccounts
モジュールはいわゆる「境界づけられたコンテキスト」のひとつで、他のモジュール、例えばUserController
は、User
モジュールに定義した関数を使ったり、Repo.all(User)
のようなことはせず、このAccounts
モジュール経由でユーザの操作を行うようにしましょうね、という意図があるようです。生成されたファイルの中を見ても、UserController
のindex
アクションでは、Accounts.list_users
を使ってユーザ一覧を得るようになっていました。User
モジュールはスキーマ定義があるだけでほぼ空っぽで、テストもUser
ではなくAccounts
モジュールのものが作られています。
ただ、生成されたAccounts
モジュールの中に以下の関数が定義されており、少々やりすぎではという印象でした。Accounts
モジュールが太っていきそうなので、自分ならこれは今まで通りUser
モジュールに移します。
def AppWithPhxNew.Accounts do ... defp user_changeset(%User{} = user, attrs) do user |> cast(attrs, [:name, :age]) |> validate_required([:name, :age]) end end
defmodule AppWithPhxNew.Accounts.User do use Ecto.Schema schema "accounts_users" do field :name, :string field :age, :integer timestamps() end # ここにchangeset/2を書きたい end
なお、lib/app_with_phx_new/web/controllers/fallback_controller.ex
という馴染みのないものがありますが、ちょっとここでは深掘りしません。
まとめ
ざっくりまとめると、
ということをフレームワークが緩く強制するということのようです。