- 投稿日:2020-05-22T22:20:12+09:00
【Rails】ウィザード形式での実装
最初に
ユーザー新規登録の際にdevise認証機能を使用して、ユーザーの情報(ユーザーモデル)と、ユーザーの連絡先の情報(addressモデル)を登録できるよにすることが目的であり、1ページでまとめて(fileds_forを使用しない)ではなくウィザード形式を用いて2ページに分割しての実装が難しかったため、備忘録として残します。
ウィザード形式とは
画面が切り替わりながら操作が進んでいくことで、具体的にはこんな感じ。
https://gyazo.com/c0b7e9d98fb66ad7da724a95a77e4438
ユーザー情報(メールアドレスなど)の登録が完了した際に次のページでユーザー情報に付随する住所などの情報をページを切り替えて表示して登録させている。これをウィザード形式と呼ぶ。らしい。実装の流れ
ユーザー情報を入力させ、その情報を一旦sessionに保持させる。
次に住所の情報を入力させ、入力が完了したらsessionに保持したユーザー情報と住所の情報を各テーブルに保存させる。
ページが切り替わる際にバリデーションをかけられるため、各ページごとにテーブルを分ける必要があります。
例えばユーザー情報登録画面にaddressモデルの住所を登録する項目(カラム)があった場合、userモデルではpermitされていないため、登録が進まないことになってしまう。1ページに1モデル対応させるようにする。手順
1.deviseのコントローラーをカスタムできるようにする。
deviseのデフォルトの状態だとコントローラを作成していなくてもdevise/registrationsコントローラーが起動して、view/regstrations/new.html.hamlにルーティングが敷かれている。(ルーティングはdevise_for: users)
これだとカスタムができないため、対応するコントローラーを作成する。$ rails g devise:controllers usersこれでusers/registrations_contoroller.rbが作成される。
https://gyazo.com/a8052ca6b6cc4bd1e19ff4e2465cff1b2.deviseのルーティングを変更
deviseのルーティングを指定する。
この記述でdeviseのルーティングの指定が完了。
devise/registrationsコントローラー → users/registrationsコントローラー
にルーティングを変更することができた。
3.app/controllers/application_controller.rbの修正
deviseのpermitはapplication_controller.rbで行う。application_controller.rbbefore_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :family_name, :first_name, :family_name_kana, :first_name_kana, :birthday]) endbefore_action :configure_permitted_parameters, if: :devise_controller?この記述でdevise_controllerが動いた際に、後に記述したconfigure_permitted_parametersメソッドが始動する様にしている。
def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :family_name, :first_name, :family_name_kana, :first_name_kana, :birthday]) endこの記述により、Strong Parametersで追加したいカラムを追加。
デフォルトで:email、:password は許可されているので記載しなくてOK。先ほど作ったコントローラー。
クラス名意外消してOK。
あとはいつも通り、コントローラーに記載をしていく。users/registrations_controller.rbdef new @user = User.new end空のハッシュを生成して、
users/registrations_controller.rbdef new @user = User.new end def create @user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password] @address = @user.addresses.build render :new_address endcreateアクションで実際にsessionsに一度保持して、address情報を登録するページに飛ぶように記載している。
最初これ何やってるか意味がわからなかったので3つに分けます。・1ページ目(ユーザー情報)で入力した情報のバリデーションのチェック
・1ページ目で入力した情報をsessionに保持させること
・次ページの住所情報登録で使用するインスタンスを生成、次ページへ遷移することusers/registrations_controller.rb@user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return endUserモデルのインスタンスを生成し、ユーザー情報登録画面から送られてきたパラメータをインスタンス変数@userに代入し、引数としてapplication_controller.rbで修正したsign_up_paramsを引数とする。
configure_permitted_parametersメソッドなのにsign_up_paramsで引っ張ってこれている。これは謎です。
もしかしたらdeviseが勝手に動いてくれているのかも。
インスタンス変数@userに対してvalid?メソッドを適用することで送られてきたパラメータが指定されたバリデーションに引っかかっているかどうかをチェック。falseになった場合は、エラーメッセージとともにnewアクションへrenderさせるような記述となっています。users/registrations_controller.rb@user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password]end以下の2文でユーザー情報登録画面での情報をsessionに保持させています。
ウィザード形式上、次ページの入力が完了した段階で各モデルのデータをDBに反映させたいです。なのでここでsessionに保持させる必要があります。この2行ずば抜けて意味不明だったので細かく書きます。。(笑)
session["devise.regist_data"]に値を代入します。この時、sessionにハッシュの形で情報を保持させるために、attributesメソッドを用いてデータを整形しています。
ここで注意したいのがパスワードはattributesメソッドでは追加できないということ。
paramsから取得して別で追加してあげます。パスワードを再度sessionに代入する必要があり、session["devise.regist_data"][:user]["password"]に再代入している。らしい。全然わからん。(泣)users/registrations_controller.rbdef new @user = User.new end def create @user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password] @address = @user.addresses.build render :new_address end最後の2行で、次ページでユーザー情報に紐ずく住所情報を入力させるため、addresses.buildで生成したインスタンス@userに紐づくAddressモデルのインスタンスを生成して@addressに代入。
住所情報を登録させるページを表示するためnew_addressアクションのビューへrenderしています。5.new_addressへのルーティング指定と対応するビューを移動。
config/routes.rbRails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations' } devise_scope :user do get 'addresses', to: 'users/registrations#new_address' post 'addresses', to: 'users/registrations#create_address' end enddevise_scope :user do~endでルーティングを指定。これで#new_addressと#create_addressが使用できるようになる。
対応するnew_address.html.hamlファイルは、
app/views/devise/registrations/下に移動。これでusers/registrationsのアクションでビューが表示される。6.create_addressアクションでユーザー情報とプロフィール情報をテーブルに保存。
registrations_controller.rbdef create_address @user = User.new(session["devise.regist_data"]["user"]) @address = Address.new(address_params) unless @address.valid? flash.now[:alert] = @address.errors.full_messages render :new_address and return end @user.addresses.build(@address.attributes) @user.save session["devise.regist_data"]["user"].clear sign_in(:user, @user) redirect_to root_path end private def address_params params.require(:address).permit(:post_code, :prefectures, :city, :block, :building, :phone_number) endさきほどsessionにuserオブジェクトのデータを保存したので、create_addressアクションでもuserオブジェクトを生成して@userに代入。2行目でprivateメソッド下の引数で address_paramsメソッドを引数としてAddressモデルを生成、@addressに代入。
あとは先ほどと同じで、addressオブジェクトのバリデーションを確認して、通ったら保存。
addressesテーブルに外部キーを設定していると、バリデーションに引っかかるので、モデルでoptional: trueを追加。address.rbclass Address < ApplicationRecord belongs_to :user, optional: true endoptional: trueは外部キーにnullが入ることを許可してくれるオプション。これでバリデーションに引っ掛からなくなる。
その後はuserモデルに紐付いたaddressモデルという記述を追加。
@userをsaveして、sessionをclearメソッドを使用して削除。
まだ登録できただけなので、sign_inメソッドでサインイン。あとはcreate_address.html.hamlが動くかを確認して、正常に動けばウィザード形式の実装完了です!
参考文献
・https://satoryu.hatenablog.com/entry/antipattern_in_designing_models_for_wizard
・https://note.com/vixer93/n/nac92cc4c0983
- 投稿日:2020-05-22T22:20:12+09:00
【Rails】ウィザード形式でのユーザー新規登録機能実装
最初に
ユーザー新規登録の際にdevise認証機能を使用して、ユーザーの情報(ユーザーモデル)と、ユーザーの連絡先の情報(addressモデル)を登録できるよにすることが目的であり、1ページでまとめて(fileds_forを使用しない)ではなくウィザード形式を用いて2ページに分割しての実装が難しかったため、備忘録として残します。
ウィザード形式とは
画面が切り替わりながら操作が進んでいくことで、具体的にはこんな感じ。
https://gyazo.com/c0b7e9d98fb66ad7da724a95a77e4438
ユーザー情報(メールアドレスなど)の登録が完了した際に次のページでユーザー情報に付随する住所などの情報をページを切り替えて表示して登録させている。これをウィザード形式と呼ぶ。らしい。実装の流れ
ユーザー情報を入力させ、その情報を一旦sessionに保持させる。
次に住所の情報を入力させ、入力が完了したらsessionに保持したユーザー情報と住所の情報を各テーブルに保存させる。
ページが切り替わる際にバリデーションをかけられるため、各ページごとにテーブルを分ける必要があります。
例えばユーザー情報登録画面にaddressモデルの住所を登録する項目(カラム)があった場合、userモデルではpermitされていないため、登録が進まないことになってしまう。1ページに1モデル対応させるようにする。手順
1.deviseのコントローラーをカスタムできるようにする。
deviseのデフォルトの状態だとコントローラを作成していなくてもdevise/registrationsコントローラーが起動して、view/regstrations/new.html.hamlにルーティングが敷かれている。(ルーティングはdevise_for: users)
これだとカスタムができないため、対応するコントローラーを作成する。$ rails g devise:controllers usersこれでusers/registrations_contoroller.rbが作成される。
https://gyazo.com/a8052ca6b6cc4bd1e19ff4e2465cff1b2.deviseのルーティングを変更
deviseのルーティングを指定する。
この記述でdeviseのルーティングの指定が完了。
devise/registrationsコントローラー → users/registrationsコントローラー
にルーティングを変更することができた。
3.app/controllers/application_controller.rbの修正
deviseのpermitはapplication_controller.rbで行う。application_controller.rbbefore_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :family_name, :first_name, :family_name_kana, :first_name_kana, :birthday]) endbefore_action :configure_permitted_parameters, if: :devise_controller?この記述でdevise_controllerが動いた際に、後に記述したconfigure_permitted_parametersメソッドが始動する様にしている。
def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :family_name, :first_name, :family_name_kana, :first_name_kana, :birthday]) endこの記述により、Strong Parametersで追加したいカラムを追加。
デフォルトで:email、:password は許可されているので記載しなくてOK。先ほど作ったコントローラー。
クラス名意外消してOK。
あとはいつも通り、コントローラーに記載をしていく。users/registrations_controller.rbdef new @user = User.new end空のハッシュを生成して、
users/registrations_controller.rbdef new @user = User.new end def create @user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password] @address = @user.addresses.build render :new_address endcreateアクションで実際にsessionsに一度保持して、address情報を登録するページに飛ぶように記載している。
最初これ何やってるか意味がわからなかったので3つに分けます。・1ページ目(ユーザー情報)で入力した情報のバリデーションのチェック
・1ページ目で入力した情報をsessionに保持させること
・次ページの住所情報登録で使用するインスタンスを生成、次ページへ遷移することusers/registrations_controller.rb@user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return endUserモデルのインスタンスを生成し、ユーザー情報登録画面から送られてきたパラメータをインスタンス変数@userに代入し、引数としてapplication_controller.rbで修正したsign_up_paramsを引数とする。
configure_permitted_parametersメソッドなのにsign_up_paramsで引っ張ってこれている。これは謎です。
もしかしたらdeviseが勝手に動いてくれているのかも。
インスタンス変数@userに対してvalid?メソッドを適用することで送られてきたパラメータが指定されたバリデーションに引っかかっているかどうかをチェック。falseになった場合は、エラーメッセージとともにnewアクションへrenderさせるような記述となっています。users/registrations_controller.rb@user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password]end以下の2文でユーザー情報登録画面での情報をsessionに保持させています。
ウィザード形式上、次ページの入力が完了した段階で各モデルのデータをDBに反映させたいです。なのでここでsessionに保持させる必要があります。この2行ずば抜けて意味不明だったので細かく書きます。。(笑)
session["devise.regist_data"]に値を代入します。この時、sessionにハッシュの形で情報を保持させるために、attributesメソッドを用いてデータを整形しています。
ここで注意したいのがパスワードはattributesメソッドでは追加できないということ。
paramsから取得して別で追加してあげます。パスワードを再度sessionに代入する必要があり、session["devise.regist_data"][:user]["password"]に再代入している。らしい。全然わからん。(泣)users/registrations_controller.rbdef new @user = User.new end def create @user = User.new(sign_up_params) unless @user.valid? flash.now[:alert] = @user.errors.full_messages render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password] @address = @user.addresses.build render :new_address end最後の2行で、次ページでユーザー情報に紐ずく住所情報を入力させるため、addresses.buildで生成したインスタンス@userに紐づくAddressモデルのインスタンスを生成して@addressに代入。
住所情報を登録させるページを表示するためnew_addressアクションのビューへrenderしています。5.new_addressへのルーティング指定と対応するビューを移動。
config/routes.rbRails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations' } devise_scope :user do get 'addresses', to: 'users/registrations#new_address' post 'addresses', to: 'users/registrations#create_address' end enddevise_scope :user do~endでルーティングを指定。これで#new_addressと#create_addressが使用できるようになる。
対応するnew_address.html.hamlファイルは、
app/views/devise/registrations/下に移動。これでusers/registrationsのアクションでビューが表示される。6.create_addressアクションでユーザー情報とプロフィール情報をテーブルに保存。
registrations_controller.rbdef create_address @user = User.new(session["devise.regist_data"]["user"]) @address = Address.new(address_params) unless @address.valid? flash.now[:alert] = @address.errors.full_messages render :new_address and return end @user.addresses.build(@address.attributes) @user.save session["devise.regist_data"]["user"].clear sign_in(:user, @user) redirect_to root_path end private def address_params params.require(:address).permit(:post_code, :prefectures, :city, :block, :building, :phone_number) endさきほどsessionにuserオブジェクトのデータを保存したので、create_addressアクションでもuserオブジェクトを生成して@userに代入。2行目でprivateメソッド下の引数で address_paramsメソッドを引数としてAddressモデルを生成、@addressに代入。
あとは先ほどと同じで、addressオブジェクトのバリデーションを確認して、通ったら保存。
addressesテーブルに外部キーを設定していると、バリデーションに引っかかるので、モデルでoptional: trueを追加。address.rbclass Address < ApplicationRecord belongs_to :user, optional: true endoptional: trueは外部キーにnullが入ることを許可してくれるオプション。これでバリデーションに引っ掛からなくなる。
その後はuserモデルに紐付いたaddressモデルという記述を追加。
@userをsaveして、sessionをclearメソッドを使用して削除。
まだ登録できただけなので、sign_inメソッドでサインイン。あとはcreate_address.html.hamlが動くかを確認して、正常に動けばウィザード形式の実装完了です!
参考文献
・https://satoryu.hatenablog.com/entry/antipattern_in_designing_models_for_wizard
・https://note.com/vixer93/n/nac92cc4c0983
- 投稿日:2020-05-22T21:21:14+09:00
rspecでGraphQLのresolverをテストする
はじめに
graphqlのテストってみなさんどう書いてますか?
queryを定義してテストもできますが、少し面倒だど感じている今日この頃。
そこで、queryとかmutationに定義されているresolverをrspecでテストする方法について説明します。mutationの例
例えば、以下のようなmutationを定義しているとしましょう。
mutation.rbmodule Mutations class CreateUser < BaseMutation argument :id, ID, required: true field :user, ObjectTypes::UserType, null: false def resolve(id: nil) user = ::User.create!(id: id) { user: user } end end end単純ですが、idをもらってuserを作成するmutationです。
今回のテスト対象はこのresolveになります。rspceを書く
ではさっそくrspecを書きたいと思います。
create_user_rspec.rbrequire 'rails_helper' RSpec.describe CreateUser, type: :request do describe 'resolver' do it 'userが作成されていること' do mutation = CreateUser.new(field: nil, object: nil, context:{}) mutation.resolve(id: [作成するuser_id]) expect(..).to eq .. endまず、mutationのクラスである
CreateUser
クラスのインスタンスを作成します。
引数のfieldとobjectは基本的にnullで良いと思います。
contextについては、場合によってはcurrent_user
を入れることもできます。その場合はcontext:{current_user: User.first}
となります。contextを直接入れられるのが便利ですよね。
そして、作成したmutationのresolveメソッドを読んであげればテスト上で定義したresolve内の処理が実行されます。
これだと、graphqlのテストがかなり楽にかけます!
- 投稿日:2020-05-22T21:01:10+09:00
例外のまとめ 自分用
記事まとめ
https://speakerdeck.com/jnchito/number-rubykansai-2018-01-13?slide=28
例外の基本
パターン1 def some_method 1 / 0 rescue ZeroDivisionError => e puts e.class puts e.message puts e.backtrace end パターン2 def some_method begin 1 /0 rescue ZeroDivisionError => e puts e.class puts e.message puts e.backtrace end end使っていい場合
仲間を道ずれにしたくない場合など
users.each do |user| beginn send_mail_to(user) rescue => e puts e.backtrace end end例外をかくポイント
- ログを残す
- backtraceを
通知する
- slackなどに
例外の対象範囲は狭くする
例外もテストする
例外の対応方針
業務エラーとシステムエラーに切り分ける
- 業務エラー
- ユーザーミス
- 権限エラー
- システムエラー
- バグ(シンタックなど)
- ネットワーク、DBエラー
業務エラー
=>がモデルで対応
=> 返り血を検証する(saveなども true falseが返ってきている)システムエラー
- 投稿日:2020-05-22T20:37:54+09:00
エラー対応方法
- ログをみる観点
- エラーは自分が書いたところからのエラーの確認
- ライブラリーのエラーの確認
- ユーザーはどんな操作をしたのかの確認
参考
https://qiita.com/jnchito/items/056325421b7e36f02335
長くて飽きてしまった
- 投稿日:2020-05-22T18:00:28+09:00
iframeタグを用いてyoutube動画を埋め込む方法(haml)
はじめに
Railsアプリケーションを作成する際に、youtubeの埋め込み方法を調べたのでまとめます。
今回はhamlを用いたマークアップで、youtubeを表示させることができます。前提環境
今回作成したアプリケーションの環境は下記の通りです。
- ruby 2.5.1
- Rails 5.2.4.3
完成見本
無事埋め込む事ができるとこのような画面になります。
それでは早速実装していきましょう!hamlを導入する
念のため、hamlの導入方法から説明していきます!
gemを導入するだけなので簡単です!Gemfileの1番下に下記のように記述します。
※ Gemfileに記述したらbundle installを忘れないように!!Gemfilegem 'haml-rails'ターミナル% bundle installこれでhamlを導入することができました!
しかし、既存のファイルには haml が適用されていないので、下記のように記述して haml に変更します。
ターミナル% rails haml:erb2haml実行して、下のような記述が出てきたときは、" y " と入力してEnterを押してください。
ターミナルWould you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)これで既存のファイルにもhamlを適用させることができました!!
Youtube動画のURLをコピーする
それではいよいよyoutubeの埋め込みの方に入っていきます。
今回は、こちらのyoutubeを例にして実装していきます。まず、表示させたいyoutube動画の、HTML埋め込みタグをコピーしてください。
コピーは、下記の手順でできます。
ビューファイルに動画を埋め込む
では、コピーができたら、ビューファイルの方をいじっていきます。
今回使用するのはiframeタグと呼ばれるものです。iframeタグをhamlで記述すると下のような書き方になります。
%iframe{ この中にyoutubeの情報を記述していく }まずyoutube動画を表示させたいビューファイルを開き、先ほどコピーしたものを1度そのままペーストします。
〇〇〇.html.haml<iframe width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>ペーストしたら、実際にhamlの書き方に直していきます。
タグの中の
width 〜 allowfullscreen
の部分を、%iframe {}
の{}
の中にコピペしてください。〇〇〇.html.haml<iframe width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> ⬇️ ⬇️ ⬇️ %iframe{ width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen }このままだとエラーが出てしまうので、さらに修正を加えていきます。
修正をする箇所はいくつかあります。細かい変更なので例を見てもらった方がわかりやすいかと思います。〇〇〇.html.haml%iframe{ width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen } ⬇️ ⬇️ ⬇️ %iframe{ width: "560", height: "315", src: "https://www.youtube.com/embed/_ZRp7KYXM1A", frameborder: "0", allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture", allow: "fullscreen" }一応、修正箇所を書き出すと、
=
から:
に変更- 属性の後に
,
を追加allowfullscreen
をallow: "fullscreen"
に変更このような変更を行っています。
最初のHTMLタグの記述と、修正後の記述を比べてみるとこのような違いになっています。
元の記述(htmlバージョン)<iframe width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>直した記述(hamlバージョン)%iframe{width: "560", height: "315", src: "https://www.youtube.com/embed/_ZRp7KYXM1A", frameborder: "0", allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture", allow: "fullscreen" }以上の操作で、無事youtubeの動画を表示させることができました!
ちなみに、パラメータ(
width
やheight
など)の値を変えることで表示される大きさを変更したりすることも可能です!パラメータは、元から記述されているもの以外にも、
- 動画の開始時間や終了時間を決めるパラメータ
- 動画終了後の関連動画を非表示にするパラメータ
などいくつか存在しています。
パラメータについては、今後時間がある時に追記して行けたらと思います!!
- 投稿日:2020-05-22T16:24:50+09:00
RSpecでActiveRecord::StatementInvalid: Could not find tableが出た
出たエラー
ActiveRecord::StatementInvalid: Could not find tableテストを実行したときにDBのテーブルを見つけられない
やってみたこと
rails db:migrate:reset RAILS_ENV=testでテストDBをリセットして反映
rails dbconsole -e test sqlite> .tableでテーブルができたか確認。作成された。
多くの人はここまでで解決と思うが、もう一度テストを走らせると、
ActiveRecord::StatementInvalid: Could not find table同じエラー。
そして、またテーブルを確認してみると、テーブルが消えてた。
これを踏まえてやってみたこと
ActiveRecord::Migration.maintain_test_schema!を外す
rails_helper.rbで
rails_helper.rb#コメントアウトするコード ActiveRecord::Migration.maintain_test_schema!これをコメントアウトした。
そしたら通った。が、まだテーブルは作られず、そのテーブルを使うテストは全て落ちた。
ActiveRecord::Migration.maintain_test_schema!とは
rspecを実行する前にschema.rbをみて、testのDBを自動でマイグレーションしてくれるためのコード。
これを外すことでテストが実行されるということは、schemaに問題ある可能性ありだと思ったので確認。
Could not dump table "テーブル名" because of following StandardError
schemaファイルにこのように書かれていた。
テーブルをschemaに反映できていない。
migrationファイルを確認
すると、
migration_name.rbdef up change_column :comments, :discovery_id, :reference, null: true endとしていた箇所があった。
型にreferenceなんてないが、なぜかこうしてしまっていた。
:referenceを:integerに変えて、rails db:migrate:reset
を行うと、schemaに記述された。再度走らせる
schemaにしっかり記述されてから、もう一度マイグレーションをリセット
rails db:migrate:reset RAILS_ENV=testそして、ActiveRecord::Migration.maintain_test_schema!のコメントを外す
rails_helper.rb#コメントアウトを外す ActiveRecord::Migration.maintain_test_schema!そしてテストを走らせる
bin/rspec全テスト通った。
まとめ
テスト実行 → テーブルが見つからない → テスト環境でリセットしてマイグレーション → テーブルできた → またテスト実行 → なくなって見つからない → 実行直前のスキーマ読まないようにする → テストは動く → スキーマに見つからないテーブルが記述されてなかった → 適切にマイグレーションする → テスト走る
こんな流れだった。
- 投稿日:2020-05-22T14:55:48+09:00
【チーム開発】マイグレーションの更新作業
概要
チーム開発において、DBの修正が発生することもあり!!
安易に更新してしまうとサーバーがおかしくなってしまうのでは???
チームに迷惑かけちゃう
と、私も更新することが怖かったのですが、以下の手順を踏んだら問題なくできました実施方法
$rake db:drop #DBの情報を全て削除!!登録したユーザー情報なども削除されます! $rake db:create #DBの更新 $rake db:migrate #マイグレート $rake db:migrate:status #きちんとマイグレートできているかを確認ちなみに、、、
カテゴリーなどをテキストエディタに作成していたら、下記のコードもマイグレートした後に行なってください!!$rake db:seedこれでDBの更新OK
この手法は、登録したユーザー情報なども合わせて削除されるので、また登録する必要があります!参考文献
- 投稿日:2020-05-22T14:03:57+09:00
【Rails】hidden_fieldでのエラー対処法
環境
Mac OS
Rails5.2状況
Railsでチャット機能を作成中で、
下記の機能を搭載したいと考えておりました。①form_withヘルパーを用いて、チャットでメッセージを送りたい
②どのチャットルームのメッセージなのかを判断できる様、
【chat_room_id】のデータをhidden_fieldを用いて、form_withのパラメータと一緒に渡したい。上手くいかなかった例
chat_rooms/show.html.erb<%= form_with(model: @chat_message ,url:chat_room_path,local:true,method: :post) do |f| %> <%= f.label :message %> <%= f.text_field :message, :class => "form-control myform", :placeholder => "メッセージを入力して下さい" %> <%= f.hidden_field :chat_room_id, :value => @chat_room.id %> <div class="text-center"> <%= f.submit "チャットを送る", :class => "btn btn-primary",method: :post %>¥ </div> <% end %>上手くいった(chat_room_idを渡せた)例
chat_rooms/show.html.erb<%= form_with(model: @chat_message ,url:chat_room_path,local:true,method: :post) do |f| %> <%= f.label :message %> <%= f.text_field :message, :class => "form-control myform", :placeholder => "メッセージを入力して下さい" %> <%= f.hidden_field :chat_room_id, :value => "@chat_room.id" %> <div class="text-center"> <%= f.submit "チャットを送る", :class => "btn btn-primary",method: :post %> </div> <% end %>
<%= f.hidden_field :chat_room_id, :value => "@chat_room.id" %>
と、""
をつけて記載することで@chat_room.idをもったデータをコントローラに送ることができました。
- 投稿日:2020-05-22T13:23:35+09:00
Visual Studio Codespaces で Ruby on Rails
最近 Rails を始めた VSCoder ですが、以下の問題点がありました。
※VSCoder: Visual Studio Code 愛用者
- 環境構築が面倒
そのため、AWS の Cloud9 を利用していたのですが、今度は以下の問題が
- ブラウザ上のエディタは使いづらく違和感がある
- VSCode が使いたい
そこで今ホットな Visual Studio Codespaces に手を出してみて、人生変わりました。
Visual Studio Codespaces とは?
一言で言うと、クラウド上の開発環境です。
公式サイトから引用すると、以下の特徴が挙げられます。
- Git リポジトリ、拡張機能、および組み込みのコマンドライン インターフェイスを備えたブラウザーベースのエディターである
- どのデバイスからでもアプリケーションを編集、実行、デバッグできる
また、私にとって最大の特徴は、
- デスクトップアプリの VSCode でも開発できる
ということです。もちろん拡張機能も追加できます。
Codespace を作成する
- Azure のアカウントを作成します。
- Visual Studio Codespaces のサイトから、Azure アカウントで Sign in します。
- Create Codespace で作成します。Codespace Name だけ指定すれば、あとはデフォルトで十分だと思います。既存のリポジトリがあれば、Git Repository で指定すると自動的に Clone してくれます。(Rails + Space で Railspace という名前にしてみました)
ターミナルも使えます。vsonline というユーザで workspace が作られています。
デスクトップアプリの VSCode で開く
- Visual Studio Code のインストール
- ウィンドウ左側のツールバーの Extensions で Visual Studio Codespaces をインストール
- Remote Explorer のアイコンができるのでそこから先ほど作成したのと同じアカウントに Sign in します。
- 先ほど作成した Railspace という Codespace が出てくるので、コンセントのマークをクリックして connect します。
Ruby on Rails の環境構築
Cloud9 はデフォルトで Ruby や Rails の環境がありますが、Visual Studio Codespaces では残念ながらありません...。まあどうせ、 Cloud9 でもバージョン変えたりするので。
以下はすべて、Codespace に接続した デスクトップアプリの VSCode のターミナルで行います。
Codespaces は Linux 環境を使用しているので、基本的には普通の Linux への環境構築と同じです。rbenv のインストール
Ruby 公式サイトでもおすすめしているこちらの README を参考にインストールしていきます。rbenv によって Ruby の複数のバージョンを管理できます。
まずはリポジトリをクローンしてパスに追加し、セットアップします。
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc $ echo 'export PATH="$HOME/.rbenv/shims:$PATH"' >> ~/.bashrc $ ~/.rbenv/bin/rbenv initその後、ターミナルを再起動する必要があるので、 + ボタンで新しいターミナルを開いてください。
以下のコマンドを入力して確認できますが、rbenv install が not found になっています。$ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash指示されたリンクの通り、以下で解決します。
$ mkdir -p "$(rbenv root)"/plugins $ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-buildもう一度確認すると、今度はうまくいっているはずです。
$ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bashこれで rbenv は完了です。私はここが山場でした。
Ruby と Rails のインストール
主要な Ruby のバージョンを確認できます。すべて見たい場合は、
$ rbenv install --list-all
で可能です。$ rbenv install --listあとは
$ rbenv install [バージョンナンバー]
で好きなバージョンをインストールできます。$ rbenv install 2.7.1インストール後、以下のコマンドでどのバージョンの Ruby を使うか指定します。
$ rbenv global 2.7.1続いて、Rails をインストールします。
$ gem install railsアプリの作成とローカルホストへのアクセス
あとはまるでローカル環境であるかのように開発ができます。
ローカルホストも使えます。$ rails new SampleAppで Rails アプリを作成後、
$ rails sでサーバを立ち上げます。
ブラウザでhttp://localhost:3000/
にアクセスすると...
以上です。
Rails に関しては始めて2カ月なので至らない点があるかもしれないです。
修正やコメントお待ちしてます。
- 投稿日:2020-05-22T13:04:28+09:00
railsを使ったajaxの非同期通信概要
非同期通信って何?
結論、ネットにつなぐことなく更新できることです。
具体例としてTwitterのいいね!機能があげられます。
試しにネット環境を遮断していいね!を押してみてください。
いいね!が普通に押せます。
これが非同期通信です。
この実装ができるようになるとページをいちいちリロードせずに
機能を反映することができ、
ユーザーの視点から見るとものすごく便利な機能です。非同期通信をするために何を使うのか
結論、AjaxとJSONです。1個ずつ説明していきます。
Ajax
非同期通信を英語で言うと"Asynchronous JavaScript + XML"です。
略してAjax!これだけです。
Ajaxと言われたり、そのコードを見かけたら非同期通信のことだと思ってください。JSON
結論、サーバからのレスポンスで返す時の形式になるものです。
これだとよくわかりませんので具体例を示しながら説明します。
まず、以下のようなコードがあったとします。{fruits: "orange", vegetable: "onion"}これはrubyの時にも学んだキーとバリューの組み合わせです。
特に何にも設定していなければサーバーからキーとバリューのレスポンスを受け取る時
HTML形式で受け取っています。
キーとバリューって何だっけ?
キーはカテゴリーでバリューはカテゴリーの中にある具体例のようなものでした。そしてこのキーとバリューをセットで扱うためにハッシュを用いるのでした。
HTML形式で受け取るとネット通信が始まり、そこでデータが更新されていくようなイメージです。
ではこれをJSON形式で受け取るとどうなるのか?
ネット通信を始めることなく一部だけページを書き換えるデータを受け取ることができます。
つまり、これが非同期通信です。
JSON形式でデータを受け取ることによって非同期通信が可能になると理解しておきましょう。非同期通信の実装概要
以下、手順です
①JavaScriptでリクエストを送り、レスポンスをJSON形式に変更する
②JSON形式でレスポンスしてもらうようにコントローラーのアクションにその旨を追記する。
③レスポンスするためのJSON形式のビューを追加する
あと細かい作業もあるのですがこの概要だけ抑えておくと、
実装のタイミングでスムーズに進められると思います。
また具体的な手順は後日、記述しようと思います。
- 投稿日:2020-05-22T11:29:33+09:00
rails CSV.open とCSV.generateの使う場面の違い CSV
CSV.open とCSV.generateの使う場面の違い
- csvの使い方を調べると、
CSV.open
が多い- 会社の実装を見てみると
CSV.generate
が多い気がするなぜ場面によって違うのか?
csvの出力先を【ディレクトリ配下に置く】のか。【export】するのか(railsで)によって異なるのかな。
## CSV.open ### これはopenの第一引数の指定先(output.csv)に出力する事を想定している ### 実行 すると同じディレクトリ配下に ls csv_ruby.rb output.csvみたいになる csv_ruby.rb # ファイル名 lists = [["1","2","3"],["4","5","6"]] CSV.open("./output.csv", "w") do |row| lists.each do row << list end end ## CSV.generate lists = [["1","2","3"],["4","5","6"]] csvs = CSV.generate do |row| lists.each do row << list end end ## これは出力結果を なにかしらの変数に入れて中身を確認する必要がある ## 最終的にsenddataの引数に渡してダウンロードする def index format.csv do sendata(csvs, type: :csv, failename: "ファイル名") end endまとめ
こんな感じで、最終的な期待結果に応じて、csvの作り方が異なる
- 投稿日:2020-05-22T06:58:39+09:00
【Rails】CarrierWaveとrmagickでプロフィール画像を設定できるようにする
CarrierWaveで画像のアップロード機能を実装する
今回はユーザーテーブルにプロフィール画像を設定できるようにします。
1. モデルに画像アップロード用のカラムを設定する
userモデルに画像をアップロードするためのimageカラムを追加します。
$ rails g migration add_image_column_to_users image:stringマイグレーションファイルの内容を適用します。
$ rails db:migrationもしすでにデータベース内にimageカラムを持っていないユーザーデータが存在しているなら、データベースの中身を一度リセットする必要があります。
$ rake db:migrate:reset2. Gemfileにcarrierwaveを追加してbundle installを実行
Gemfilegem 'carrierwave'$ bundle installサーバーの再起動も忘れないように、このタイミングで。(基本いつでも大丈夫)
rails s3. rails g uploader imageで画像のアップローダークラスを作成
$ rails g uploader imageこれで画像をアップロードできるようになります。
4. アップローダを実装したいクラスに以下のコードを追加
/app/models/user.rbmount_uploader :image, ImageUploader5. Strong Parameterにimageカラムを追加
/app/controllers/users_controller.rbdef user_params params.require(:user).permit(:name, :email, :image) endこれで画像をアップロードする準備が整いました、あとはviewを記述するだけです。
6. 画像の表示・投稿するためのviewを記述
画像表示するためのview
/app/views/users/show.html.erb<% if @user.image? %> <%= image_tag @user.image.url %> <% end %>画像を投稿するためのview
/app/views/users/new.html.erb<%= f.label :image %> <%= f.file_field :image %>これで画像の表示・投稿機能が完成しました!
アップロードした画像はpublicフォルダ内に格納されていました。しかしこのままではアップロードしたときの画像名・大きさでフォルダ内に保存されるので、画像の加工を行うgemのrmagickを利用します。rmagicで画像の加工を行う
rmagickのgemをインストールするにはimageMagickがインストールされていないといけないそうです。
imageMagickがインストールされているかは以下のコマンドで確認できます。
$ convert -versionもしインストールされていなかったらimageMagickをインストールしてください。Macの場合はbrewを使えばインストールできるそうです。
$ brew install imagemagick@61. Gemfileにrmagickを追加してbundle installを実行します
Gemfilegem 'rmagick'$ bundle installサーバーの再起動も忘れないでね
2. image_uploader.rbで設定を記述する
画像をどのような形式に加工するかは、/app/uploaders/image_uploader.rbで設定できます。
デフォルトでは以下のようになっています、このファイルを編集していきます。
/app/uploaders/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick # include CarrierWave::MiniMagick # Choose what kind of storage to use for this uploader: storage :file # storage :fog # Override the directory where uploaded files will be stored. # This is a sensible default for uploaders that are meant to be mounted: def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # Provide a default URL as a default if there hasn't been a file uploaded: # def default_url(*args) # # For Rails 3.1+ asset pipeline compatibility: # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_')) # # "/images/fallback/" + [version_name, "default.png"].compact.join('_') # end # Process files as they are uploaded: # process scale: [200, 300] # # def scale(width, height) # # do something # end # Create different versions of your uploaded files: # version :thumb do # process resize_to_fit: [50, 50] # end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: # def extension_whitelist # %w(jpg jpeg gif png) # end # Override the filename of the uploaded files: # Avoid using model.id or version_name here, see uploader/store.rb for details. # def filename # "something.jpg" if original_filename # end end変更を加えたファイルはこのようになりました。
/app/uploaders/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # リサイズしたり画像形式を変更するのに必要 include CarrierWave::RMagick # 画像の上限を640x480にする process :resize_to_limit => [640, 480] # 保存形式をJPGにする process :convert => 'jpg' # サムネイルを生成する設定 version :thumb do process :resize_to_limit => [300, 300] end version :thumb100 do process :resize_to_limit => [100, 100] end version :thumb30 do process :resize_to_limit => [30, 30] end # jpg,jpeg,gif,pngしか受け付けない def extension_white_list %w(jpg jpeg gif png) end # 拡張子が同じでないとGIFをJPGとかにコンバートできないので、ファイル名を変更 def filename super.chomp(File.extname(super)) + '.jpg' if original_filename.present? end # ファイル名を日付にするとタイミングのせいでサムネイル名がずれる #ファイル名はランダムで一意になる def filename "#{secure_token}.#{file.extension}" if original_filename.present? end protected def secure_token var = :"@#{mounted_as}_secure_token" model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid) end endこれで完了です!
参考
https://pg-happy.jp/carrierwave-rmagic-uploader.html
https://nyoken.com/rails-carrierwaveimage_uploader.rbについて
http://www.workabroad.jp/tech/1118
https://qiita.com/tackey/items/ba68ca8489500b7cb739
- 投稿日:2020-05-22T05:48:44+09:00
controller を間違えて生成してしまったら
rails tutorial 3章 「ほぼ静的なページの作成」 にてcontrollerを生成する際にcontroller名を間違えて生成してしまいました。
その時の対処法です。正
controller Static_Pages_controller.rb
アクション home help誤
controller Static_controller.rb
アクション Pages home help原因
controllerを生成する際にコマンドラインで
rails g controller Static Pages home help
としてしまった為、Pages がアクションとして認識されてしまったんですね、、、対処
rails destroy controller Static Pages home help
これで間違えて生成したcontrollerは削除できました。再度
rails g controller StaticPages home help
で正しいcontrollerを生成ちなみに
rails g controller Static_Pages home help
でも同じStatic_Pagesコントローラーが生成されます。
- 投稿日:2020-05-22T00:44:35+09:00
Rails 6のdevelopment環境でhttpsを使う
はじめに
この記事はRails6をしばらく使用し、ハマった部分、忘れたくないことを将来の自分のためにメモとして残し、公開することで同じようなことをRails6で実現しようとしている方のお役に立てたら、うれしいなと思い書いています。
この記事を読んで「助かったぜ!」、「それは間違ってるぜ!」や「もっとこうした方がいいぜ!」
がありましたら、お知らせ頂ければと思います。実現したこと
- Rails 6.0.3.1のdevelopment環境で
webpack-dev-server
とpuma
の起動をforeman
で同時に起動しhttps
を使うなんでdevelopment環境でhttpsを使う必要があるの?
development環境で
rails s
を実行してRailsアプリ(Server)を起動するとデフォルトではhttp
で起動することになります。
ところが、どこかのサービスが提供しているAPIを叩く際、エンドポイントURLをhttpsで指定しなければならない場合がほとんどなのでdevelopmentでAPIを叩いて連携し、目的の情報を正しく取得、加工、表示、できているのかを確認できません。なんでwebpack-dev-serverを使うの?
Rails6ではデフォルトでJavaScriptの管理がWebpacker環境となりますので
rails s
だけだと、ページが表示されるまでに時間がかかり不便なのでwebpack-dev-server
を利用することでファイルが更新されたら勝手にコンパイルしてくれるようにし、開発効率を上げるためです。なんでforemanを使うの?
Railsアプリを起動する度に
rails s
とwebpack-dev-server
のコマンドを打つ必要があり、これまた面倒です。
そこで登場するのがforeman
です。要するに
- ムダなことに時間をとられたくない
- 人生(命)を削られたくない
- コンピュータを自由自在に操るのが生き甲斐なのに、Rails6のデフォルトだとコンピュータに操られてるみたいで屈辱的なので絶対に許せない
ってことです
SSL証明書どうするの問題の解決
Railsの開発環境でhttpsを使う
https://qiita.com/itkrt2y/items/2837a45061b9a3b711b7
という記事を参考にさせて頂きました。@itkrt2y さん、ありがとうございます!ただし、Rails6では1箇所だけエラーを引き起こす箇所がありました。
エラー内容
NameError: uninitialized constant #<Class:#<Puma::DSL:0x00007f9070276ea8>>::Rails
該当箇所(1行目のところが原因です)
config/puma.rbif Rails.env.development?なので下記のように修正することでエラーを回避しました。
config/puma.rbif ENV['RAILS_ENV']=='development'これで
rails s
を実行します。
すると、config/certs/
配下にlocalhost.key
とlocalhost.cert
が生成されます。webpack-dev-serverのhttpsを有効化
developmentのdev_serverのhttpsをtrueに変更。下記コードの一番最後の行のところです。
config/webpacker.ymlevelopment: <<: *default compile: true # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules check_yarn_integrity: true # Reference: https://webpack.js.org/configuration/dev-server/ dev_server: https: trueforemanを使ってラクになる
foreman で アプリケーションを動かす。
https://qiita.com/7kaji/items/6a59977d2ad85604e7fd
という記事を参考にさせて頂きました。 @7kajis さん、ありがとうございます!インストール
$ gem install foreman設定
下記の設定ではportを443にしていますが、必要に応じて8443などに変更してください。
Procfilerails: rails s -b 'ssl://0.0.0.0:443?key=config/certs/localhost.key&cert=config/certs/localhost.cert' webpack: bin/webpack-dev-server --https起動!
下記のコマンドを実行し、Railsアプリを起動します。 https://localhost/ にアクセスし、ページが表示されれば成功です。
foreman startはぁ〜ラクチン。幸せだなぁ。