- 投稿日:2020-01-15T20:00:42+09:00
Railsで作ったSNSでユーザの投稿へのコメント作成機能を実装する
・参考URL
https://sadah.github.io/rails-training/ja/004_comments.html今回紹介するコードは、プログラミングスクールで学んだTwitterクローンを基にしたRailsのアプリ(Micropost)への追加機能であることを前提にするとコードが読みやすくなると思います。
モデルの作成
rails g model Comment content:string user:references micropost:referencesコメントはUserとMicropostの多:多の関係性を表すため、中間テーブルが必要になります。
Userがコメントする投稿が複数あって、MicropostにはコメントしたUserが複数いるためです。マイグレーションファイル
年月日時_create_relationships.rbclass CreateComments < ActiveRecord::Migration[5.2] def change create_table :comments do |t| t.string :content t.references :user, foreign_key: true t.references :micropost, foreign_key: true t.timestamps end end endマイグレーションを忘れずに。
rails db:migrate自動作成されたコメントモデルにバリデーションを付けておきましょう。
コメントモデル
comment.rbclass Comment < ApplicationRecord belongs_to :user belongs_to :micropost validates :content, presence: true, length: { maximum: 255 } endコメントを文字なしで投稿できないように設定します。
関連モデル
Userモデル
user.rbclass User < ApplicationRecord has_many :microposts has_many :comments end一つのUserに対して多数のコメントがあるためこの関係になります。
Micropostモデル
micropost.rbclass Micropost < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy end一つのMicropostに対して多数のコメントがあるためこの関係になります。
Micropostモデルに
dependent: :destroy
を追記したことで、投稿が削除されるとそれに紐づいたコメントが削除されるようになります。
dependent: :destroy
がないと、コメントの付いた投稿を削除するときにエラーが起きます。micropost.rbhas_many :commenters, through: :comments, source: :userMicropost側から見ると、コメントしてくるユーザが多数いるので、正確にモデル作成するなら以下のコードの追記が必要かもしれません。(今回使うことはありませんでしたが、間違っていたらごめんなさい)
ルーティング
routes.rbRails.application.routes.draw do resources :microposts, only: [:create, :destroy, :show] do resources :comments, only: [:create, :destroy] end endどのMicropostに対してコメントなのかを明らかにするためにルーティングのネスト(入れ子構造)をします。
コントローラー作成
$ rails g controller comments create destroyコメントで必要な機能は作成機能(のちに削除機能)の2つだけなのでターミナルへの入力はこのようになります。
comments_controller.rbclass CommentsController < ApplicationController before_action :require_user_logged_in def create @micropost = Micropost.find(params[:micropost_id]) @comment = @micropost.comments.build(comment_params) @comment.user_id = current_user.id if @comment.save flash[:success] = '投稿にコメントしました。' redirect_back(fallback_location: root_path) else @micropost = Micropost.find(params[:micropost_id]) @comments = @micropost.comments.includes(:user) flash.now[:danger] = '投稿へのコメントに失敗しました。' render 'microposts/show' end end private def comment_params params.require(:comment).permit(:content) end endコメントはmicropostと紐づいているため
@micropost
を頭につけます。
コメントとuserを紐づけるために@comment.user_id = current_user.id
が必要です。
これがないとコメントからuserデータを得られません。else文が実行されるときrenderするファイルに
@micropost
と@comments
の
2つのインスタンス変数を渡すのは、micropostのshowファイルを表示するために必要だからです。
片方でも欠けるとエラーになります。関連コントローラー
コメント作成を投稿の詳細ページ(show)で実行できるようにします。
microposts_controller.rbclass MicropostsController < ApplicationController before_action :require_user_logged_in def show @micropost = Micropost.find(params[:id]) @comments = @micropost.comments.includes(:user) @comment = @micropost.comments.build(user_id: current_user.id) if current_user # form_with 用 end endshowページの
@micropost = Micropost.find(params[:id])
で特定の一つのmicropostを表示できるように設定します。
@comments = @micropost.comments.includes(:user)
により、対応する投稿へのコメントを一覧表示することができます。
includes(:user)
はコメントしたuserを表示するためにuserカラムを取得するメソッドです。これがないとgravatar_url
などを表示する時にuser
がnilになりエラーが起こります。
@comment
には、コメント入力フォームの表示エラーを避けるために空のコメントを入れておきます。comments_controller.rbclass CommentsController < ApplicationController before_action :require_user_logged_in def create @micropost = Micropost.find(params[:micropost_id]) @comment = @micropost.comments.build(comment_params) @comment.user_id = current_user.id if @comment.save flash[:success] = '投稿にコメントしました。' redirect_back(fallback_location: root_path) else @micropost = Micropost.find(params[:micropost_id]) @comments = @micropost.comments.includes(:user) flash.now[:danger] = '投稿へのコメントに失敗しました。' render 'microposts/show' end end private def comment_params params.require(:comment).permit(:content) end end基本的には
microposts_controller
のcreateと同じようにします。(microposts_controllerに関してはRails Tutorialなどを参考にしてください)
コメントはmicropost
に紐づくように作成したいので、対応するmicropost
の
インスタンスを記入しておきます。
@comment.user_id = current_user.id
がないとコメントにuser情報が入らないため必須です。
@micropost
と@comennts
両方のインスタンスを代入しないとrender先のshowファイルで
コメント失敗メッセージを表示できずにエラーが起こります。ルーティング
投稿詳細ページに飛ぶためのルーティングをターミナルを使って確認します。
$ rails routes
microposts#show
ページに飛ぶために必要なPrefixを確認しますmicropost GET /microposts/:id(.:format) microposts#show投稿一覧のパーシャルに以下の1行を追加して、投稿詳細ページに移動できるようにしましょう。
microposts一覧のパーシャルファイルの一部<% microposts.each do |micropost| %> 〜 <%= link_to 'Comments', micropost_path(micropost), class: 'btn btn-link btn-sm' %> 〜 <% end %>microposts#showのPrefixが
micropost
なのでmicropost_path
と記入します。
micropost一覧から特定の|micropost|
のページに飛びたいので、
micropost_path
の()内には||内のmicropost
を代入します。Viewファイル
投稿詳細ページ
コメントしたい特定のmicropostを表示するファイルです。
micropostを表示させるためには、controllerのdef show
で定義した
@micropost
のインスタンスをshowファイルに渡せるように注意しましょう。microposts/show.html.erb<ul class="list-unstyled"> <li class="media mb-3"> <img class="mr-2 rounded" src="<%= gravatar_url(@micropost.user, { size: 50 }) %>" alt=""> <div class="media-body"> <div> <%= link_to @micropost.user.name, user_path(@micropost.user) %> <span class="text-muted">posted at <%= @micropost.created_at %></span> </div> <div> <p><%= @micropost.content %></p> </div> <div class="btn-group"> <% if current_user == @micropost.user %> <%#=詳細ページで削除するとid見つからないエラー発生 link_to "Delete", @micropost, method: :delete, data: { confirm: "You sure?" }, class: 'btn btn-danger btn-sm' %> <% end %> <%= render 'favorites/favorite_button', micropost: @micropost %> </div> </div> </li> </ul> <%# コメント入力フォームのパーシャル %> <%= render 'comments/form', micropost: @micropost %> <%# コメント一覧のパーシャル %> <%= render 'comments/comments', micropost: @micropost %>投稿詳細ページでmicropostを削除すると、micropostの削除自体はできても
ルーティングエラーが起きてしまうため、現在は削除ボタンを使えないようにしています。コメント入力フォーム
form_with
のmodelに@micropost
と@comment
が入るようにしないとエラーが起こります。comments/_form.html.erb<div class="col-sm-8"> <%= form_with(model: [@micropost, @comment], local: true) do |f| %> <div class="form-group"> <%= f.text_area :content, class: 'form-control', rows: 3 %> </div> <%= f.submit 'Comment', class: 'btn btn-primary btn-block' %> <% end %> </div>コメント一覧
comments/_comments.html.erb<ul class="list-unstyled"> <% @comments.each do |comment| %> <li class="media mb-3"> <img class="mr-2 rounded" src="<%= gravatar_url(comment.user, { size: 50 }) %>" alt=""> <div class="media-body"> <div> <%= link_to comment.user.name, user_path(comment.user) %> <span class="text-muted">posted at <%= comment.created_at %></span> </div> <div> <p><%= comment.content %></p> </div> <div class="btn-group"> <% if current_user == comment.user %> <%#= link_to "Delete", micropost_comment_path(comment), method: :delete, data: { confirm: "You sure?" }, class: 'btn btn-danger btn-sm' %> <% end %> </div> </div> </li> <% end %> <%#= paginate comments %> </ul>コメント削除機能は正しいルーティングができていないので、まだ未実装です。
- 投稿日:2020-01-15T20:00:42+09:00
Railsで作ったSNSアプリでユーザの投稿へのコメント作成機能を実装する
・参考URL
https://sadah.github.io/rails-training/ja/004_comments.html今回紹介するコードは、プログラミングスクールで学んだTwitterクローンを基にしたRailsのアプリ(Micropost)への追加機能であることを前提にするとコードが読みやすくなると思います。
モデルの作成
rails g model Comment content:string user:references micropost:referencesコメントはUserとMicropostの多:多の関係性を表すため、中間テーブルが必要になります。
Userがコメントする投稿が複数あって、MicropostにはコメントしたUserが複数いるためです。マイグレーションファイル
年月日時_create_relationships.rbclass CreateComments < ActiveRecord::Migration[5.2] def change create_table :comments do |t| t.string :content t.references :user, foreign_key: true t.references :micropost, foreign_key: true t.timestamps end end endマイグレーションを忘れずに。
rails db:migrate自動作成されたコメントモデルにバリデーションを付けておきましょう。
コメントモデル
comment.rbclass Comment < ApplicationRecord belongs_to :user belongs_to :micropost validates :content, presence: true, length: { maximum: 255 } endコメントを文字なしで投稿できないように設定します。
関連モデル
Userモデル
user.rbclass User < ApplicationRecord has_many :microposts has_many :comments end一つのUserに対して多数のコメントがあるためこの関係になります。
Micropostモデル
micropost.rbclass Micropost < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy end一つのMicropostに対して多数のコメントがあるためこの関係になります。
Micropostモデルに
dependent: :destroy
を追記したことで、投稿が削除されるとそれに紐づいたコメントが削除されるようになります。
dependent: :destroy
がないと、コメントの付いた投稿を削除するときにエラーが起きます。micropost.rbhas_many :commenters, through: :comments, source: :userMicropost側から見ると、コメントしてくるユーザが多数いるので、正確にモデル作成するなら以下のコードの追記が必要かもしれません。(今回使うことはありませんでしたが、間違っていたらごめんなさい)
ルーティング
routes.rbRails.application.routes.draw do resources :microposts, only: [:create, :destroy, :show] do resources :comments, only: [:create, :destroy] end endどのMicropostに対してコメントなのかを明らかにするためにルーティングのネスト(入れ子構造)をします。
コントローラー作成
$ rails g controller comments create destroyコメントで必要な機能は作成機能(のちに削除機能)の2つだけなのでターミナルへの入力はこのようになります。
comments_controller.rbclass CommentsController < ApplicationController before_action :require_user_logged_in def create @micropost = Micropost.find(params[:micropost_id]) @comment = @micropost.comments.build(comment_params) @comment.user_id = current_user.id if @comment.save flash[:success] = '投稿にコメントしました。' redirect_back(fallback_location: root_path) else @micropost = Micropost.find(params[:micropost_id]) @comments = @micropost.comments.includes(:user) flash.now[:danger] = '投稿へのコメントに失敗しました。' render 'microposts/show' end end private def comment_params params.require(:comment).permit(:content) end endコメントはmicropostと紐づいているため
@micropost
を頭につけます。
コメントとuserを紐づけるために@comment.user_id = current_user.id
が必要です。
これがないとコメントからuserデータを得られません。else文が実行されるときrenderするファイルに
@micropost
と@comments
の
2つのインスタンス変数を渡すのは、micropostのshowファイルを表示するために必要だからです。
片方でも欠けるとエラーになります。関連コントローラー
コメント作成を投稿の詳細ページ(show)で実行できるようにします。
microposts_controller.rbclass MicropostsController < ApplicationController before_action :require_user_logged_in def show @micropost = Micropost.find(params[:id]) @comments = @micropost.comments.includes(:user) @comment = @micropost.comments.build(user_id: current_user.id) if current_user # form_with 用 end endshowページの
@micropost = Micropost.find(params[:id])
で特定の一つのmicropostを表示できるように設定します。
@comments = @micropost.comments.includes(:user)
により、対応する投稿へのコメントを一覧表示することができます。
includes(:user)
はコメントしたuserを表示するためにuserカラムを取得するメソッドです。これがないとgravatar_url
などを表示する時にuser
がnilになりエラーが起こります。
@comment
には、コメント入力フォームの表示エラーを避けるために空のコメントを入れておきます。comments_controller.rbclass CommentsController < ApplicationController before_action :require_user_logged_in def create @micropost = Micropost.find(params[:micropost_id]) @comment = @micropost.comments.build(comment_params) @comment.user_id = current_user.id if @comment.save flash[:success] = '投稿にコメントしました。' redirect_back(fallback_location: root_path) else @micropost = Micropost.find(params[:micropost_id]) @comments = @micropost.comments.includes(:user) flash.now[:danger] = '投稿へのコメントに失敗しました。' render 'microposts/show' end end private def comment_params params.require(:comment).permit(:content) end end基本的には
microposts_controller
のcreateと同じようにします。(microposts_controllerに関してはRails Tutorialなどを参考にしてください)
コメントはmicropost
に紐づくように作成したいので、対応するmicropost
の
インスタンスを記入しておきます。
@comment.user_id = current_user.id
がないとコメントにuser情報が入らないため必須です。
@micropost
と@comennts
両方のインスタンスを代入しないとrender先のshowファイルで
コメント失敗メッセージを表示できずにエラーが起こります。投稿詳細ページのルーティングの確認
投稿詳細ページに飛ぶためのルーティングをターミナルを使って確認します。
$ rails routes
microposts#show
ページに飛ぶために必要なPrefixを確認しますmicropost GET /microposts/:id(.:format) microposts#show投稿一覧のパーシャルに以下の1行を追加して、投稿詳細ページに移動できるようにしましょう。
microposts一覧のパーシャルファイルの一部<% microposts.each do |micropost| %> 〜 <%= link_to 'Comments', micropost_path(micropost), class: 'btn btn-link btn-sm' %> 〜 <% end %>microposts#showのPrefixが
micropost
なのでmicropost_path
と記入します。
micropost一覧から特定の|micropost|
のページに飛びたいので、
micropost_path
の()内には||内のmicropost
を代入します。Viewファイル
投稿詳細ページ
コメントしたい特定のmicropostを表示するファイルです。
micropostを表示させるためには、controllerのdef show
で定義した
@micropost
のインスタンスをshowファイルに渡せるように注意しましょう。microposts/show.html.erb<ul class="list-unstyled"> <li class="media mb-3"> <img class="mr-2 rounded" src="<%= gravatar_url(@micropost.user, { size: 50 }) %>" alt=""> <div class="media-body"> <div> <%= link_to @micropost.user.name, user_path(@micropost.user) %> <span class="text-muted">posted at <%= @micropost.created_at %></span> </div> <div> <p><%= @micropost.content %></p> </div> <div class="btn-group"> <% if current_user == @micropost.user %> <%#=詳細ページで削除するとid見つからないエラー発生 link_to "Delete", @micropost, method: :delete, data: { confirm: "You sure?" }, class: 'btn btn-danger btn-sm' %> <% end %> <%= render 'favorites/favorite_button', micropost: @micropost %> </div> </div> </li> </ul> <%# コメント入力フォームのパーシャル %> <%= render 'comments/form', micropost: @micropost %> <%# コメント一覧のパーシャル %> <%= render 'comments/comments', micropost: @micropost %>投稿詳細ページでmicropostを削除すると、micropostの削除自体はできても
ルーティングエラーが起きてしまうため、現在は削除ボタンを使えないようにしています。コメント入力フォーム
form_with
のmodelに@micropost
と@comment
が入るようにしないとエラーが起こります。comments/_form.html.erb<div class="col-sm-8"> <%= form_with(model: [@micropost, @comment], local: true) do |f| %> <div class="form-group"> <%= f.text_area :content, class: 'form-control', rows: 3 %> </div> <%= f.submit 'Comment', class: 'btn btn-primary btn-block' %> <% end %> </div>コメント一覧
comments/_comments.html.erb<ul class="list-unstyled"> <% @comments.each do |comment| %> <li class="media mb-3"> <img class="mr-2 rounded" src="<%= gravatar_url(comment.user, { size: 50 }) %>" alt=""> <div class="media-body"> <div> <%= link_to comment.user.name, user_path(comment.user) %> <span class="text-muted">posted at <%= comment.created_at %></span> </div> <div> <p><%= comment.content %></p> </div> <div class="btn-group"> <% if current_user == comment.user %> <%#= link_to "Delete", micropost_comment_path(comment), method: :delete, data: { confirm: "You sure?" }, class: 'btn btn-danger btn-sm' %> <% end %> </div> </div> </li> <% end %> <%#= paginate comments %> </ul>コメント削除機能は正しいルーティングができていないので、まだ未実装です。
- 投稿日:2020-01-15T19:51:56+09:00
MacからGoogleContainerRegistryアクセスでエラー
概要
- ローカルで作成したDockerイメージをGCR(GoogleContainerRegistry)にPUSHしたい
- MacからGCRにDockerImageをPULL/PUSHできない(Winは試していない)
- CloudShellからはPULL/PUSH可能
- どうやら権限周りが原因っぽい
- 以下のようなエラーで弾かれる
- 解決したのでメモしておく
$ docker pull asia.gcr.io/[project-id]/[repository]:latest Error response from daemon: pull access denied for asia.gcr.io/[project-id]/[repository], repository does not exist or may require 'docker login': denied: Permission denied for "latest" from request "/v2/[project-id]/[repository]/manifests/latest".詳細
Docker認証ヘルパーとしてgcloudコマンド発行
$ gcloud auth configure-docker※アップデートを促されるメッセージが出た場合は"gcloud components update"を実行してから
- GCRの実態はGoogleCloudStorageなのでこれに権限を与えてみる
- 「ストレージオブジェクト閲覧者」権限のみ与えてみる
PULLしてみる
$ docker pull asia.gcr.io/[project-id]/[repository]:latest latest: Pulling from [project-id]/[repository] Digest: sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Status: Downloaded newer image for asia.gcr.io/[project-id]/[repository]:latest asia.gcr.io/[project-id]/[repository]:latest確認
$ docker images asia.gcr.io/[project-id]/[repository] REPOSITORY TAG IMAGE ID CREATED SIZE asia.gcr.io/[project-id]/[repository] latest f7bb5701a33c 2 weeks ago 126MBPULLできてる
PUSHもしてみる
$ docker image push asia.gcr.io/[project-id]/[repository] The push refers to repository [asia.gcr.io/[project-id]/[repository]] 75248c0d5438: Preparing 49434cc20e95: Preparing 556c5fb0d91b: Preparing denied: Token exchange failed for project '[project-id]'. Caller does not have permission 'storage.buckets.get'. To configure permissions, follow instructions at: https://cloud.google.com/container-registry/docs/access-controlやはり閲覧権限だけではPUSHできない様子
「ストレージオブジェクト作成者権限」を付与してみる
PUSHしてみる
$ docker image push asia.gcr.io/[project-id]/[repository] The push refers to repository [asia.gcr.io/[project-id]/[repository]] 75248c0d5438: Preparing 49434cc20e95: Preparing 556c5fb0d91b: Preparing denied: Token exchange failed for project '[project-id]'. Caller does not have permission 'storage.buckets.get'. To configure permissions, follow instructions at: https://cloud.google.com/container-registry/docs/access-controlダメらしい。。
「ストレージオブジェクト管理者」を付与してみる
PUSHしてみる
$ docker image push asia.gcr.io/[project-id]/[repository]:latest The push refers to repository [asia.gcr.io/[project-id]/[repository]] 75248c0d5438: Preparing 49434cc20e95: Preparing 556c5fb0d91b: Preparing denied: Token exchange failed for project '[project-id]'. Caller does not have permission 'storage.buckets.get'. To configure permissions, follow instructions at: https://cloud.google.com/container-registry/docs/access-controlダメらしい
「ストレージ管理者」権限を付与してみる
PUSHしてみる
$ docker image push asia.gcr.io/[project-id]/[repository]:latest The push refers to repository [asia.gcr.io/[project-id]/[repository]] 75248c0d5438: Layer already exists 49434cc20e95: Layer already exists 556c5fb0d91b: Layer already exists latest: digest: sha256:36b77d8bb27ffca25c7f6f53cadd059aca2747d46fb6ef34064e31727325784e size: 948PUSHできた
結論
- MacなどからGCRへのアクセスにはGCSの権限を変更する必要がある
- GCRへのPULLには「ストレージオブジェクトの閲覧者」権限が必要
- GCRへのPUSHには「ストレージの管理者」権限が必要
- gcloudの認証は予め済ませておく
参考
- 投稿日:2020-01-15T17:10:06+09:00
[Mac]画像をepsに変換
- 投稿日:2020-01-15T12:16:07+09:00
aws cli 複数アカウント切り替え(MAC)
複数アカウントで aws cli を使うとき、--profileを指定してコマンドを実行するなどの方法がある。
direnvを使ってディレクトリ単位で付けないようにしてみた。参考
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html
手順概要
- プロファイルを作成する。
- direnvでAWS_PROFILE環境変数をディレクトリごとに設定する
手順詳細
1. プロファイルを作成する
personalってプロファイルを作成する想定だと aws configureで下記の入力をする。
各値は、自分のものを入力する。~ $ aws configure --profile personal AWS Access Key ID [None]: XXXXXXXXXXXXXXX AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]: json2. direnvでAWS_PROFILE環境変数をディレクトリごとに設定する
各ディレクトリに .envrc ファイルを作成して、下記の内容を記載する。
export AWS_PROFILE=personal
- 投稿日:2020-01-15T12:05:01+09:00
ショートカットキー mac
option + 矢印
単語移動command + controll + 矢印
エディタとコンソールを移動command + shift + ↓
カーソルから下を選択option + shift + 矢印
単語ごとを選択
- 投稿日:2020-01-15T10:58:47+09:00
DockerでローカルのLambda実行環境を用意する。
はじめに
ファイルの中身のみ記載します。
ディレクトリ構造
. ├── docker-compose.yml ├── lambda ├── sample └── index.jsdocker-compose.ymlの内容
version: '3' services: addData: container_name: sample-lambda image: lambci/lambda:nodejs12.x volumes: - ./lambda/sample:/var/task ports: - 9001:9001 environment: AWS_DEFAULT_REGION: XXXXXXXX AWS_ACCOUNT_ID: YYYYYYYY DOCKER_LAMBDA_WATCH: 1 DOCKER_LAMBDA_STAY_OPEN: 1 command: index.handlerindex.jsの内容
const aws = require("aws-sdk"); exports.handler = async (event, context) => { context.succeed({ statusCode: 200, body : JSON.stringify('hallo sample!'), }); };いざ実行!
dockerの起動
docker-compose upレスポンス確認
別タブで実行
curl -d '{}' http://localhost:9001/2015-03-31/functions/sample/invocationsレスポンス
{"statusCode":200,"body":"\"hallo sample!\""}終わりに
デプロイなんかもDocker使ってできるみたいですね。
時間があれば、そちらも記載したいと思います