- 投稿日:2021-06-13T23:54:15+09:00
form_withで使用できるhtmlタグ
form_forの書き方 <%= form_with model: @user do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %> メソッド名 機能 f.search_field 検索用ボックス f.label ラベルタグをを生成Total cost f.text_field 1行のテキストボックス f.text_area 複数行のテキストボックス f.email_field メールアドレス用ボックス f.range_field 範囲選択バー f.number_field 数値選択 f.select 選択ボックス f.date_field 日付ボックス f.datetime_field 日付時刻ボックス f.datetime_local_field 日付時刻ボックス(ローカル) f.month_field 月ボックス f.time_field 時刻ボックス f.week_field 週ボックス f.telephone_field 電話番号用ボックス f.file_field ファイル選択用ボックス f.password_field パスワード用ボックス f.hidden_field 隠しフィールド f.check_box チェックボックス f.radio_button ラジオボタン f.url_field URL用ボックス f.color_field 色選択ボックス f.submit 提出用ボタン Pikawaka https://pikawaka.com/rails/form_with
- 投稿日:2021-06-13T23:38:18+09:00
f.select 男女を選択させる
<div class="form-group"> <%= f.label :sex, '性別' %> <%= f.select :sex, {'男性': 1, '女性': 2}, { include_blank: '選択してください'}, { class: 'form-control' , required: true } %> </div> https://techacademy.jp/magazine/37096
- 投稿日:2021-06-13T21:29:47+09:00
Rails初学者によるRailsチュートリアル学習記録⑭ 第12章
目次 1. はじめに 2. 第12章の概要 3. 学習内容 4. 終わりに 1. はじめに この記事は、Rails初学者の工業大学三年生がRailsチュートリアルの学習記録をつけるための記事です。 筆者自体がRailsやWebについて知識が少ないので、内容の解釈などに間違いがある可能性があります。(その時はコメントで指摘してくださると助かります!) Railsチュートリアル内ではRailsの内容以外にも、gitでのバージョン管理やHerokuを使ったデプロイも学習しますが、gitに関しては既に私が学習済みのため学習記録には記述しません。 演習の記録も省略します。 2. 第12章の概要 この章では、ユーザーがパスワードを忘れてしまった時の、パスワードの再設定機能を実装していきます。 パスワードの再設定機能に使用する処理は、11章のアカウントの有効化で使用した処理と似ている個所が多いです。 この機能を使用する流れとしては、ログインフォームの「forgot password」というリンクをクリックしたら、 メールアドレスを入力するフォームに遷移し、メールアドレスを送信するとパスワードの再設定用のメールが届きます。 そのメール内にあるリンクをクリックすると新しいパスワードを入力するフォームに遷移し、 新しいパスワードを送信することでパスワードを更新できます。 パスワードの再設定機能実装の準備 PasswordResetsリソースの追加 Userモデルの変更 パスワードを再設定するためのアクション createアクションでメールを送信する editアクションでパスワードを再設定する updateアクションでパスワードを更新する 3. 学習内容 1. パスワードを再設定機能の実装準備 1-1. PasswordResetsリソースの追加 アカウントの有効化の時と同じように、パスワードを再設定するという処理を、 リソースとして扱えるようにするために、PasswordResetsリソースの作成からはじめます。 アカウントの有効化の機能では、メール内のリンクをクリックしたタイミングで、 データベース内の有効化ステータスを変更したかったので、editアクションのみを使用しましたが、 今回はフォームを生成する必要があるのでnewアクションも使用します。 以下のコマンドを利用してPasswordResetsコントローラを生成します。 rails generate controller PasswordResets new edit 生成されたアクションへのルーティングを追加します。 newアクションとeditアクションで生成されたビューから値を受け取り、 メールの送信やパスワードの再設定を行うcreateアクションとupdateアクションのルーティングも用意する必要があります。 そのために、以下のコードをroutes/rbの最後の行に追加します。 'resources :password_resets, only: [:new, :create, :edit, :update]' これでPasswordResetsリソースのルーティングの追加ができました。 ついでにこのタイミングで、forgot passwordのリンクをログインフォームに追加して、 パスワード再設定画面へ遷移できるようにします。 パスワード再設定画面へのリンクを追加したログインフォーム <% provide(:title, "Log in") %> <h1>Log in</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(url: login_path, scope: :session, local: true) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= link_to "(forgot password)", new_password_reset_path %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :remember_me, class: "checkbox inline" do %> <%= f.check_box :remember_me %> <span>Remember me on this computer</span> <% end %> <%= f.submit "Log in", class: "btn btn-primary" %> <% end %> <p>New user? <%= link_to "Sign up now!", signup_path %></p> </div> </div> この時点で、ログインフォームは以下の画像のようになります。(画像はRailsチュートリアル内から引用) 1-2. Userモデルの変更 アカウントの有効化の機能の実装時に、 ・activation_digest ・activated ・activated_at の3つの属性をUserモデルに追加した時のように、ここでは以下の2つの属性を追加します。 ・reset_digest ・reset_sent_at 前回追加した有効化した日時を格納するactivated_atは有効化機能で使用しませんでしたが、 今回追加する、パスワード再設定用のメールを送った日時を格納するreset_sent_atは、 再設定用のメールに有効期限を持たせるために使用します。 以下のコマンドで属性の追加を行います。 rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime このコマンドを実行することでUserモデルは以下の表のようになります。 属性名 型名 id integer name string email string created_at datetime updated_at datetime password_digest string remember_digest string admin boolean activation_digest string activated boolean activated_at datetime reset_digest string reset_sent_at datetime PasswordResetsコントローラを生成したときにforgot passwordリンクを追加した時のように、 ここではパスワード再設定用のフォームを作成しておきます。 このフォームはメールアドレスの入力フィールドのみのフォームで、 ここにメールアドレスを入力して送信すると、パスワード再設定用のメールが届きます。 パスワード再設定用のフォーム <% provide(:title, "Forgot password") %> <h1>Forgot password</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(url: password_resets_path, scope: :password_reset, local: true) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.submit "Submit", class: "btn btn-primary" %> <% end %> </div> </div> このフォームは以下の画像のようになります。(画像はRailsチュートリアル内から引用) ここまでで、パスワード再設定機能の実装をする準備ができました。 2.パスワードを再設定するためのアクション 2-1. createアクションでメールを送信する createアクションでは、先ほど実装したフォームに入力されたメールアドレスからユーザーを探し、 生成したパスワード再設定トークンをハッシュ化した再設定ダイジェストと、その時のタイムスタンプで データベースの属性を更新します。 それと同時にトークンとメールアドレスが入ったリンクが含まれたメールを送信します。 以下のコードがcreateアクションでのコードです。 app/controllers/password_resets_controller.rb def create @user = User.find_by(email: params[:password_reset][:email].downcase) if @user @user.create_reset_digest #再設定トークンを生成する @user.send_password_reset_email #再設定用メールを送信する flash[:info] = "Email sent with password reset instructions" redirect_to root_url else flash.now[:danger] = "Email address not found" render 'new' end end コメントを入れた2行でそれぞれトークンの生成とメールの送信を行っています。 これらのメソッドはUserモデルに追加します。 app/models/user.rb class User < ApplicationRecord attr_accessor :remember_token, :activation_token, :reset_token before_save :downcase_email before_create :create_activation_digest # パスワード再設定の属性を設定する def create_reset_digest self.reset_token = User.new_token #トークンの生成 update_attribute(:reset_digest, User.digest(reset_token)) #トークンをハッシュ化してreset_digestに格納 update_attribute(:reset_sent_at, Time.zone.now) #reset_sent_atにタイムスタンプを格納 end # パスワード再設定のメールを送信する def send_password_reset_email UserMailer.password_reset(self).deliver_now #メールを送信する end これでパスワードの再設定用のメールを送信する機能が実装できました。 ここからはメイラーの設定を行っていきます。 ただ、設定内容はアカウントの有効化の時と同じで、違う部分としてはメールの中に入れる リンクとメールの文章くらいです。 app/mailers/user_mailer.rb class UserMailer < ApplicationMailer def account_activation(user) #有効化用のメールのメソッド @user = user mail to: user.email, subject: "Account activation" end def password_reset(user) #パスワード再設定用のメールのメソッド @user = user mail to: user.email, subject: "Password reset" #送信先をuserのメールアドレスに、件名をPassword resetに設定 end end app/views/user_mailer/password_reset.html.erb <h1>Password reset</h1> <p>To reset your password click the link below:</p> <%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %> <p>This link will expire in two hours.</p> <p> If you did not request your password to be reset, please ignore this email and your password will stay as it is. </p> これにより、 https://example.com/password_resets/<再設定トークン>/edit?<メールアドレス> というリンクが含まれたメールが送信できます。 2-2. editアクションでパスワードを再設定する これでメールを送信する準備が整いました。 ここからはリンクをクリックされた後のeditアクションの動作を定義していきます。 まず、メール内のリンクをクリックしたときに遷移するフォームが必要です。 このフォームではパスワードの入力フィールドを2つ置き、1つは確認用として使用します。 この後、そこに入力されたパスワードでデータベースの属性を更新したいのですが、 ここで1つ、このフォーム特有の処理を加える必要があります。 それはリンクから受け取ったメールアドレスを、隠しフィールドとしてページ内に保存しておく処理です。 なぜこの処理が必要なのかというと、updateアクションでメールアドレスを基にユーザーを検索し、 そのユーザーのパスワードを更新したいのですが、 editアクションでリンクからメールアドレスを取得しても、何もしないとフォーム送信時にその情報が消えてしまうからです。 よって、隠しフィールドとしてメールアドレスを保存することで、、フォームの送信時に新しいパスワードと一緒に メールアドレスが送信されるようにします。 パスワード再設定のフォーム <% provide(:title, 'Reset password') %> <h1>Reset password</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(model: @user, url: password_reset_path(params[:id]), local: true) do |f| %> <%= render 'shared/error_messages' %> <%= hidden_field_tag :email, @user.email %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Update password", class: "btn btn-primary" %> <% end %> </div> </div> <%= hidden_field_tag :email, @user.email %>この部分でユーザーのメールアドレスを 隠しフィールドで保存しています。 ここからeditアクションにバリデーションを加えます。 パスワードを再設定しようとしているユーザーが有効化されているか、 リンク内の有効化トークンは最初にデータベースに保存した記憶ダイジェストと一致するかを検証します。 app/controllers/password_resets_controller.rb class PasswordResetsController < ApplicationController before_action :get_user, only: [:edit, :update] before_action :valid_user, only: [:edit, :update] def edit end private def get_user @user = User.find_by(email: params[:email]) end # 正しいユーザーかどうか確認する def valid_user unless (@user && @user.activated? && @user.authenticated?(:reset, params[:id])) redirect_to root_url end end end 2-3. updateアクションでパスワードを更新する ここまでで、パスワード再設定のフォームへの遷移、メールの送信、新しいパスワードの送信までが実装できました。 ここからは、送信された新しいパスワードを基にデータベースの値を更新します。 ただ、新しいパスワードが送られたらどのような場合でも更新できてしまっては危険です。 具体的には次の3つの条件を設けます。 1. パスワード再設定の有効期限が切れていないか 2. 無効なパスワードは受け付けず、エラー文を表示する 3. 新しいパスワードが空文字列ではないか(ユーザー情報の編集で空入力を許可していた) これらの条件を満たした時にパスワードの更新が行われるようにします。 2はrenderメソッドでフォームを再表示する。3はempty?メソッドとrenderメソッドで空文字だった時に フォームが送信されないようにすることでそれぞれ実装できます。 1の有効期限の判断は、password_reset_expired?とcheck_expirationというメソッドを定義することで 実装していきます。 1つめのpassword_reset_expired?メソッドが有効期限内の時にfalse、有効期限を過ぎたときにtrueを返すメソッドで、 Userモデルに定義します。 2つめのcheck_expirationは上記のpassword_reset_expired?メソッドを呼び出し、 有効期限が切れていた時にエラー文を表示させて、 パスワード再設定用のメールアドレスを入力するフォームにリダイレクトします。 app/models/user.rb def check_expiration if @user.password_reset_expired? flash[:danger] = "Password reset has expired." redirect_to new_password_reset_url end end app/controllers/password_resets_controller.rb def check_expiration if @user.password_reset_expired? flash[:danger] = "Password reset has expired." redirect_to new_password_reset_url end end そして、上記のメソッドを使用して3つの条件を設けたupdateアクションが以下のコードです。 updateアクション class PasswordResetsController < ApplicationController before_action :get_user, only: [:edit, :update] before_action :valid_user, only: [:edit, :update] before_action :check_expiration, only: [:edit, :update] # 有効期限が切れてないかを最初に判断する def update if params[:user][:password].empty? # 空文字かどうかを判断する @user.errors.add(:password, :blank) render 'edit' elsif @user.update(user_params) # パスワードを更新する log_in @user flash[:success] = "Password has been reset." redirect_to @user else render 'edit' # パスワードが無効の場合フォームを再表示する end end end 以上で、この章で実装したかったパスワードの再設定機能が実装できました。 4. 終わりに この章は11章で扱った内容と似た部分が多かったので、 内容を進めるのも記事を書くのもあまり時間をかけずに進められました。 今まで実装したものはHerokuでデプロイしてるので、自分が開発したアプリで実際にユーザー登録できて感動です。 まだユーザー登録の機能しかないですが、残り2章でマイクロポストの投稿機能とユーザーのフォロー機能を 実装していきます。 どちらも内容がかなり濃いので時間がかかりそうですが頑張ります。
- 投稿日:2021-06-13T20:54:39+09:00
【AWS】S3を用いた画像のアップロード
目的 AWSのS3を使用し、画像をアップロードする。 開発環境 macOS: Big Sur Rubyバージョン: 2.6.5 Railsバージョン: 6.0.0 前提 S3のバケットが作成されている。 【AWS】S3バケット作成 アプリが作成されている。 【Rails】簡単な投稿アプリの作成 画像アップロード機能が実装されている。 【Rails】画像アップロード機能の導入 手順 はじめに Gemのインストール development.rbの編集 storage.ymlの編集 環境変数の設定 環境変数の使用 git-secretsの導入 git-secretsの条件設定 GitHub Desktopの利用設定 本番環境でのS3設定 Heroku上での環境変数設定 はじめに 今回はAWSのS3を用いた画像アップロードを実装していきます。 Gemのインストール まずはローカル環境での実装を行います。 なお、以降のコードを編集する際はmasterブランチで作業していきます。 それでは早速始めていきます! S3を使用するために必要なaws-sdk-s3というGemをインストールします! Gemfile #中略 gem "aws-sdk-s3", require: false ターミナル % bundle install これでインストールできました! development.rbの編集 続いて、画像の保存先をlocalからS3に保存されるように設定を変更します! まず、development.rbに記述している画像の保存先の設定を下記のように変更します。 config/environments/development.rb #省略 #config.active_storage.service = :local config.active_storage.service = :amazon #省略 これで画像の保存先が変更できました。 storage.ymlの編集 次に、S3で使用するバケット名とリージョン名を記述します。 storage.ymlに以下のコードを追記します。 config/storage.yml test: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %> amazon: service: S3 region: ap-northeast-1 bucket: 「バケット名を入力」 #省略 「バケット名を入力」の箇所には、自分のバケット名を入力します! 環境変数の設定 最後に、S3にアクセスするための認証情報を設定します。 秘密情報である「Access key ID」と「Secret access key」は直接記載してはいけないため、それぞれの環境変数に入れてソースコードに記述します。 まずは、環境変数を設定するファイルを編集するために、vimコマンドを使用します。 ターミナル $ vim ~/.bash_profile 続いて「i」を入力して、ファイルを編集モードにします。 ターミナル左下に「INSERT」という文字が表示されれば文字が入力できるようになっています。 環境変数に代入する「Access key ID」と「Secret access key」の値を調べるため、「new_user_credentials.csv」というcsvファイルを開き、以下のコードを追加します。 ターミナル export AWS_ACCESS_KEY_ID="CSVファイルのAccess key IDの値を貼り付け" export AWS_SECRET_ACCESS_KEY="CSVファイルのSecret access keyの値を貼り付け" 値を貼りつけたら「escキー」→「:wq」の順で入力し、環境変数の設定ファイルを保存します。 次にsourceコマンドを入力し、先ほど設定した環境変数を使えるようにします。 $ source ~/.bash_profile sourceコマンドとは現在開いているターミナルのタブで環境変数の設定ファイルを読み込み直してくれるものです。 環境変数の使用 ここまでで、環境変数を設定することができました! 実際にソースコード内で環境変数を使用して、S3への認証情報を記述します。 config/storage.yml test: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %> amazon: service: S3 region: ap-northeast-1 bucket: (自分のバケット名が記載されている状態) access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %> #省略 これで安全にソースコード内で安全に秘密情報を記述することができました! git-secretsの導入 ここまでの作業で、環境変数は設定できました。 しかし、うっかりミスで環境変数を使用し忘れてしまい、誤って秘密情報をGitHubにpushしてしまう可能性もあります。 そのため、誤操作で秘密情報をpushしないよう対策していきます! AWSが公開している「git-secrets」というツールを使用して、誤ってGitHubにpushしないよう設定していきます。 git-secretsはcommitしようとしたコードをチェックし、パスワードだと推定されるような文字列が含まれている場合は、警告を出して処理が中断してくれる機能です。 まずはgit-secretsのインストールです。 ターミナル(ホームディレクトリで実行すること) % brew install git-secrets git-secretsが導入できたら、設定を適用したいアプリケーションのディレクトリに移動して、git-secretsを有効化します。 ターミナル % git secrets --install これで、git-secretsを使用する準備ができました! git-secretsの条件設定 続いて、どのようなコードのcommitを防ぐのかを設定していきます。 下記のコマンドを実行し、「Access key ID」や「Secret access key」など、アップロードしたくないAWS関連の秘密情報を一括で設定します。 ターミナル % git secrets --register-aws --global これで条件が設定出来ました! GitHub Desktopの利用設定 commitなどソースコード管理をGitHub Desktopを使用して行なっている場合は、また追加で設定が必要になります。 以下のコマンドを実行してGitHub Desktopにgit-secretsを適用させます! ターミナル % sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/app/git/bin/git-secrets このコマンドの際にパスワードの入力が必要となる場合があります。このパスワードはPCにログインする際のパスワードです。 git-secretsが適用範囲設定 ここまでの設定では、今後作成するリポジトリにはgit-secretsが適用されません。 そのため、以下のコマンドを実行して自動で適用されるようにします! % git secrets --install ~/.git-templates/git-secrets % git config --global init.templatedir '~/.git-templates/git-secrets' これで、今後作成する他のアプリケーションにも、git-secretsが適用されるようになりました! ここまでで、ローカル環境での設定とセキュリティ対策ができたので、実際に投稿した画像ファイルがS3に保存されるか確認してみましょう。問題がないようでしたら、成功です! 本番環境でのS3設定 続いてローカル環境での設定と同様に、画像の保存先を指定します。 現状では画像の保存先がローカルに設定されているため、S3に保存されるように設定を変更します。 まずproduction.rbに記述している画像の保存先の設定を「:local」→「:amazon」に変更します。 config/environments/production.rb #省略 #config.active_storage.service = :local config.active_storage.service = :amazon #省略 Heroku上での環境変数設定 Herokuで環境変数を扱うには、heroku config:setコマンドを打ちます。 ターミナル % heroku config:set AWS_ACCESS_KEY_ID="CSVファイルのAccess key IDの値を貼り付け" ターミナル % heroku config:set AWS_SECRET_ACCESS_KEY="CSVファイルのSecret access keyの値を貼り付け" 環境変数が正しく設定できているかを確認するために、下記のコマンドを入力してください。 % heroku config 確認できたら、コミットしてHerokuに反映させます。 ターミナル % git push heroku master その後、本番環境で挙動確認し問題がなければ完了です。 最後に 以上で、S3を用いた画像アップロード機能実装は完了です。 では。
- 投稿日:2021-06-13T18:24:09+09:00
Herokuにデプロイ後にCSVで作成したデータをテーブルに登録したい Rails
あらかじめデプロイする前に db/seeds.rb require "csv" CSV.foreach("db/archive.csv", headers: true) do |row| Archive.create( id: row["id"], archiveid: row["archiveid"], archivetitle: row["archivetitle"], archiveviews: row["archiveviews"], archivepostdate: row["archivepostdate"], archiveurl: row["archiveurl"], archiveimg: row["archiveimg"], created_at: row["created_at"], updated_at: row["updated_at"], ) end を記述して、該当のディレクトリにCSVを格納しておきます。 これは要はHerokuにデプロイした後に、 heroku run rake db:seed を実行して、インポートしようという算段です。 Herokuへのデプロイのやり方は省略します。 ただし、今回はデプロイ時にいくつかポイントがありました。 ローカル環境でもそうでしたが、今回絵文字を多用しているので、テーブルに登録するデータのエンコードはutf8mb4にしなければなりません。 ということで、 heroku run rails db:migrate する前に、設定を変更しましょう。 heroku config:set DATABASE_URL="mysql2://<username>:<password>@<host>/<database>?reconnect=true&encoding=utf8mb4" これでMySQLのエンコードをutf8mb4にすることになります。 そして db/migrate/20210************_create_***.rb class CreateMusics < ActiveRecord::Migration[6.0] def change create_table :musics, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4' do |t| #OPTION以降を追記 コミット、プッシュして、Herokuにもプッシュ この状態で heroku run rails db:migrate でマイグレーション。 更に heroku run rake db:seed これで登録ができるはず・・・・ トラブルシューティング 実際はこれでできなかったから困りました。 そこで対応したこととしては ①Herokuへ再度プッシュ ②DBのリセット 今回は②が有効でした。 heroku run rails db:migrate:reset DISABLE_DATABASE_ENVIRONMENT_CHECK=1
- 投稿日:2021-06-13T17:24:49+09:00
SyntaxErrorでエラーを起こしました
こんにちは! エラーが発生したのをきっかけに、 SyntaxErrorInTemplateについて調べて アウトプットしたいと思います! エラー内容 上記に該当する実際のVSコード SyntaxErrorInTemplateというエラー内容です。 SyntaxErrorInTemplateについて調べてみたところ 以下のエラー問題点に関する仮説を立てました。 ①ビューに乱れがある? ②Controller#showとshow.html.erb間で 読み込めないていないので Controller#showで行った変数定義に誤りがあるのか? ①だとするならば36行目がの記述に誤りがあるのかと 思い、「,」や()の抜け漏れやspellチェックなどを確かめてみましたが エラーは解消できませんでした。 ②の場合、Controller#showを調べてみましたが 特に誤った記述は確認できませんでした。 SyntaxErrorInTemplateはviewに関するエラーの可能性が高い 正直ね、ここで僕3時間ぐらい悩みました。 これだけ悩んだら質問していいだろうと思い メンターの方に聞いてみました。 そもそもSyntaxError自体は構文エラーで 書き方に誤りがあるのでビューファイルに 問題がある可能性が高いとのことを伺いました。 本当にありがとうございました。 user_signed_in?メソッドの使用方法に誤り <% if user_signed_in? %> と記述しなきゃいけないところを <% if user_signed_in ? %> とinと?の間に半角スペースがあったことで user_signed_in?メソッドが処理されなかったことで エラーが起こっていただけでした。
- 投稿日:2021-06-13T17:18:07+09:00
git push heroku masterでエラー出ちゃった
git push heroku masterコマンドでエラーが起こったので アウトプットしていきたいと思います。 エラー内容 Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used. bundlerに関するエラー? Failed to install gems via Bundler. こちらも同様にbundlerのバージョンに関するエラーなのか? と思いbundler -vコマンドでバージョンを調べて見ると Bundler version 2.1.4 でバージョンは問題ナッシングソウマッチ。 gem同士のバージョンに関するエラーでした remote: Unable to find a spec satisfying nokogiri (>= 1.8.5) in the set. Perhaps the remote: lockfile is corrupted? Found nokogiri (1.11.7-x86_64-darwin), nokogiri remote: (1.11.7-x86_64-darwin), nokogiri (1.11.7-x86_64-darwin), nokogiri remote: (1.11.7-x86_64-darwin), nokogiri (1.11.7-x86_64-darwin) that did not match the remote: current platform. nokogiriを満たすスペックが見つかってませんよ!!と怒られてます。 gem同士のバージョンにすれ違いがある為エラーが起こっているようです。 bundle update をして git push heroku masterコマンドを 入力すると解決することができました。
- 投稿日:2021-06-13T17:11:23+09:00
【第3章】Railsチュートリアルで急にファイルの保存ができなくなった時にやったこと
Railsチュートリアル第3章(第6版)で、急にファイルが保存できなくなった。 やったこと① ファイル保存時に表示されるメッセージを読んだ ファイルを保存しようとすると、メッセージが表示されて保存が出来ない。 メッセージを読むもよく分からず、調べてみると以下のような記事があった。 『ENOSPCってなんやねん!!』 @kaitaku どうやらENOSPCはディスクに十分な容量がないことを意味するらしい。 上記の記事の参考元はこちら↓ https://github.com/facebook/create-react-app/issues/4301 やったこと② チュートリアルを遡り容量の上げ方を確認した そういえば、どこかで容量について触れられていたような・・・。と思い探してみると普通に書いてあった。 これをやっておけば良かったのか。。。 ということでRailsチュートリアルのヘルプページを確認する。 やったこと③ Railsチュートリアルのヘルプページに従って、hello_appとtoy_appを削除した 後はヘルプページに従うのみ。 今後のチュートリアルでは第3章で作成したsample_appだけを使うようなので、まずは以前の章で作成したhello_appとtoy_appを思い切って削除する。ソースコードはGitHubにpushしているので安心。 再度ファイルを保存してみると、無事メッセージが表示されずに保存することができた。 手順にはEBSのボリュームを増やす方法も書いてあるので、やっておいた方が良いと思われる。 まとめ やったこと①②③の流れで解決できた。
- 投稿日:2021-06-13T17:02:57+09:00
Railsチュートリアル2章でUsersページを表示できなかった理由
教材の順序どおりにやっているとどうしてもできないところがありました。 これの直接的な原因は、おそらく私がRailsのバージョンを教材指定ではない版で(勝手に)やっていたせいかと想像しています。ただ、ググってみると同じところで詰まっている人がぼちぼちいるようでしたので、そういった方のヒントになればと思います。 どこで詰まったか 2.2.1 ユーザーページを探検するに /usersを表示すればすべてのユーザーの一覧が表示されますし、/users/newを表示すれば新規ユーザー作成ページが表示されます。 とありますが、そのとおりURL末尾に/usersを指定しても、 We're sorry, but something went wrong.とエラーが表示されるだけでした。 ターミナルのログを見ると、No route matches [GET] "/users"と出ていました。ルーティングが悪かろうなというのは直感的に感じていましたが、やはりそのようです。 ではそのルーティング設定はどうなっていたかというと、この時点は以下のようになっていました。 routes.rb Rails.application.routes.draw do root 'application#hello' end 確かにusers的な要素が足りていない感はあるものの、何をどうすればいいか(チュートリアル中のチュートリアル段階のため)分からず四苦八苦していました。 解決策 次のセクションである2.2.2 MVCの挙動のリスト2.6に、routes.rbの内容が書いてあります。 ここを見ると以下のようになっておりまして、この通りにすると無事にusersページが表示されました。 routes.rb Rails.application.routes.draw do resources :users root 'application#hello' end なんで下に書いてるんだと思いましたが、私が見落としていたのかもですよね……同じようなことで困っている人の参考になれば幸いです。 (追記) その後の章を読んでいると、"scaffoldジェネレータでRailsのroutesファイルが更新される"という記述がありました。しかし、再度試してみたのですがやはり自動では追記されませんでした。やはりバージョンによる差異かはたまた私が何かをトチっているのか……
- 投稿日:2021-06-13T16:37:56+09:00
【AWS】S3バケット作成
目的 AWSのS3を使用するためにバケットを作成する。 開発環境 macOS: Big Sur Rubyバージョン: 2.6.5 Railsバージョン: 6.0.0 前提 AWSアカウントのルートユーザー、IAMユーザーが作成されている。 手順 はじめに S3とは バケットとは リージョンとは バケットの作成 IAMユーザーの情報取得 バケットポリシーの設定 はじめに 今回はS3を使用する前準備として、自分用の保存場所を作成していきます。 S3とは AWSの中のサービスの1つであり、Simple Storage Serviceを略してS3と呼んでいます。 バケットとは S3では自分用のデータを保存する場所のことをバケットと呼びます。 リージョンとは S3に保存されたデータは実在の施設に分散して保管されています。 その実在の施設が所在している場所をリージョンといいます。 バケットの作成 それでは、実際にバケットを作成していきます! この作業はルートユーザーのまま設定を進めます。 ルートユーザーではないと確認できない情報があるため、もしIAMユーザーでログインしている場合は、ルートユーザーにログインし直しましょう。 まず画面上部のメニューバーからサービスをクリックし、サービス一覧からS3を選択してS3ページを開きます。 その後バケットを作成を選択し、S3バケットのページに遷移します。 次にバケット名を入力し、リージョンがアジアパシフィック(東京)になっていることを確認します。 バケットの名前はアクセスするときのURLに使用されるため、英数字で、まだ誰も付けたことがない名前を使う必要があります。 画面を下にスクロールすると、アクセス許可の設定が表示されます。 今回はバケットポリシーというものを使用して、S3のセキュリティ対策を行います。 まずは、パブリックアクセスをすべてブロックのチェックを外し、画像のように3つのチェックボックスにチェックを入れます。 この設定が誤っているとファイルのアップロードができなくなってしまいます。 入力できたら、下にスクロールししましょう。 オプション設定が表示されますが、何も入力せず、バケットを作成をクリックしましょう。 ここまでで、バケットを作成することができました。 IAMユーザーの情報取得 次にバケットポリシーの設定をするため、IAMユーザーの情報を取得します。 まずIAMユーザーの情報を取得します。画面上部のメニューバーからサービスをクリックし、IAMページへ遷移します。 次に左のサイドバーからユーザーをクリックし、IAMユーザーページの作成したIAMユーザー名をクリックします。 ユーザー概要のページに遷移したあと、「ユーザーのARN」をコピーします。 この「ユーザーのARN」は後ほど使用します! バケットポリシーの設定 次に、バケットポリシーの設定を行います。 バケットポリシーとは、バケットに対して、どのユーザーがどの処理をできるか取り決めをするものです。 今回は、IAMユーザーのみバケットにアクセスできるよう設定していきます。 画面上部のメニューバーからサービスをクリックし、サービス一覧からS3を選択します。 その後、先ほど作成したバケット名をクリックします。 次に、画面中央のアクセス許可をクリックし、バケットポリシー設定の、編集をクリックします。 そして、下記のバケットポリシーのコードをコピーし、「ポリシー」という入力欄に貼り付けます。 バケットポリシー { "Version": "2012-10-17", "Id": "Policy1544152951996", "Statement": [ { "Sid": "Stmt1544152948221", "Effect": "Allow", "Principal": { "AWS": "①" }, "Action": "s3:*", "Resource": "arn:aws:s3:::②" } ] } 次に、ユーザーによって異なる個別の情報を入力します。 「①」の箇所に、先ほどコピーした「ユーザーのARN」を入力します。 「②」の箇所に、作成したバケット名を入力します。 問題なければ画面下にスクロールして、「変更の保存」をクリックしましょう。 問題なくバケット作成ができれば成功です。 最後に 以上でバケットの作成は完了です。 次回はアプリに導入する方法を投稿します。【AWS】S3を用いた画像のアップロード では。
- 投稿日:2021-06-13T14:09:16+09:00
[Rails] テストコードとは
はじめに 私はテストコードを書くのが苦手です。特にこれを読めば、「そんなあなたでも大丈夫!テストコードを楽にかける方法が。。。」みたいな記事でもありません。 書くのが苦手な私に、こんなに重要で必要なものだから、書かないといけないんだよ。と再認識させるため、そもそもテストコードとはということをまとめてみました。よろしくお願いします。 テストコード テストコードとはアプリケーション内に記述するコードで、実装した機能が正しく機能するか自動で確かめてくれるコードです。 例えば、「名前の記入欄が空欄だとエラーがでる」や「メールアドレスが空欄だとエラーが出る」など、考えられる挙動を全て書き出し、そのテストコードを記述していきます。 記述するのは大変ですが、一度記述してしまえば、コマンド一つでそれらの挙動を自動で確かめてくれるのです。 RSpec(アールスペック) RSpecはRailsアプリケーションのテストコードを書くために用いられるGemです。 Railsには標準でmini_testというテスト用のGemが導入されていますが、RSpecを使うのが主流だそうです。 なぜテストコードを書くのか 私はいつもブラウザで挙動がうまくいっているのかポチポチして確かめていますが、テストコードは書く必要はあります。 もしブラウザポチポチだけで確認を終えてしまうと、どのようなテストを行ったのか、記録が残りません。 また、ブラウザでポチポチしているのは人間なので、人為的ミスが起こったり、抜けがある可能性が高いです。 加えて、仕様に変更があったときは、もう一度全部確認し直さなければなりません。その変更で他の機能にも影響が及んでいる可能性もありますし。。。 しかしテストコードを書いているとそのようなことは起こらないため、アプリケーションのクオリティが担保がされるものと言えます。 テストコードはアプリケーションの挙動を漏れなく確認したり、変更があったときに迅速に対応できたりと、アプリケーションのクオリティが担保できる重要なものなんですね。 テストコードには書くべきパターンというのが二つあります。それが正常系と異常系です。 正常系 このテストは、ユーザーが開発者の意図する操作を行った時の挙動を確認するテストコードです。 例えば「正しく全ての項目が入力されていればログインできる」というのは、開発者が意図した操作で、正常系です。 異常系 こっちが私は記述量が多くて大変だと感じます。 異常系は正常系の逆で、開発者が意図しない操作をユーザーが行った時の挙動を確認するテストコードです。 例えば「パスワードが空欄だった場合、ログインできない」みたいなものです。 続いてテストの種類についてです。 単体テストコード これはモデルやコントローラーなどの機能ごとに問題がないか確かめるテストコードです。 バリデーションの挙動を確認したりします。先ほどの「パスワードを空欄だった場合、ログインできない」というのはバリデーションがしっかり機能しているか確認している単体テストコードと言えますね。 結合テストコード これはユーザーがブラウザで操作する一連の流れを再現して、問題ないか確かめます。 例えば、「投稿ボタンを押すと投稿完了ページに移動し、TOPページに戻ると先ほど投稿した内容が表示されている」といった一連の流れを一気に確かめます。これはコマンドを押してテストを実行すると、自動でブラウザが開き、アプリケーションが動き出すので、うおおおおおってなります。笑 RSpecの導入と設定 最後にRSpecの導入方法と設定を残しておきます。よく忘れるので。。。 まずはGemfileにGemを追加します。gem 'rspec-rails', '~> 4.0.0'を記述してください group :development, :test doの中に記述すること気をつけてください。(Gemの動作に制限を持たせるため。) Gemfile group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 4.0.0' end 記述できたら、ターミナルで導入したいアプリケーションのディレクトリにいることを確認して ターミナル % bundle install 続いてターミナルで下記のコマンドを実行。 ターミナル % rails g rspec:install 下記のように表示されたら成功 ターミナル create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb 続いて、生成された.rspecファイルを開いて下記のように記述します。(2行目を追加) .rspec --require spec_helper --format documentation これで設定完了です。 おまけで、もし、例えばUserモデルのテストコードが書きたければ、ターミナルで下記のコマンドを実行して作成します。 ターミナル % rails g rspec:model user 最後に オリジナルアプリのテストコードが全くかけておらずこれから取り掛かるのがちょっといやです。 ただテストコードは記述中にバグを見つけられたり、一発でクリアしたりすると気持ちいいので、個人的には取り掛かると結構ハマったりします。
- 投稿日:2021-06-13T13:19:51+09:00
【Rails】世界一丁寧なDeviseの導入手順
0. はじめに はじめに Deviseを導入するとき毎回手順を忘れてしまいませんか? 僕自身毎回DeviseのREADMEを見たり、Qiitaの記事を見たりして進めていたのですが、これを機にまとめようと思ったので記しておきます。 一度この記事を一通り読んでから手を動かすことをおすすめします。 環境 Ruby: 2.6.6 Rails: 6.0.0 1. 導入 Gemfile gem 'devise' bundle installでhttps://rubygems.orgからGemをインストール 2. Deviseのインストール terminal $ rails g devise:install これを走らせることでdeviseのconfigファイルをインストールして導入していきます。 2.5 ルートビューの作成 ルートパスのビューを作成します。すでにルートパスのビューを作成している方は3. モデルのインストールに進んでください。 terminal $ rails g controllers Pages index routes.rbを以下のように書き換え config/routes.rb Rails.application.routes.draw do root to: 'pages#index' end 3. モデルのインストール terminal $ rails g devise {モデル名} これを走らせることでモデル、マイグレーションファイル、テストファイルのインストールができる。 4. マイグレーションファイルの編集 db/migrate/××××××××××××××_devise_create_◯◯.rb # frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| ## Database authenticatable t.string :name, null: false t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## 省略 end end end 上記で行ったモデルのインストールでマイグレーションファイルが作成されます。emailとpassword以外に必要なカラムがあれば追加。ここではnameカラムを追加しています。 すでに他のテーブルを作成していればrails db:migrate、していなければrails db:createをしてからrails db:migrateをターミナルで実行してください。 5. ストロングパラメータの設定 4. マイグレーションファイルの編集でマイグレーションファイルにカラムを追加した場合、以下の設定が必要になります app/controllers/application_controller.rb class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) end end keys: [:◯◯]は4. マイグレーションファイルの編集で設定したカラム名です。今回の場合、nameカラムを追加で記述したので、keys: [:name]となります。 6. ビューのインストール terminal $ rails g devise views 先にモデルをインストールしてからでないと後々設定がめんどくさくなるので、先にモデルをインストールすることをおすすめします。勉強したての頃、ここで詰まったことがあります。 7. 最後に これでDeviseを導入する一連の流れは終了です。 8. まとめ 一連の作業をターミナルの出力結果とともに載せておきます。ご参考までに。 terminal $ rails g devise:install Running via Spring preloader in process 78732 create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Depending on your application's configuration some manual setup may be required: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. * Required for all applications. * 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" * Not required for API-only Applications * 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> * Not required for API-only Applications * 4. You can copy Devise views (for customization) to your app by running: rails g devise:views * Not required * =============================================================================== $ rails g controller Pages index Running via Spring preloader in process 78991 create app/controllers/pages_controller.rb route get 'pages/index' invoke erb create app/views/pages create app/views/pages/index.html.erb invoke test_unit create test/controllers/pages_controller_test.rb invoke helper create app/helpers/pages_helper.rb invoke test_unit invoke assets invoke scss create app/assets/stylesheets/pages.scss $ rails g devise User Running via Spring preloader in process 78899 invoke active_record create db/migrate/20210610081935_devise_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml insert app/models/user.rb route devise_for :users $ rails g devise:views Running via Spring preloader in process 80035 invoke Devise::Generators::SharedViewsGenerator create app/views/devise/shared create app/views/devise/shared/_error_messages.html.erb create app/views/devise/shared/_links.html.erb invoke form_for create app/views/devise/confirmations create app/views/devise/confirmations/new.html.erb create app/views/devise/passwords create app/views/devise/passwords/edit.html.erb create app/views/devise/passwords/new.html.erb create app/views/devise/registrations create app/views/devise/registrations/edit.html.erb create app/views/devise/registrations/new.html.erb create app/views/devise/sessions create app/views/devise/sessions/new.html.erb create app/views/devise/unlocks create app/views/devise/unlocks/new.html.erb invoke erb create app/views/devise/mailer create app/views/devise/mailer/confirmation_instructions.html.erb create app/views/devise/mailer/email_changed.html.erb create app/views/devise/mailer/password_change.html.erb create app/views/devise/mailer/reset_password_instructions.html.erb create app/views/devise/mailer/unlock_instructions.html.erb $ rails db:create Created database '〇〇_development' Created database '〇〇_test' $ rails db:migrate == 20210610083101 DeviseCreateUsers: migrating ================================ -- create_table(:users) -> 0.0374s -- add_index(:users, :email, {:unique=>true}) -> 0.0408s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0357s == 20210610083101 DeviseCreateUsers: migrated (0.1141s) ======================= 参考記事
- 投稿日:2021-06-13T12:28:18+09:00
【Rails】Bootstrapを活用し、アイコンを押すと指定のリンクに飛ばす方法
はじめに 今回はBootstrapで使用できるアイコンを押すと、 指定のパス先へ飛ぶ方法について書かせていただきます。 BootstrapのアイコンURL:https://icons.getbootstrap.jp/ 下記イメージです! このようなヘッダーの人型のアイコンを押すと、ユーザー詳細ページに飛ぶような場合 ※今回はBootstrapの基本的な使い方等は割愛させていただきます。 開発環境 Bootstamp バージョン5 Ruby on Rails バージョン2.6.5 記述方法 通常Railsでパスを指定する際には下記のlink_toメソッドを使用します。 link_toメソッド <%= link_to '表示させる文字列', (指定のパス)_path, method: :メソッド, class: "" %> 最初、自分は”表示させる文字列”の部分にアイコンのHTMLを差し込めば表示させれるかなと思い、記述してみましたがうまくいきませんでした。 (式展開でHTMLを記述しましたが、コードがそのまま表示されてしまいました?) 解決策としてlink_toメソッドの記述の仕方を下記に変更することで問題が解決しました! link_toメソッド <%= link_to( 指定のぱす _path) do %> 表示させる文字 <% end %> 完成形 完成形 <%= link_to(user_path) do %> # アイコンの記述 <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30"fill="currentColor" class="bi bi-person-circle mx-3 text-dark" viewBox="0 0 16 16"> <path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/> <path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/> </svg> <% end %> この書き方で先程のフッダー画像の人型のアイコンを押すとユーザー詳細ページへ飛ぶように設定できました! CSSでテキストのデコレーションを消さないと、アイコンが青色のリンク色になってしまうので気をつけてください!
- 投稿日:2021-06-13T12:22:30+09:00
'/tmp/mysql.sock' (2)エラー解決方法
環境 Rails6.1.3 Ruby3.0.1 MySQL8.0.23 エラー内容 時々、 ActiveRecord::ConnectionNotEstablished (Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)): というソケットに接続できないエラーが出ることがあります。 原因 原因の一つとして考えられるのが、Mysqlサーバーが起動していないことです。 MySQLサーバーの状態を確認してみます。 % mysql.server status Permission denied ERROR! MySQL is not running, but PID file exists PIDファイルはあるが、サーバーが動いていないと表示されました。 PIDとはプロセスIDのことで、プロセスを管理するための識別子のことです。実際にファイルの中身をみてみると整数が割り当てられていると思います。 PIDファイルもない場合は、ファイルを作成することになります。 解決方法 サーバーを動かすために以下のコマンドを入力します。 % sudo mysql.server start Starting MySQL SUCCESS! これでもうまくいかない時は、 % sudo mysql.server restart も試してみてください。
- 投稿日:2021-06-13T11:20:11+09:00
Redditの日本語版サイト(ようなもの)作ってみた
はじめに redditという海外の掲示板サイトの日本語版のようなものを作ってみました。 概要 5chのような掲示板サイトです。登録をすると自分の立てたスレの通知や一覧が見れたりします。 工夫した点 登録なしでもありでも使えるようにすること。
- 投稿日:2021-06-13T11:03:29+09:00
Rails 消費税計算
プログラミングの学習を始めて3ヶ月、 Ruby on Railsでの、消費税計算についてまとめます。 前提として、 商品(Product)とカート(Cart)の作成が終えていて、 Productにはsales_priseのカラムがあります。 モデルに記載 Productモデルに記載します。 Product.rb def add_tax_sales_price (self.sales_price * 1.10).round end Productのカラムである sales_price の前に 必ず add_tax_ を記載してください。 私はそれで rails s した時に永遠にループしてしまい serverが起動せず大変な目にあいました。 viewに記載 消費税を計算して反映させたい部分に <%= @product.add_tax_sales_price.to_s(:dalimited) %> Productモデルに * 1.10 (2021年6月現在)の税率のため、 変更があった場合はmodelを変更するだけでviewページは反映されます。 (:dalimited)は数値を3桁区切りにしてくれます。 例: 12,345,678 1,234円
- 投稿日:2021-06-13T09:26:05+09:00
ActionTextの投稿機能をRspec(system spec)でテストする
ActionTextでテキストエディタを実装しましたが、 なかなかそのテストの仕方までは探しても見つからなかったので備忘録です。 参考: * 【Rails6】Action TextでRSpecのエラーが出たときにすること * ドキュメント * Everyday Rails 前提 Local(地元民)がかくArticle(記事)の作成機能をActionTextを使って作成。 articlesテーブルにはtitle属性があり、 ActionTextを使ってcontent(本文)(画像付きもOK)を作成できるようにしている。 ちなみに今回の内容とは関係ないですが、 記事にはタグ(Tagモデルで管理)をつけられるようにしてます. Left align バージョン OS macOS Big Sur 11.3 Ruby 3.0.0 Rails 6.1.3.1 Rspec 3.10 各関連ファイル モデル app/models/article.rb class Article < ApplicationRecord has_rich_text :content belongs_to :local has_one :tag, dependent: :destroy accepts_nested_attributes_for :tag, allow_destroy: true default_scope -> { order(created_at: :desc)} validates :title, presence: true, length: { maximum: 255 } validate :content_required private def content_required errors.add(:content, "を入力してください") unless content.body.present? end end コントローラー app/controllers/articles_controller.rb class ArticlesController < ApplicationController skip_before_action :authenticate_user!, if: :local_signed_in? skip_before_action :authenticate_local!, if: :user_signed_in?, only: [:index, :show] before_action :correct_local, only: [:edit, :update, :destroy] def index @articles = Article.all end def show @article = Article.find_by(id: params[:id]) end def new @article = Article.new @article.build_tag tags end def create @article = current_local.articles.build(article_params) if @article.save flash[:success] = "投稿が完了しました" redirect_to @article else render 'new' end end def edit 未 end def update 未 end def destroy 未 end private def article_params params.require(:article).permit(:title, :content, tag_attributes:[:hoge ...略]) end def correct_local @article = Article.find_by(id: [params[:id]]) @local = Local.find_by(id: @article.local_id) redirect_to(top_url) unless @local == current_local end def tags @tags = { hoge: "hoge" ...略 } end end ビュー app/views/articles/new.html.erb <h1>Articles#new</h1> <p>Find me in app/views/articles/new.html.erb</p> <%= form_with(model: @article, local: true) do |form| %> <div class="field"> <%= form.label :title, 'タイトル' %> <%= form.text_field :title %> </div> <div class="field"> <%= form.label :content, '本文' %> <%= form.rich_text_area :content %> </div> <p>タグ付け</p> <div style="display: flex; flex-wrap: wrap;"> <%= form.fields_for :tag do |tag_form| %> <% @tags.each do |key, jp| %> <div class="field" style="padding: 5px;"> <%= tag_form.label key, jp %> <%= tag_form.check_box key, {}, true, false %> </div> <% end %> <% end %> </div> <div class="actions"> <%= form.submit "投稿" %> </div> <% end %> app/views/articles/shoe.erb <h1><%= @article.title %></h1> <section> <%= @article.content %> </section> 本題(テスト部分) ハマったことその① 本文(content)のテストが通らない spec/system/articles_spec.rb RSpec.describe 'Profiles', js: true, type: :system do let(:local) { FactoryBot.create(:local) } let(:article) { FactoryBot.build(:article) } before do local.confirm @number_of_articles = Article.count @number_of_tags = Tag.count end describe 'creating new article' do it 'success to create an article' do sign_in local visit new_article_path fill_in 'タイトル', with: article.title fill_in '本文', with: article.content #←←← ここ 以下略 Failure/Error: fill_in '本文', with: article.content Capybara::ElementNotFound: Unable to find field '本文' that is not disabled しばらく本文の2文字を書き間違えてないか見返したりしてたのですがそんなこともなく。。 正解は下記記事にありました!! ありがとうございました。 * 【Rails6】Action TextでRSpecのエラーが出たときにすること 解決策 ざっくり概要をまとめると、 原因としては本文の部分はActionTextを使って入力フォームを作っていて、 そこの箇所はfill_inの対象であるテキストエリアではないため、 カピバラさんが見つけられなかったよ、と仰っている。 そこで、action_text_helper.rbを作成し、 その中でリッチテキストエディターを見つけてそこに書き込んでねー、というfill_in_rich_text_areaメソッドを作成。 ※詳細は上記参考記事をご確認くださいませ。 すると以下で通るようになります。 spec/system/articles_spec.rb RSpec.describe 'Profiles', js: true, type: :system do let(:local) { FactoryBot.create(:local) } let(:article) { FactoryBot.build(:article) } before do local.confirm @number_of_articles = Article.count @number_of_tags = Tag.count end describe 'creating new article' do it 'success to create an article' do sign_in local visit new_article_path fill_in 'タイトル', with: article.title fill_in_rich_text_area '本文', with: article.content #←←← ここ ハマったことその② 画像添付のテストの書き方がわからない・・・ 低レベルだなと思った方はスルーしてください。。 恥ずかしながらファイルのアップロードのテストについて、こう書く↓知識しかなく・・ attach_file "Attachment", "#{Rails.root}/spec/files/attachment.jpg" ActionTextを使うとファイルアップロードのボタンはクリップマーク。。。 さてどうしよう。 解決策 で、ドキュメントを確認したところ # will attach file to whatever file input is triggered by the block page.attach_file('/path/to/file.png') do page.find('#upload_button').click end これで行けそう。 ということで、デベロッパーツールで使えそうなCSSを探し・・ spec/system/articles_spec.rb RSpec.describe 'Profiles', js: true, type: :system do let(:local) { FactoryBot.create(:local) } let(:article) { FactoryBot.build(:article) } before do local.confirm @number_of_articles = Article.count @number_of_tags = Tag.count end describe 'creating new article' do it 'success to create an article' do sign_in local visit new_article_path fill_in 'タイトル', with: article.title fill_in_rich_text_area '本文', with: article.content page.attach_file("#{Rails.root}/spec/files/attachment.jpeg") do #←←←ここから page.find('.trix-button--icon-attach').click end #←←←ここまで check 'tag' check 'another_tag' click_button '投稿' expect(Article.count).to eq @number_of_articles +1 expect(Tag.count).to eq @number_of_tags +1 expect(page).to have_current_path article_path(Article.last) expect(page).to have_content article.title expect(page).to have_content 'this is my article!' expect(page).to have_content "attachment.jpeg" end end end これで無事通りました! まとめ とにかく困ったらドキュメント読め、と思いました。 しかしもっと良き書き方がありましたらご教示いただけますと幸いです!
- 投稿日:2021-06-13T08:50:05+09:00
date_select でつくるセレクトボックスを「年」「月」「日」で区切る
date型のデータについてdate_selectメソッドを使うと簡単にセレクトボックスをつくる デフォルトでは下記のように年月日の区切りは空白です。 <div class="birth_date_field"> <%= f.label :birth_date %> <%= f.date_select( :birth_date, start_year: 1930, end_year: (Time.now.year - 18), default: Date.new(1989, 1, 1)) %> </div> もう少し分かりやすくしたいなと思い、区切りにスラッシュを 年月日の区切りにスラッシュを入れる <div class="birth_date_field"> <%= f.label :birth_date %> <%= f.date_select( :birth_date, use_month_numbers: true, start_year: 1930, end_year: (Time.now.year - 18), default: Date.new(1989, 1, 1), date_separator: '/') %> </div> 年月日を入れる 年月日の区切りに「年」「月」「日」を入れる <div class="birth_date_field"> <%= f.label :birth_date %> <%= raw sprintf( f.date_select( :birth_date, use_month_numbers: true, start_year: 1930, end_year: (Time.now.year - 18), default: Date.new(1989, 1, 1), date_separator: '%s'), '年 ', '月 ') + '日' %> </div> オプション 概要 書き方例 with_css_class 年月日それぞれでCSSを当てたい場合に利用。実際のCSSはyear,month,dayで指定が可能になる with_css_classes:'任意のクラス名' order 年月日の順番を変えたい場合に利用。表示させない場合でも必ず年月日全部書く必要あり order:[:month,:year,:day] use_month_numbers 月の表示を数字にしたい場合に利用 use_month_numbers:true discard_year 年の表示を消したいときに利用 discard_year:true discard_month 月の表示を消したいときに利用 discard_month:true discard_day 日の表示を消したいときに利用 discard_day:true disabled プルダウンを選択不可にする disabled:true prompt 指定データがない場合の初期値表示(一番上の表示)に利用 prompt:"--" selected 指定データがある場合の指定に利用 selected:Time.now(Date型変数ならOK) start_year プルダウンの一番上に来る年数を指定 start_year:Time.now.year start_year:2019 end_year プルダウンの一番下に来る年数を指定 end_year:Time.now.year-10 end_year:1900 date_separator 年月日のフォームを分けた場合の中間に表示する値を指定。 date_separator:'%s' date_separator:'/' @ozackieeささん https://qiita.com/ozackiee/items/3c027d07cdeb61df6029 Hatena Blog https://blog.inouetakuya.info/entry/2014/01/31/233000
- 投稿日:2021-06-13T02:59:28+09:00
②マイグレーションファイルのすすめ 外部キー
初めに 今回は外部キーとリファレンスについてまとめます。 こちらは②マイグレーションファイルのすすめ②ですので、よければ①もご覧ください。 マイグレーションファイルのすすめ 以下の記事を参考にさせていただきました! 外部キーの概要と制約を使うことのメリット・デメリット 外部キー 概要 create_table(:users) do |t| t.string :name, null: false end create_table(:user_tweet) do |t| t.references :user, null: false, foreign_key: true end 外部キーとは テーブル同士を紐付ける際に用意されるカラムのこと。 例えば以下の二つのテーブルは、1ユーザーが複数のツイートを持っており、ツイートテーブルのuser_idがuser.idの値を格納することで二つのテーブルを紐付けている。 この時ユーザーテーブルのidカラムは主キー、ツイートテーブルのuser_idが外部キーである。 ユーザーテーブル id user 1 KEN 2 MIKE ツイートテーブル id user_id content 1 1 Hello 2 1 How are you? 3 2 こんにちは 外部キー制約とは 主キーと外部キーを使った制約で利用した場合、以下の制限がある。 1. 存在しない値を外部キーとして登録することはできない 2. 子テーブルの外部キーに値が登録されている親テーブルのレコードは削除できない 1. 存在しない値を外部キーとして登録することはできない ツイートテーブルにデータを登録する際に、user_idがuserテーブルのidカラムに存在しない値(例えば nullや3など)を指定するとエラーが生じる。 2. 子テーブルの外部キーに値が登録されている親テーブルのレコードは削除できない ユーザーKENを削除する際に、user_idの値が1のツイートが存在する場合は、ユーザーKENを削除することができない。 ※ KENを削除する場合は、Userモデルでdependent: :destroy を使用する必要がある。 外部キーのメリットとデメリット 存在しない値が、登録されることを防ぐことがきる為、データの整合性が保たれる。 親テーブルの存在しない子テーブルが発生することを防げる。 外部キーの作成方法 外部キーの作成には以下の2パターンがある。 create_table(:users) do |t| t.string :name, null: false end create_table(:tweet) do |t| t.references :user, null: false, foreign_key: true end OR create_table(:users) do |t| t.string :name, null: false end create_table(:tweet) do |t| t.references :user end # add_foreign_keyは後から外部キー制約を貼りたい場合にも使える add_foreign_key(:tweet, :users) t.reference :userによりuser_idカラムを作成している。 *foreign_key: trueがなければ外部キー制約がつかないので注意! まとめ 外部キーは、テーブル設計に欠かせない知識なのでぜひ覚えましょう! 人のことは言えないので僕も覚えます!笑
- 投稿日:2021-06-13T01:57:20+09:00
newとbuildの違い
newとbuildってどう使い分けるの? buildは、buildはアソシエーションを関連づけさせているときに使用し 外部参照キーを自動でセットしてくれる 親要素への結びつけが必要な場合はそこを自動でやってくれる モデルを関連付けしたときにbuildを使う buildはnewのエイリアス(ショートカットキー) buildは新しい書き方で buildは親要素から子要素のデータをセットしてくれる便利なことをするやつ newは子要素を自分で記述する必要がある <例> ※@post = @user.posts.buildは、@post = Post.new(user_id: @user.id)と同じ。 ※user.rbに「has_many :posts」があると、「Userインスタンス.posts.build」というメソッドが使える」ようになる。 これは「Userインスタンスに紐付いたPostインスタンスを生成する」メソッド。
- 投稿日:2021-06-13T01:55:25+09:00
アソシエーション部分で働いている色々なモデル内まとめ
belong_to この関連付けをされているクラスには外部キーがあること前提 外部キーが別クラスにある場合はhas_oneを使用する必要がある :optional belong_toで使えるオプションの一つ 公式 :optionalオプションをtrueに設定すると、 関連付けされたオブジェクトの存在性のバリデーションが実行されないようになる。 ☆値が空で来たときはエラーを返さずに、その空情報をなかったかのように振る舞わせることができる ☆optional: trueはpresence:trueと逆のことをしている ☆外部キーのnilを許容してくれる ☆アソシエーション組んである状態のため親要素がないとエラー!って出てしまうモデルのチェックを回避してくれる 今回は親の情報が入った時点で子要素側に親_idの情報が渡すことができるようになる そのため親が存在していないと子は保存できない状態にあるということ いつ親の要素が入るのか 親と子の要素が一緒に登録されるからことここの記述が必要になるという特殊な状態 一旦子要素が親要素がない状態でも作られるようにしていて 親要素ができたタイミングで子要素を紐付けをさせるようにしている reject_if: :all_blank accepts_nested_attributes_forをすると空の文字列が入っているデータを弾いてくれる allow_destroy: true モデル経由から別モデルを削除する時に必要なオプション 親要素側から削除する必要がある ※今回はネストさせて使用するデータベース = controllerがない = destroyアクションがないという状態 コントローラがない特殊なモデルaccepts_nested_attributes_forの関係の時どう削除するか 自分に紐づいているコントローラを持たないモデルのデータを消したいときに使う dependent: :destroy 親要素がいなくなってしまったときに子要素が浮いた状態にならないように削除される belongs_toとhas_oneのどちらを選ぶか 2つのモデルの間に1対1の関係を作りたいのであれば、いずれか一方のモデルにbelongs_toを追加し、もう一方のモデルにhas_oneを追加する必要があります。 外部キー(foreign key)をどちらに置くかできまる
- 投稿日:2021-06-13T01:30:43+09:00
草野球の出欠確認Webアプリを作ろう! part.12
これから作っていく簡単なWebアプリの作成メモ(自分の備忘)です。 自分用なのであまり凝りすぎないように書いていきたい。 <<前回の記事 今回やったこと 前回記事でbootstrapを導入した際に、既存のCSSが適用されなくなってしまい、一時気力を失ってしまった。 しかし、これまで作ったものをbootstrapのスタイルに置換することで、見た目はこれまでと違うが以降の作業を継続できるようにした。 (未考慮の部分は時間ややる気があるときに整えることにする、後回しともいう) 以下、今回主にクラスなどの部分を編集したスケジュール機能とユーザー機能の画面を示す。 スケジュール機能 一覧画面 app/views/schedules/index.html.erb <h1>チームの予定一覧</h1> <div class="mx-3 d-block"> <%= link_to '新規作成', new_schedule_path, class: 'btn btn-primary' %> </div> <% if @schedules.blank? %> <div class="container mx-auto"><%= "表示できる予定がありません。" %></div> <% else %> <div class="container mx-auto table-responsive"> <table id="schedules_table" class="table table-striped table-hover table-sm"> <thead> <tr> <th scope="col">件名</th> <th scope="col">予定日</th> <th scope="col">予定の時間</th> </tr> </thead> <tbody> <% @schedules.each do |lst| %> <tr class="clickable-row" data-href="<%= schedule_path lst.id %>"> <td><%= lst.title %></td> <td><%= lst.date_of %></td> <td><%= lst.start_time.strftime('%H:%M') %> ~ <%= lst.end_time.strftime('%H:%M') %></td> </tr> <% end %> </tbody> </table> </div> <% end %> 新規画面 app/views/schedules/new.html.erb <h1>予定の新規作成</h1> <div class="container mx-auto"> <div class="mb-3 d-block"> <%= link_to '予定一覧へ', schedules_path, class: 'btn btn-primary' %> </div> <%= form_for @schedules, url: {action: "create"} do |f| %> <div class="form-group mb-3"> <label class="form-label">件名:</label> <%= f.text_field :title, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">予定の日付:</label> <%= raw sprintf( f.date_select(:date_of, use_month_numbers: true, order: [:year,:month,:day], selected: Time.zone.now, start_year: Time.zone.now.year + 5, end_year: Time.zone.now.year, date_separator: '%s' ),'年','月') + '日' %> </div> <div class="form-group mb-3"> <label class="form-label">開始時間:</label> <%= f.time_select :start_time, class: 'form-control' %> ~ <label class="form-label">終了時間:</label> <%= f.time_select :end_time , class: 'form-control'%> ( <label class="form-label">集合時間:</label> <%= f.time_select :meeting_time, class: 'form-control' %> ) </div> <div class="d-block"> <%= f.submit "新規作成する", class: 'btn btn-success' %> </div> <% end %> </div> 編集画面 app/views/schedules/edit.html.erb <h1>予定の編集</h1> <div class="container mx-auto"> <div class="mb-3 d-block"> <%= link_to '予定一覧へ', schedules_path, class: 'btn btn-primary' %> </div> <%= form_for @schedules, url: {action: "update", id: @schedules.id} do |f| %> <div class="form-group mb-3"> <label class="form-label">件名:</label> <%= f.text_field :title, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">予定の日付:</label> <%= raw sprintf( f.date_select(:date_of, use_month_numbers: true, order: [:year,:month,:day], selected: Time.zone.now, start_year: Time.zone.now.year + 5, end_year: Time.zone.now.year, date_separator: '%s' ),'年','月') + '日' %> </div> <div class="form-group mb-3"> <label class="form-label">開始時間:</label> <%= f.time_select :start_time, class: 'form-control' %> ~ <label class="form-label">終了時間:</label> <%= f.time_select :end_time, class: 'form-control' %> ( <label class="form-label">集合時間:</label> <%= f.time_select :meeting_time, class: 'form-control' %> ) </div> <div class="d-block"> <%= f.submit "保存する", class: 'btn btn-success' %> </div> <% end %> </div> 詳細画面 app/views/schedules/show.html.erb <h1>予定の詳細</h1> <div class="container mx-auto"> <div class="mb-3 d-block"> <%= link_to '予定一覧へ', schedules_path, class: 'btn btn-primary' %> </div> <div class="form-group mb-3"> <label class="form-label">件名:</label> <%= @schedules.title %> </div> <div class="form-group mb-3"> <label class="form-label">予定の日付:</label> <%= @schedules.date_of.strftime("%Y年%m月%d日") %> </div> <div class="form-group mb-3"> <label class="form-label">開始時間 ~ 終了時間: </label> <%= @schedules.start_time.strftime("%H:%M") %> ~ <%= @schedules.end_time.strftime("%H:%M") %> ( <label class="form-label">集合時間:</label> <%= @schedules.meeting_time.strftime("%H:%M") %> ) </div> <div class="d-block"> <%= link_to '編集', edit_schedule_path(@schedules.id), class: 'btn btn-success' %> <%= link_to '削除', schedule_path(@schedules.id), method: :delete, class: 'btn btn-danger' %> </div> </div> ユーザー機能 一覧画面 app/views/users/index.html.erb <h1>メンバーの一覧</h1> <div class="mx-3 d-block"> <%= link_to '新規作成', new_user_path, class: 'btn btn-primary' %> </div> <% if @users.blank? %> <div><%= "表示できるメンバーがいません。" %></div> <% else %> <div class="container mx-auto table-responsive"> <table class="table table-striped table-hover table-sm"> <thead> <tr> <th scope="col">ユーザー名</th> <th scope="col">メールアドレス</th> </tr> </thead> <tbody> <% @users.each do |lst| %> <tr class="clickable-row" data-href="<%= user_path lst.id %>"> <td><%= lst.name %></td> <td><%= lst.email %></td> </tr> <% end %> </tbody> </table> </div> <% end %> 新規画面 app/views/users/new.html.erb <h1>ユーザーの新規作成</h1> <div class="container mx-auto"> <div class="mb-3 d-block"> <%= link_to 'ユーザー一覧へ', users_path, class: 'btn btn-primary' %> </div> <%= form_for @users, url: {action: "create"} do |f| %> <div class="form-group mb-3"> <label class="form-label">ユーザー名:</label> <%= f.text_field :name, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">メールアドレス:</label> <%= f.email_field :email, size: "40", class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">パスワード:</label> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">パスワード(確認欄):</label> <%= f.password_field :password_confirmation, class: 'form-control' %> </div> <div class="d-block"> <%= f.submit "新規作成する", class: 'btn btn-success' %> </div> <% end %> </div> 編集画面 app/views/users/edit.html.erb <h1>ユーザーの編集</h1> <div class="container mx-auto"> <div class="mb-3 d-block"> <%= link_to 'ユーザー一覧へ', users_path, class: 'btn btn-primary' %> </div> <%= form_for @users, url: {action: "update", id: @users.id} do |f| %> <div class="form-group mb-3"> <label class="form-label">ユーザー名:</label> <%= f.text_field :name, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">メールアドレス:</label> <%= f.email_field :email, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">パスワード:</label> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group mb-3"> <label class="form-label">パスワード(確認欄):</label> <%= f.password_field :password_confirmation, class: 'form-control' %> </div> <div class="d-block"> <%= f.submit "保存する", class: 'btn btn-success' %> </div> <% end %> </div> 詳細画面 app/views/users/show.html.erb <h1>ユーザーの詳細</h1> <div class="container mx-auto"> <div class="mb-3 d-block"> <%= link_to 'ユーザー一覧へ', users_path, class: 'btn btn-primary' %> </div> <div class="form-group mb-3"> <label class="form-label">ユーザー名:</label> <%= @users.name %> </div> <div class="form-group mb-3"> <label class="form-label">メールアドレス:</label> <%= @users.email %> </div> <div class="form-group mb-3"> <label class="form-label">パスワード:</label> <%= "(セキュリティに配慮し、パスワードは非表示です)" %> </div> <div class="d-block"> <%= link_to '編集', edit_user_path(@users.id), class: 'btn btn-success' %> <%= link_to '削除', user_path(@users.id), method: :delete, class: 'btn btn-danger' %> </div> </div> ちまちましか進められなくて進みが遅いので、できればどこかで時間を作って作業したいと思う。 内容はゼロだけど今回はここまで。