[Elixir]Guardianでログインを固める on Phoenix

ちょっとしたWebサイトを作ろうとして、Elixirを最近触っていることもあってElixir x Phoenixで構築し始めている最近です。 その中で簡単なログイン機能を追加しようとしたところ、awesome-elixirにてGuardianなる開発途中の認証ライブラリを見つけました。開発者はRack Authenticationのwardenの開発者でもあるのですね。ほー。 試したのは、v0.5.0 Readmeを見ながら作業かなーと思っていたのですが、サンプル実装を公開していることを知り、まずはそこから動かしてみることにしました。 ただ、動かしてみるとPhoenixが0.13.1ベース。 私が今使っているのは0.15.0ベース。 …. ということで、サンプルをPhoenix 0.13.1ベースから0.15.0ベースに書き換えました。開発者の方へPRも投げたので、もしかするとマージされるかも。 変更ブランチはこちら https://github.com/KazuCocoa/phoenix_guardian/tree/upgrade_to_phoenix_015 差分はこちら https://github.com/KazuCocoa/phoenix_guardian/commit/c5bf0edf1fd64ecbf7d16ac0a45fb4f2fd10be6b 上記をcloneして以下の通りに実行すれば、Phoenix 0.15.0上でアプリを起動、ログイン試したり諸々できます。 主な修正はPhoenixの0.13.1=>0.14.0へのアップグレードガイド、0.14.0=>0.15.0へのアップグレードガイドをみるとわかるのですが、この例のChannelで使うSocket周りに破壊的な変更が入っているのと、controllerで :plug action を呼ぶ必要がなくなっているところです。 なんだかんだでこうやって簡単な例からコード追うと理解が進んで良いですね。 session管理の箇所とか、色々参考になりました。 作者が簡単な説明をこことかで説明しているので、そこをまず読んでみると良いかもしれません。あと、サンプル実装は mix phoenix.new で生成されたアプリをそのまま使っているので、差分をコードリーディングすると良さそう。 ちなみに、このサンプルアプリ、以下のようにsession情報を見ながら動作をみることができるので理解の助けになりそうです。 Rubyを継承してか?Elixir、なんかゲームみたいなライブラリ名が結構ありますね。More

[Elixir]パターンマッチやcase文でのwhenにおけるguard clauses

Elixirの記述に慣れるため、exercism.ioで簡単な問題を解きながら時間の合間に遊んでいます。 そこで少しエラーを何回も出してしまったのでメモ。 以下の通り、Elixirではguard clausesの中におけるbooleanの式ではand or not を使う必要があるみたいですね。 Note that while boolean operators such as and, or, not are allowed in guards, the more general and short-circuiting operators &&, || and ! are not. from: http://elixir-lang.org/getting-started/case-cond-and-if.html 他言語だとビット演算云々で私は || とか && を使うことに慣れていたのでつまづいてしまった… あと、同じように以下の[ここ]と書いている箇所。判定を読みやすくするために独自の関数を作ってみたのですが、guard clausesの中ではそのような独自な関数はちゃんとマクロ組んで作らないといけないのですね。 あらかじめこのwhenの中で使える関数が用意されていますが、それを超えるものはマクロ組んでいく必要があるみたい。ただ、それは可読性を損なう恐れのあるものなので、個人的には case example do のexampleをうまいこと表現したいですねー。 もう1個。以下のようなcaseを使った関数、パターンマッチを組み合わせた関数でも記述することができます。 case文 パターンマッチ whenの中に記述できる処理式には限りがあるので、複雑な分類であれば case を使うほうがよさそうですが、簡単な when で区分できる場合、関数自体を分けたほうが責務が明確になってよさそう。…More

[Elixir]パターンマッチにおけるpin演算子

個人的なメモです。 Elixirでの、pin演算子の意義として以下の返答をもらいました。 確かに、個人的に明示的にパターンマッチと知らせるために ^x = 1 のように書かせることに利点があるという言語設計、納得。More

[Elixir][Phoenix]has_oneやbelongs_toの関係にあるモデルを、他方のモデル生成時に合わせて生成する

has_one や belongs_to の関係になっているDeviceとUserのモデルを考えた時に、Device作成時にUserモデルも新規に作成したい時の話。 このメソッドは対象は、Phoenixのcreateリクエストを元にしています。 Rep.transaction とその中で依存性を持つ複数のDB処理を一括で書いています。 has_one や belongs_to の関係を持っているので、一方の _id に、もう一方の id を与える必要がある、ところがポイント。 もっとちゃんとした書き方あるのかな。ありそうな気がする。。。 この関係が正しくできているかは、controllerレベルでは以下のように適当なユーザを生成して、その上でindexなどが表示できることを確認すればOK。 この時、index.htmlでは device.user.user_name のような、has_oneの関係にある要素を描画しようとしていることが必要ですが。 Railsに似ているので、Railsも参考にしながら書いてみているけれど、DB周りの扱いがだいぶ想像できるようになってきた感じ。 まだ公開していないけれど、練習用repositoryはこちらMore

[Elixir]引数で与えられた関数を実行する

Elixirで、引数として関数 fn を与えた時、その与えられた関数を実行する方法がぱっと調べただけでは見つからなかったのでメモ。 以下のように、 f.() という形式で、与えられた引数としての関数を実施できるようですね。 簡単なサンプル もう少し進んで、exercismから同様な問題があったのでそれを使って見てみます。 テストコード 実行コード Rubyでいう、 yield を呼び出すのをElixirでは .() で行うのですね。 参考: http://hashrocket.com/blog/posts/elixir-functions-ruby-lambdasMore

