RailsチュートリアルをPhoenixで実装しました。 https://github.com/pojiro/sample_app
Phoenixではどうやるか?をまとめています。随時更新します。
※RailsはRailsチュートリアルでしかさわったことがありません。
bundle install
$ mix deps.get
railsコマンド
rails new
$ mix phx.new
rails console
$ iex -S mix
rails server
$ mix phx.server
rails db:create
$ mix ecto.create
rails db:migrate:reset & rails db:seed
$ mix ecto.reset # aliasなので、自身のプロジェクトのmix.exsのdeps aliasesを見ること
rails generate migration
$ mix ecto.gen.migration
rails db:migrate
$ mix ecto.migrate
rails db:rollback
$ mix ecto.rollback
rails test
$ mix test
rails destroy
ない。gitでdiscardする。
modelのメソッドはどこに書くの?
schemaをラップするコンテキストモジュールに関数を作成する。
RailsとPhoenixは思想が大きく異なる。
Railsでは、データと関数のペアのクラスでモデルを定義するが、
Phoenixでは、データと関数をモジュールを分けて定義する。
schemaとchangesetをデータ側にそのデータを操作する関数の集まりをコンテキストモジュールとして定義する。
これはruby(オブジェクト指向)とelixir(関数型)の思想の違いによる。Phoenixでオブジェクト指向をやるのはよくない。
validates
validatesの各キーはそれぞれ関数として用意される。
- presence => validate_required
- length => validate_length
- format => validate_format
- uniqueness => unique_constraint
provide & yield
Phoenixではtemplate内での変数参照はPlug.connストラクトのassignsを介して行う。
渡したいデータはassign(conn, key, value)すると、template内では@ keyで参照できる。
flash
put_flash(conn, key, message)をつかう。
template側で取るときはget_flash(conn, key)
ファイルのアップロード
form_forでfile_inputを使う。[multipart: true]を忘れないようにする。
<%= form_for @changeset, Routes.micropost_path(@conn, :create), [multipart: true], fn f-> %>
<div class="field">
<%= textarea f, :content, placeholder: "Compose new micropost..." %>
<%= error_tag f, :content %>
</div>
<span class="picture">
<%= file_input f, :picture, class: "form-control" %>
<%= error_tag f, :picture %>
</span>
<%= content_tag :input, nil, type: "submit", class: "btn btn-primary", value: "Post" %>
<% end %>
sessionメソッド
put_session(conn, key, value)
get_session(conn, key)
delete_session(conn, key)
- expire期間をkey毎に設定できない。一律にはできる。
(endpoint.exのPlug.sessionにmax_ageオプションを設定する) - デフォルトは改ざん防止のために署名される。
- 暗号化も可能。(endpoint.exのPlug.sessionにencryption_saltオプションを設定する)
cookiesメソッド
put_resp_cookie(conn, key, value)
get_resp_cookie(conn, key)
delete_resp_cookie(conn, key)
- 署名、暗号化はされないので、必要な場合はput_resp_cookieする前に行う。Phoenic.Tokenが便利
- expire期間をkey毎に設定できる。
before_action
plugを作って、
# 例
def logged_in_user(conn, _opts) do
if logged_in?(conn) do
conn
else
conn
|> SessionHelper.store_location()
|> put_flash(:error, "Please log in.")
|> redirect(to: Routes.session_path(conn, :new))
|> halt()
end
end
コントローラにつなぐ。
defmodule SampleAppWeb.UserController do
use SampleAppWeb, :controller
plug :logged_in_user when action in [:index, :show, :edit, :update, :delete]
...
end
request.referer
List.first(get_req_header(conn, "referer"))
assert_select
flokiを使ってassertすればやりたいことはできるはず。
conn = get(logged_in_conn, Routes.user_path(conn, :show, user))
parsed_html = Floki.parse_document!(html_response(conn, 200))
assert Floki.find(parsed_html, "h1") |> Floki.text() =~ user.name
テストの自動実行、guard
guardに相当するのがmix_test_watch
$ bundle exec guard # rails
$ mix test.watch # phoenix
テスト結果の通知、minitest-reporters
ex_unit_notifierを使う。
heroku
herokuに相当するのがgigalixir。
コマンドも同じようにつくられているようだ。
$ heroku logs
$ gigalixir logs
$ heroku run rails db:migrate
$ gigalixir run mix ecto.migrate