技術メモスマートフォン用の表示で見る技術メモプログラミングとか電子工作とか2019-04-01S3の静的ウェブサイトホスティング(2019年03月31日現在)AWSどうも、靖宗です。S3で静的ウェブサイトを公開するときにちょっと詰まったのでメモ。最初は下記のQiita記事を参考にさせていただきました。qiita.comまた、当然のことだとは思いますが、アクセス権などをいじるので、その辺は自己責任でお願いします。基本的にはS3のバケットのルート(直下)にindex.htmlを配置するCloudFrontは挟まないという想定で行います。一般公開するならPVにもよるんでしょうがCloudFrontは挟んだ方が良いと思います。(Basic認証なんかもLambda使えばすぐですし。)バケットの作成なにはともあれバケットを作成しないことには始まりません。S3のコンソールからバケットを作成します。適当に名前を付けて次へ。リージョンはどこでも良いと思うんですが、日本向けなら東京リージョンが良いんじゃ無いでしょうか。プロパティとかはすっ飛ばして次へ。ここで、パブリックアクセスの設定が出てきます。ここでコントロールしてもいいんですが、一旦推奨設定を利用するという事で次へに行きます。慣れてきたらここで後ほど説明する設定を適応しても良いと思います。成功すれば一覧の所に先ほど決めた名前でバケットができてると思います。ファイルのアップロード早速ファイルをアップロードしていきます。一応ルートディレクトリでなくてもホスティングできると思いますが、今回はルートに配置します。適当に作ったindex.htmlを利用します。rel="stylesheet"></head><body>hoge</body></html>S3にアップロード深いことは考えずとりあえずアップロードしました。ここでもパブリックアクセスなどの設定で後々の手順をスキップできるのですが、今回は手順を極力分離します。静的ウェブサイトホスティングの機能を有効にするホスティング機能を有効にします。S3のバケットのページからプロパティのページへ移動します。hostingと書かれた箇所をクリックします。何もしなければここで記載されている「エンドポイント」がアクセスするURLになります。独自ドメインの設定なんかはまた別の機会に。設定を「ウェブサイトのホスティングを無効にする」から「このバケットを使用してウェブサイトをホストする」に変更します。インデックスドキュメントのところに先ほどアップロードしたindex.htmlを指定します。htmlとかいうディレクトリに格納してる場合はhtml/index.htmlとかでもいけるとは思います。設定を変更したら保存を押して設定完了。すると、先ほどのエンドポイントで指定したURLでウェブサイトのホスティングができないんですよこれが。バケットポリシーの変更S3のバケットのページからアクセス権限のページへ移動します。いままで通りにバケットを作成していれば、上記のようになっていると思います。ここの「パブリックバケットポリシーを管理する」がFalseになっていないとバケットポリシーを変更できません。この設定と、「バケットにパブリックポリシーがある場合、パブリックアクセスとクロスアカウントアクセスをブロックする」という設定をFalseにします。二個目の奴はバケットポリシーが変更されてもアクセスをはじく設定なので消しときます。これでバケットポリシーが変更できるようになりました。お次はバケットポリシーを変更していきます。ポリシーエディターでポリシーを設定します。]}保存して完了です。公開できる設定になっていれば、アクセス権限やバケットポリシーの所に「パブリック」の表示が出ていると思います。これでエンドポイントにアクセスして表示されれば静的ウェブサイトホスティングができています!今回はホスティング機能だけ記載しましたが、本来であればCloudFrontを経由してアクセスさせるのが適切だと思います。補足もしかしたら場合によってはindex.htmlのアクセス権限をいじる必要があるかもしれません。先ほど試した限りでは大丈夫でしたが、もしうまくいかない方がいらっしゃったらその辺をいじってみて下さい。プログラミングどうも、靖宗です。最近AWSばっかり触っててご無沙汰でしたが、若干落ち着きそうなのでPhoenixの学習を再開していきます。今回はContextsということですが、これもまた結構長そうな項目です。GeneratorsContextsこの章(機能?)いままでの機能を組み合わせるにはどうするか、などでしょうか。確かに各々の機能にだけ注目してもなかなか実装できないことってありますよね。designElixirの標準ライブラリを利用する際に、内部実装を気にする必要は特にありません。Logger.info/1などは一度使い方を学べば特に内部で○○モジュールが動いてて・・・などと気にする人はそこまでいないと思います。(場合によっては必要だと思いますが)Contextを利用することでPhoenixプロジェクトもElixirのモジュールのように外部から利用できるようにする機能という感じでしょうか。この章では簡単なCMSを作成していくことで、このContextを学習しましょう!という方針のようです。Contextまずはアカウントの実装からやっていくようです。どんなシステムでもアカウントの登録や編集などはあると思いますし、わりかしこの辺のデザインがシステム全体に大きな影響を及ぼします。前回のEctoの章で使ったプロジェクトを利用していくようですが、自分は新しく作り直します(作業してたプロジェクトがどっかいった)。前回はユーザーのスキームを手動で作成しましたが、なにやらGeneratorsというもので生成するっぽいので、もし引き続きの人がいれば一旦消します。priv/repo/migrations/*_create_users.exsマイグレーションのファイルも消してるっぽいです。データベースのリセットも行っています。ecto.createまではいつも通りと同じです。ここまできてようやくContextを利用していくようです。GeneratorでアカウントのContextを作成していきます。phx.gen.htmlとしていくと、どうやらCRUDのような機能まで実装してくれるっぽいです。ecto.migrate今まではlib/hello_web/側のPhoenixのウェブ機能側にしか色々ファイルは自動生成されませんでしたが、lib/hello/側にもファイルが生成されました。これがContextでしょうか。lib/hello/accounts/user.exにスキーマの定義ファイルも生成されています。ウェブ側の実装をしちゃいます。lib/hello_web/router.exを編集します。phx.gen.htmlでuser関連のファイルは生成されているので、大体これで終わっちゃいます。スキーマの追加などされているのでデータベースをマイグレーションして起動してみます。phx.server追加したページのhttp://localhost:4000/usersにアクセスします。入力フォームまで既に用意されています。なんでもいいけどとっとと実装したいときとかめっちゃ便利そうですね。バリデーションやエラー表示までデフォルトでやってくれます。ちなみに成功する表示もバッチリです。リストページにも追加されてるリストページからは編集や削除もできます。デフォルトで生成されるCRUDのAPIとしてのベストプラクティスみたいなものという認識でも結構有り難い機能です。Generatorsただ、一応今回の目的はPhoenixプロジェクトのウェブ表示の機能以外でもユーザーの情報が取り扱えるようにContexを利用してみることです。これじゃPhoenixのウェブ表示便利機能やんけ!と思うかもしれませんが、ここでuser_controller.exを見てみます。Hello.Accounts.Userで、AccountsのContextを利用してコントローラが実装されていることが分かります。これによって、ウェブ表示の機能とデータベース周りの機能が分離されていることが分かります。仮にデータベースがPostgreSQLからMySQLに変わろうがPhoenix側ではなんら問題無いですし、なんなら設計次第ではテーブル構造が変わっても吸収できる実装も可能ということです。次にAccountのContextであるlib/hello/accounts.exを見ていきます。...end非常にシンプルなモジュールです。Repoに対する操作をラップするようなモジュールになっています。このContextに関数を追加することで、操作をカスタマイズしたり、新しい機能を実装したりして、様々な箇所でその機能を共有するのでしょう。この辺はわりとオブジェクト指向っぽい(カプセル化)ですが、機能のみの実装ですので似て非なる物です。ここで、ユーザー作成の関数Accounts.create_user/1を見てみます。%{}はデフォルトの引数は%{}やで!という事を意味してます。)スキーマの作成や更新はchangesetでやるというのは前回やりました。changesetの実装を見てみます。endend前回はスルーしましたが、changesetの前に@doc属性がfalseになっています。これはどうやらドキュメントにこの情報が載らないようにするための属性だそうで、「プライベートな(他のAPIからは参照されない)実装」というところを意味するそうです。(実際に外部から使えなくなる訳では無い)なので、別のモジュールからuserの作成や更新をするときは、必ずAccountのContextを通して行うという方針を採るようです。このへんはローカルルールみたいなもんなんで指針だけ把握してれば良いんじゃ無いかと思います。プログラミングどうも、靖宗です。前回はほとんど仕様みたいな感じになってたので実際に扱う所までできたらなぁと思います。Validations概要Changesetsは前回みたスキーマが定義されているファイルにある関数で、データをデータベースに登録する前に型変換やバリデーションが行われる関数のようです。バリデーションで不正な値をはじいたり、扱ってるデータのフィールドが更新されているか否かも取得できるみたいです。:number_of_pets])endchangeset/2関数の中にはパイプラインが存在していて、userデータがcast/3とvalidate_required/3という関数を通過しています。cast/3は第一引数に構造体、第二引数にパラメータ(値の登録や更新に使われる)、第三引数にアップデートするカラムの指定を渡す仕様のようです。役割としてはスキーマのフィールドを取ってきてるだけのようです。validate_required/3はcast/3の返値を第一引数で受けて、第二引数に渡されたカラムのデータをバリデーションするようです。実際に扱うなにはともあれ動かして確認するのが一番です。iexで確認できるようなのでやってみます。HelloPhoenix.Userを使って、いちいちHelloPhoenix.User.changesetとか書かないでいいようにしておきます。HelloPhoenix.UserHelloPhoenix.User空のUser構造体を利用してchangesetを使ってみます。false>なにか帰ってきましたがエラーが出てるっぽいです。空でバリデーションしてるんであたりまえなんですが。changeset.valid?ではじかれてるかどうか判断できるようです。changeset.valid?falsechangeset.errorsで何がマズいのか見れます。各フィールドのエラーが参照できてますね。:required]}]全てが必須項目になってます。バリデーションの編集number_of_petsをオプション(必須ではない)にしてみましょう。changesetのパイプラインのvalidate_requiredを編集します。end...iexで再コンパイルして実行します。:required]}]number_of_petsのエラーは消えました。必須か否かはvalidate_requiredに渡すリストで制御されていることが確認できました。不要なデータ(castの動作チェック)changesetに渡すパラメータの中にスキーマに登録されていないデータが存在した場合にはどうなるでしょうか?(バリデーションではじかれるか無視される?)実際にやってみます。value"という余分なキーペアを入れたマップを作成しました。これをchangesetに渡します。true>はじかれはしないようです。changeset.valid?true問題ナシ。value"は綺麗さっぱり消滅しました。cast/3のところでスキーマに無いパラメータは落とされるのでしょう。他のバリデーションRDBで様々な制約などが考えられますし、そもそもの制約(英語だけとか)もあるので、必須か否かのバリデーションだけでは心許ないです。他のバリデーションもあるようなので見ていきます。データ長を指定するvalidate_lengthから。endこれで最小2文字というバリデーションがかかるようです。パイプラインで完結に書けるのはElixirの良いところですね。実際に1文字とかを登録してバリデーションではじかれるかやってみます。:min]}長さ足りないぞ!って怒られてます。最大長さはminのところをmaxにすればよさそうです。endお次はフォーマットを決めるvalidate_format/3。例としてemailのフォーマットを@が入ってないと怒るようにします。end最後の引数に渡す正規表現でフォーマットを決定しているようです。実際にはじかれるか確認します。:format]}長さの時もそうでしたが、一応changesに値は格納されるようです。Persistenceスキーマとかを見て来ましたが、これらを取得したり格納したりはまだでした。APIもEctoで扱えるみたいです。insertとりあえずiexで触ってみます。最初にUserのエイリアスも付けてますが、さっきやってるので本来は不要です。}}Repo.insert/1でデータを格納できるようです。%User{}}で成功か否かを判断するようです。一応PostgreSQLに格納されてるか見ときます。rows)2行追加されています。all今度はデータを取り出してみます。スキーマを指定すれば該当するテーブルからデータを取り出せるようです。}]取ってきたデータは既にElixirの構造体になっており、非常に便利です。Ecto.QueryEcto.Queryを利用すると様々な機能が使える上にSQLインジェクション攻撃防止や最適化もしてくれるようです。"user2@example.com"]SQL文の様な書き方ができるようになりました。u.emailでu(各要素)のemailを抽出する処理になるようです。少々SQL文とは違いますが雰囲気一緒なので慣れればどうということは無さそうです。[]1Repo.oneは結果が1個のクエリを発行する際に使われる関数のようで、where句やselect句を取っています。ilikeやcountはPostgreSQLなどに準拠する物です。Ecto.Queryを利用すればSQLの様な形で直接マップのリストなどを作成することができます。"user2@example.com"}]updateなどRepoには当然のことRepo.update/1やRepo.delete/1などが実装されています。その他にもRepo.insert_all、Repo.update_all、Repo.delete_allも実装されています。その他詳細とはいえココで全部の機能を書ききれる訳ではないのでより知りたい方はEctoのドキュメントを参照とのこと。hexdocs.pm近いうちにやります。リレーションとかどうやらこの章ではデータベースのリレーションなどには言及しないようです。MySQLMySQLを利用するときの指南書。MySQLじゃないとだめってシーン少ないと思うので素直にPostgreSQLを利用するのが良いんじゃないかとは思いますが、さっと見ておきます。phx.newするときに明示的にmysqlを利用するオプションを付けるようです。付けなくてもあとで変更可能だとは思うんですがめんどくさいと思います。deps.getして完了!簡単ですね。既存のプロジェクトをMySQLに変更する場合。mix.exsを編集します。endend次にconfig/dev.exsを編集。"hello_phoenix_dev"必要に応じてconfig/test.exsやconfig/prod.secret.exsも編集。Ecto.Adapters.PostgresをEcto.Adapters.MySQLに変更。ecto.createすれば利用できるはずです。なんにせよ途中でデータベースを変更するのはあまり推奨されることでは無いとは思います。APIで分離して管理しておけばAPI側でデータベースを変更するのは楽だと思います。プログラミングどうも、靖宗です。今回はEcto、ということでデータベースラッパーのお話でしょうか。Elixirでよく使われるライブラリなので個別に触りたいところでもありますが、とりあえず今回はPhoenixのドキュメントを読み進めます。SchemaEctoウェブシステムを開発する上で避けられないのがデータベース絡みのお話です。(とはいえFirebaseなど便利なものも出てきているので、薄ぼんやりとした理解でもある程度のものができてしまう時代だとは思います。)Ectoは現在PostgreSQLMySQLMSSQLSQLiteMnesiaの5つのRDBMSに対応してるそうです。Mnesiaなんかは初耳です。何も指定しない場合はPhoenixはPostgreSQLをチョイスします。Runningでも触れましたが開発用のデータベースの設定はconfig/dev.exsに記載されてます。ユーザー名や参照先を変更したい場合はこのファイルを編集します。デフォルトでは参照先はlocalhost、ユーザー名はpostgres、パスワードはpostgresとなっています。このpostgresのユーザーを作成するにはpsqlコマンドでrootユーザーとしてログインした後下記のコマンドで作成できます。SUPERUSER;簡単に触ってみるとりあえずなんか動かしてみます。phx.gen.schemaでEctoのスキーマを作成できるそうです。EctoのスキーマはElixirのデータタイプをPostgreSQLのテーブルへと変換するルールのようなものでしょうか。ドキュメントどおりやってみます。ecto.migrateファイルが2個生成され、始めてlib/hello_phoenix/の方にファイルができました。1個目は先ほど定義したスキーマの情報が記載されています。2個目は名前や中身的に現在のデータベースに先ほど定義した情報のテーブルを作成するための記述でしょう。ORMとかでよくありそうな感じです。スキーマを登録したらマイグレーション(データベース作ったりリレーションの整合性とったりとか)が必要な筈です。ecto.migrateでマイグレーションします。0.0sデータベースにusersのテーブルが作成されているか確認します。自分はdockerでpostgresを動かしてるのでdockerから確認します。rows)テーブル作成されていることが確認できます。usersだけでなくusers_id_seqというテーブルもできていますが、コレはusersのシーケンスだそうです。いままでDjangoなどのORMに頼りっきりでPostgreSQL使ってるのにシーケンスという概念を知りませんでしたが、どうやらユニークなIDを生成するオブジェクトだそうで、ユーザー情報を生成するときに連番の番号を高速に生成して登録したり検索の時によしなにしてくれるもんだと思っておけばいいんじゃないでしょうか(適当)一応usersの中身も見ておきます。(id)idは先ほど説明したシーケンスの物なのでいいとして、inserted_atとupdated_atが着いてます。phx.gen.schemaで生成された一個目のファイルusers.exに記載があります。end...この辺をカスタマイズしたい欲求はあまり無いと思いますが、変更するならこの辺をいじるっぽいです。プライマリキーを変更する方法はまだ特に記載がありませんが、基本デフォルトでいい気はします。RepoPhoenixでアプリケーションを生成した際に生成されるファイルlib/hello_phoenix/repo.exに関してです。中身は非常にシンプルでものの数行しかありません。Ecto.Adapters.Postgresend生成したプロジェクトでEctoを使うで!アダプターはPostgresで!ってぐらいの役割なのでほとんど編集することは無いと思います。あるとするならPostgreSQL以外の選択肢をチョイスした場合にアダプターを変更するぐらいでしょうか?あとデータベースの設定はconfig/dev.exsを編集しましたが、必要に応じてテスト用の設定config/test.exsやデプロイ用の設定config/prod.secret.exsを編集するようです。Schema一番最初にスキーマの作成をしましたが、EctoのスキーマはElixirのデータ構造とデータベースのテーブルを繋ぐ(変換する)役割を果たしています。上記の例ではlib/hello_phoenix/user.exにスキーマが作成されています。endendどうやらスキーマで定義した型は構造体として利用できるそうです。データのキャストからバリデーションもこのスキーマのモジュールが担ってくれるようなので、開発している際はバリデーションをいちいち書く必要は無さそうです。次回はChangesetsやValidationsのお話。プログラミングどうも、靖宗です。AWSでpipでインストールされるライブラリを使用するよくある欲求で、なおかつウェブ上に結構ドキュメントはあると思いますが、自分のメモ用に。Lambdaでは標準ライブラリ以外は使えませんが、zipファイルに固めてアップロードすれば利用できることが知られています。基本的にはvenv仮想環境で開発されてる方には「当たり前やろ!」と怒られそうですが、Pythonのようにバージョンに左右される言語を利用するときは開発は極力venvなど環境をローカルとは切り離して開発するのが未然にトラブルを防ぐ手段です。venvで必要なライブラリだけインポートして、それらをzipに固めます。venvは導入済みとし、今回はrequestsモジュールを使う関数を作成します。requestsとりあえず動かすLambdaで実行したいファイルを作成します。一応ここではindex.pyとしておきます。}引数などにかかわらずpythonの公式サイトのニュースを取得してくるような関数になってます。一応動作確認しておきます。同フォルダでPythonを仮想環境で実行。'body':...>>>正常に動作すればとりあえずヨシ。(ホントはテストとかを入れときたいですが、とりあえずこれで行きます。)必要ファイルをローカルにインストールします。requirements.txtを作成しておきます。requirements.txtrequirements.txtからローカルにライブラリをインストールします。.これを実行するとローカルに色々ファイルが出てくると思います。ここまで来れば、後はzipに固めてLambdaにアップロードするだけです。MacやLinuxではzipコマンドがあるかと思いますが、Windowsではないのでなにがしかの方法で圧縮して下さい。venvディレクトリは不要です。(サイズが大きいので間違えないように)本来であればライブラリ名-バージョン-dist-infoみたいなディレクトリも要らないと思いますが、そこまでファイルサイズ大きくも無いですし、手間なら一緒に圧縮しても良いと思います。Lambdaにアップロードzipに固めたファイルをアップロードします。既存のLamndaか、新しく作ったLambdaの「関数コード」という所からアップロードします。ランタイムをPythonにして置いて下さい(バージョンは適宜変更)。上記のコードを利用していればハンドラの名前が異なっているので、ハンドラの名前も合わせて下さい。(index,pyならindex..lambda_handler)適当なテストイベントを作成してテストを実行すれば、実行結果が表示されます。ライブラリが動作していれば実行結果は成功になり、ログに取得した情報が表示されるかと思います。.オプションを付けてインストールし直すことで、importなどの依存関係をそのまま引き継ぐことができます。プログラミングどうも、靖宗です。なにやらLiveViewなる機能がちらほらツイッターで見かけまして、一応触っておこうと思いました。下記サイトを参考にしております。dockyard.comgithub.comLiveView名前からも感じますが、どうやらLiveViewというのはリアルタイムの双方向性のあるレンダリング機能のようです。WebSocketsとあるのでWebSocketでうまいこと実装されているんでしょう。JavaScriptでフロント+バックエンド(Phoenix)やと複雑やしメンテナンスたいへんやろ?というモチベーションのようです。主な機能としてはリアルタイムで値をアップデートするクライアント側でのデータのバリデーション(バックエンドでの評価が要らない)入力のオートコンプリートリアルタイム性のあるゲームをJavaScript無しでできるそうです。Model書き方は大まかにはController+Viewと通常のHTMLレンダリングに近いようです。今までレンダリングしていたテンプレートがモジュールと化し、そのモジュールで定義する関数が様々なイベントのコールバック関数になるイメージでしょう。プロジェクト作成なにはともあれ動かしてみます。新しいプロジェクトでやってみましょう。新しいプロジェクト作る練習にもなります。名前はhello_liveviewにしました。hello_liveviewhello_liveview/config/dev.exsを編集します。10データベースのユーザー情報などは適宜合わせて下さい。再起動してコンテナが落ちてたので起動。ecto.createでデータ作成。createdこれで起動まではいく筈。phx.server...問題ナシ。参考にしたブログでもPageControllerを編集してるのでそれに則る。LiveViewの機能を有効にするPhoenix側の依存関係を追記するLiveViewは標準機能ではなくオプションとして提供されています。deps.getしてやる必要があります。deps.getする。(https://github.com/phoenixframework/phoenix_live_view.git)...ソケット通信の設定もうちょっと設定が必要なよう。エンドポイントにsigning_saltなるものが必要だそうで。32を実行して得られたお塩(salt)をconfig.exsのエンドポイントの設定に追記してやります。32SECRET_SALT(なんか変な文字列がでます)config.exsのエンドポイントの設定に追記します。:hello_liveview,句の一番最後に追記しました。テンプレートファイルを有効にするついでにLiveViewのテンプレートは.leexになるそうなので、この設定も同じconfig.exsに書いときます。Phoenix.LiveView.Engine]一番最後に追記しました。router.exのパイプラインに設定を追加するお次にパイプラインに設定を追加しておきます。lib/hello_liveview_web/router.exの:browserパイプラインを編集します。end...レイアウトに当てはめるplugを明示的に記載していますが、後述するコントローラからLiveViewを呼び出す場合は要らないと思います。次にlib/hello_liveview_web.exのviewとrouterの箇所も編集します。endendLiveViewのレンダリング関数とルーティング機能のインポートを追記しました。これは全てのViewでインポートされてしまうのでいちいちLiveViewの方で読み込んでも良いのかもしれませんが、LiveView多めのアプリケーションならこの手法で良いと思います。Endpointの設定まだ設定は続きます。phx.newのオプションとかになるんじゃないでしょうか?)lib/hello_liveview_web/endpoint.exにLiveViewに使用されるソケット通信を追記します。:hello_liveviewは無いですが、Phoenixのデフォルトなので一応書いておきます。LiveViewを入れとくフォルダの作成+ホットリロードの設定LiveViewを入れておくフォルダlib/hello_liveview_web/liveを作成しておき、ホットリロードを有効にするためにconfig/dev.exsに追記しておきます。]...JavaScript側の依存関係を追記する以上でおそらくPhoenix自体の初期設定は終わりました。仕上げにレンダリングされるJavaScript周りに手を加えます。バックで動いているJSの依存関係を編集します。assets/package.jsonを編集します。installしておいて下さい。isntallソケット通信開始の箇所を追記するレンダリングされるJavaScript側からPhoenixへの通信を開始する箇所を追記します。assets/js/app.jsの最後に下記を追記します。LiveSocket("/live")liveSocket.connect()これで初期設定は大体OKなはずです。試しにサンプルのThermostatViewを実装してみます。追加の依存関係ThermostatViewはcalendarというライブラリを使用しています。mix.exsに:calendarの依存関係を追記しておきます。deps.getをお忘れ無きよう。(忘れててもmixが教えてくれますが。)CSSの追加ThermostatのCSSを追加します。この編は素直にassets/cssに保存しておきます。レイアウトが違うのかちょっと位置が微妙なのでassets/css/thermostat.cssのmargin-topだけいじります。250px;}...assets/css/app.cssでインポートしておきます。"./thermostat.css";LiveViewのルーティング方法は2種類LiveViewのルーティング方法は2種類あります。1個目はrouter.exからコントローラを介さずに直接レンダリングする方法で、liveマクロを利用します。end...2個目は従来通りコントローラを介してレンダリングします。end...たぶんコントローラを介してレンダリングする方が柔軟性があると思いますのでそちらで実装していきたいと思います。:app}は不要ですので消していただいても問題無いかと思います。上記に従ってlib/hello_liveview_web/router.exを編集します。endendコントローラの設定めんどくさいのでlib/hello_liveview_web//controllers/page_controller.exを編集して使います。endendlive_renderはlive_render/2もインポートしてるので最後のマップは要らなさそうな気もするんですがなんか渡さないと怒られたのでlive_render/3を利用しています。この辺は後ほど再確認したいところです・・・index/2とthermo/2を比較すると、関数名こそ違えどほぼ同じ様な構造になっているので抵抗感無く使えるかと思います。LiveViewの追加lib/hello_liveview_web/live/thermostat_live.exを作成し、LiveViewを作って行きます。endrender/1の所は通常のViewと同様にテンプレートを置いておけば書かなくても良いのかもしれませんが、とりあえずサンプル通りにしておきます。上記の挙動から鑑みるに、下記のタイミングで再レンダリングがかかっています。厳密にはソケットのステートが変更された際(Phoenix側でsocketに値がアサインされた時)に再レンダリングするようです。Thermostatの例では、マウント時に1秒後に:tickというメッセージを自分宛に発行しています。これを利用して:tickというメッセージを受けた際にhandle_info/2が呼び出される(このへんはGenServerを参照してください)ので、そのコールバック関数の中でソケットに値をアサインしています。(コレを1秒毎に繰り返す)クライアントサイドではphx-click="hoge"というディレクティブを要素に追加するとクリック時にイベントが発生し、handle_event/3で受けれるといった仕組みのようです。それぞれ呼び出されるイベント毎にソケットに値をアサインしてレンダリングを実行しているようです。実際にやってることとしては他のフレームワークとそう大差ない(通常のフレームワークであればおそらくソケット通信よりも非同期通信で処理するのが一般的だとは思いますが)ですが、裏を返せば他のフレームワークを学習しなくてもデータバインディングやコンポーネントの再レンダリングができるので、ラピッドプロトタイピングがかなり捗りそうです。プログラミングどうも、靖宗です。今回はPresenceということであまりなじみのない概念・・・どうやらChannnel絡みの機能だそうですので、前回のサンプルの続き、ということになりそうです。PresencePresenceモジュール作成mixを利用したChannel作成Channel編集クライアントサイドを実装する動作確認PresencePresenceはトピックのプロセス情報を取得したり、クラスタ上にこの情報を複製したりする機能のようです。サーバー側もクライアント側も実装がシンプルになっているそうなのでそこまで苦労することはないでしょう。書いてますが要はCRDTであるといった感じです。CRDTに関しては下記を参考にしました。qiita.comぶっちゃけよく分かってませんが、要はチャットとかで時間の整合性が若干ズレても構わんけど最終的に全員の発言をきっちり表示したいとかそんなんだと思います。Presenceモジュール作成Presenceを利用するにはPresenceモジュールを作成する必要があるそうです。mixで作成していきます。phx.gen.presenceするとlib/hello_web/channels/presence.exが作成されます。該当するファイルはほとんどがコメントで中身はほとんどないです。Hello.PubSubendひとまずこのままにしておきます。生成した際にでていた指定通りにlib/hello/application.exにHelloWeb.Presenceを追記します。次にRoomChannelを作成します。前回作成したんですが、どうやらmixでも生成できるみたいなので作り直します。HelloWeb.RoomChannellib/hello_web/channels/user_socket.exに上記の情報を追加し、connect関数を書き換えます。nilendchannelのルーティングの方は前回通りです。connectはパラメータからユーザーIDを取り出し、ソケット情報にアサイン(登録)しておきます。接続ユーザー一覧とかを作成したい意図でしょう。nilですが、サンプルではとくに記載されていませんがコレが無いとUserSocketの生成の際にエラーが発生するようです。Channel編集RoomChannelでPresenceを利用するように編集していきます。endend接続を確立した際にsend/2で:after_joinというメッセージを送っています。これはChannelの内部実装がGenServerになっていて、send/2でメッセージを送信しているようなものでしょう。doの処理が始まるといったところです。肝心のhandle_infoでは"presence_state"というイベントにPresence.list(socket)というメッセージを載せてプッシュ(サブスクライブ)しています。Presenceはどうやら"presence_state"や"presence_diff"といったイベントを監視して状態を共有しているのだと思います。Presence.trackの箇所はユーザーID毎にどのタイミングまでオンラインであったかをトラッキングできるように情報を更新しているようなものでしょうか。inspect(System.system_time(:second))で現在のシステムの時間がUNIX時間で格納されるようです。クライアントサイドを実装するphoenix.jsを使っているクライアント側でPresenceを取得したりするよう実装します。"presence_state"や"presence_diff"といったイベントが発生した際にpresence.onSyncでその時に実行されるコールバック関数を登録できるようです。現在接続しているユーザーを表示している際などは、レンダリング関数などもこのコールバックの中に入れておくのが望ましいでしょう。Presenceに登録されている情報を取得するにはpresence.listを利用するようです。id(今回でいうユーザーID)と、第二引数は登録してあるメタ情報(今回で言うonline_atのマップ)が利用できます。サンプルではassets/js/app.jsを利用してますが、ここでは前回同様assets/js/socket.jsを編集します。socket上記で#mainを利用するような記述に変更しましたのでlib/hello_web/templates/page/index.html.eexを編集しておきます。id="main"></div>今回は接続しているクライアントを表示するだけにします。実際にブラウザで見てみましょう。実際にブラウザで見てみます。http://localhost:4000/?name=Aliceを2個、http://localhost:4000/?name=Bobを1個のブラウザで開きます。ここでは写し忘れましたが、ブラウザを閉じるときちんと名前が消えたりカウントが減ったりします。厳密にはnameというクエリを取得しているわけではないんですが、とりあえずそれっぽいものはできました。実際の実装ではクエリ文字からユーザーIDを取得するよりかはユーザートークンから取得することになりそうです。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。