[Elixir]Pin演算子のちょっとした確認

ElixirにはPin演算子があって、その挙動を少し頭に入れるために簡単なcase文をつくってみました。 Pin演算子は、変数の再代入を防ぐ、という役割を持ちます。 以下は引数として与えられた a が、 SamplePin.pin1(a) ではcase文では b=10 が再代入される SamplePin.pin2(a) ではcase文で b=10 が再代入されず、パターンマッチされる という例です。 Erlangの流れを持ちますが、再代入が許容されるElixirでの再代入という副作用を防ぐ手段ですね。More

[Elixir]belongs_toしているモデルのcontrollerのテストコードを修正する

belongs_toの関係を持つモデルを、 #show で描画するところのcontroller層の話です。そのとあるコードをテストする時、従属の関係にあるモデルをDBに生成しておく必要があるのですが、そのコードを書くにあたりEctoの知識が欠如しててつまづいていたけれど、うまくかけた、というメモ。 Userモデルは、Pageモデルの一部に従属(belongs_to)しているので、Userモデルの要素を取得する場合、PageモデルをDBに生成しておく必要がある、というコードです。 以下のshowがテスト対象となるもので、UserのRepoを取得してきて、それに対してSQLの IN (Repo.preload)をしてきて、その結果を描画するという内容。 それに対して、Repo.insertでPageを作成、その次に従属の関係を作るためにpage_idに生成したpageのidを代入したUserを用意する。 最後に、Repoを更新すると、最低限のデータが揃うので、showで表示する要素としては十分なデータが揃えられる、というものです。 Ectoの理解があるとひょいひょいっとできるのですが、そこが欠如していた時はどう書けば良いかよくわからなかったのですよね…More

[Elixir]Ectoを触ってみる

Elixirの軽量なORMであるEcto それの使いかたをざっと把握していくために少しEctoを触ってみました。 メモです。 対象 標準的なクエリ とか これらはいずれも、以下のようなクエリが発行される。 パイプで接続されたEcto.Queryはクエリを結合していくのですね。 他、以下のようにlimitをかけることも可能。 preloadを行う belongs_to などで関係を持った、異なるテーブルに対してクエリを投げる場合、以下のようにpreloadを使います。 Ecto.Repo#preload/2 は、以下の通り IN で統合したテーブルに対してクエリを投げるのですね。この時に発行されるクエリは以下。 なるほど。More

[Elixir]Plug.Testを使ったRequestのテスト

作業メモ。 Elixir 1.0.5 Phoenix 0.14.0 Plug.Testを使ったやつ テスト対象 テストコード Phoenixを使ったやつ http://www.phoenixframework.org/docs/introduction test_helper.exs で、test用DBのcreate、migrationを行っているのですね。 test/test_helper.exs ファイル単位とか、以下のように行単位でテストできるのですね。 タグの付与と実行 moduleへのタグ moduleへのタグ定義 実行 実行 特定のタグのみ除いたテストケースの実行 test case個別へのタグ タグの定義 実行 特定のmoduleを除いたうえで、individual_test:yupを実行する ランダムにテストを実施する これ、面白いなーと感じました。 🙂 テストの自動生成 以下のようにcontroller、modelを作成したら、テストケースも自動生成されていました。 作成したモデルに対する、基本的なテストケースの自動生成良いですね。 ざっと生成されたテストケース見てみると、moduleで作成したリソースの生成/更新などの基本的な正常系、作成失敗などのエラー系。 ちなみに、以下を実行した後だと、 phoenix.gen.html とうViewも自動生成されるので、かなりお手軽。 test/controllers/user_controller_test.exs test/models/user_test.exs HelloPhoenix.ModelCase は、 test/support/model_case.ex で実装されている。More

[Elixir]Plugのコードを少し読んでみる

PhoenixのPlugの説明を読んでいると、Plug自体の動きが少し気になったので読んでみました。 ここでは、以下の Hello World を追ってみることに。 https://github.com/elixir-lang/plug API Document: http://hexdocs.pm/plug/ Plug自体、簡単なWebサーバの機構を提供するものですね。 ひとまず、 を実行してみる。 まずは以下が呼ばれる。 https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L41 この中を辿ると、 run(:http, plug, opts, cowboy_options) にいきつく。 ここで何しているかを見てみる。 https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L118 なるほど。最終的に、Cowboyを実行しているのですね。これはErlang向けの軽量なWebサーバらしい。 Cowboy https://github.com/ninenines/cowboy 返り値として、 {:ok, pid} のタプルを返す。 試しに、もう一度実行すると以下のようにerrorを得られる 今度は :error のタプルですね。:already_started が得られているので、pidを得たい場合、 とすれば得られます。なるほど。 ちなみに、停止するときは、 とします。 これは、 https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L96 を参考にすれば良いですね。ここで与える :ref は、起動時に与えた Plug.Adapters.Cowboy.http MyPlug, [] の中の MyPlug という名前。 これは、以下のコードを見てみると、確かに:refの引数( build_ref(plug, scheme) )としてplugを与えているのでわかります。 https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L35 なるほど。Plugというか、Cawboyの理解が少し進んだぞ。 %Plug.Conn{}…More