20211022のRailsに関する記事は21件です。

railsチュートリアル第九章 二つの目立たないバグ

二つの目立たないバグ ユーザーがログイン中の場合にのみログアウトさせる必要がある。 、ユーザーが複数のブラウザ(FirefoxやChromeなど)でログインしていたときに起こります 2番目のウィンドウでユーザーをログアウトするテスト test/integration/users_login_test.rb require 'test_helper' class UsersLoginTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) # ハッシュで呼び出す。 # なんでもハッシュで呼び出せるようになるのか? end . . . test "login with valid information followed by logout" do get login_path # ログイン画面にいく post login_path, params: { session: { email: @user.email, password: 'password' } } # データをデータベースに投稿 assert is_logged_in? # ログインされているよね? assert_redirected_to @user # ユーザー情報を取り出すのか? follow_redirect! # もう一回確認 assert_template 'users/show' # ユーザーページに移動する assert_select "a[href=?]", login_path, count: 0 assert_select "a[href=?]", logout_path assert_select "a[href=?]", user_path(@user) delete logout_path assert_not is_logged_in? assert_redirected_to root_url # 2番目のウィンドウでログアウトをクリックするユーザーをシミュレートする delete logout_path follow_redirect! assert_select "a[href=?]", login_path # これは表示する assert_select "a[href=?]", logout_path, count: 0 assert_select "a[href=?]", user_path(@user), count: 0 # 上2行は非表示になっていることを確認。 end end delete logout_pathを書くことによって二度目のログアウトを試すことができることがわかった。 テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 8268 Started with run options --seed 30959 ERROR["test_login_with_valid_information_followed_by_logout", #<Minitest::Reporters::Suite:0x0000555ef3ce2c08 @name="UsersLoginTest">, 5.425516088000222] test_login_with_valid_information_followed_by_logout#UsersLoginTest (5.43s) NoMethodError: NoMethodError: undefined method `forget' for nil:NilClass app/helpers/sessions_helper.rb:58:in `forget' app/helpers/sessions_helper.rb:67:in `log_out' app/controllers/sessions_controller.rb:32:in `destroy' test/integration/users_login_test.rb:44:in `block in <class:UsersLoginTest>' 24/24: [============================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.96672s 24 tests, 58 assertions, 0 failures, 1 errors, 0 skips NoMethodError: undefined method `forget' for nil:NilClass NoMethodError: 「メソッドがない」という意味 undefined method `forget' for nil:NilClass nilクラスになっているためforgetメソッドが見つからないのかな? ログイン中の場合のみログアウトする app/controllers/sessions_controller.rb class SessionsController < ApplicationController . . . def destroy # ユーザー削除 log_out if logged_in? # log_outメソッドを発動 もしログインされてたら redirect_to root_url # ホーム画面に戻る end end テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 4083 Started with run options --seed 36998 24/24: [============================] 100% Time: 00:00:03, Time: 00:00:03 Finished in 3.43251s 24 tests, 61 assertions, 0 failures, 0 errors, 0 skips 上のコードで条件をつけることによってログアウトできるようにしたみたい。 ダイジェストが存在しない場合のauthenticated?のテスト test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase . . . test "authenticated? should return false for a user with nil digest" do assert_not @user.authenticated?('') # 記憶ダイジェストはからじゃないよね? end end テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 4630 Started with run options --seed 26378 ERROR["test_authenticated?_should_return_false_for_a_user_with_nil_digest", #<Minitest::Reporters::Suite:0x00007fd9c14daa60 @name="UserTest">, 2.703540293999822] test_authenticated?_should_return_false_for_a_user_with_nil_digest#UserTest (2.70s) BCrypt::Errors::InvalidHash: BCrypt::Errors::InvalidHash: invalid hash app/models/user.rb:49:in `new' app/models/user.rb:49:in `authenticated?' test/models/user_test.rb:88:in `block in <class:UserTest>' 25/25: [============================] 100% Time: 00:00:03, Time: 00:00:03 Finished in 3.43387s 25 tests, 61 assertions, 0 failures, 1 errors, 0 skips BCrypt::Errors::InvalidHash: BCrypt::Errors::InvalidHash: invalid hash 無効なハッシュがあるからエラーになるのかな? authenticated?を更新して、ダイジェストが存在しない場合に対応 app/models/user.rb class User < ApplicationRecord . . . # 渡されたトークンがダイジェストと一致したらtrueを返す def authenticated?(remember_token) return false if remember_digest.nil? # 記憶ダイジェストが空の場合 falseを返すことができる BCrypt::Password.new(remember_digest).is_password?(remember_token) # remember_tokenとremamber_digestを比較するもの # bycryptで暗号化 # .is_password?で==の働きをする end # ユーザーのログイン情報を破棄する def forget # ユーザーがログアウトできるようにするため update_attribute(:remember_digest, nil) # .forgetで.rememberが取り消される # nilに更新される end end テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 4973 Started with run options --seed 52742 25/25: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.38406s 25 tests, 62 assertions, 0 failures, 0 errors, 0 skips 演習 1. リスト 9.16で修正した行をコメントアウトし、2つのログイン済みのタブによるバグを実際に確かめてみましょう。まず片方のタブでログアウトし、その後、もう1つのタブで再度ログアウトを試してみてください。 コメントアウト後accountが消えなかった。 修正後消えた。 2. リスト 9.19で修正した行をコメントアウトし、2つのログイン済みのブラウザによるバグを実際に確かめてみましょう。まず片方のブラウザでログアウトし、もう一方のブラウザを再起動してサンプルアプリケーションにアクセスしてみてください。 できなかった。 3. 上のコードでコメントアウトした部分を元に戻し、テストスイートが red から green になることを確認しましょう。 buntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 8142 Started with run options --seed 10285 ERROR["test_authenticated?_should_return_false_for_a_user_with_nil_digest", #<Minitest::Reporters::Suite:0x000055d781f9bba0 @name="UserTest">, 5.757596166999974] test_authenticated?_should_return_false_for_a_user_with_nil_digest#UserTest (5.76s) BCrypt::Errors::InvalidHash: BCrypt::Errors::InvalidHash: invalid hash app/models/user.rb:51:in `new' app/models/user.rb:51:in `authenticated?' test/models/user_test.rb:88:in `block in <class:UserTest>' FAIL["test_login_with_valid_information_followed_by_logout", #<Minitest::Reporters::Suite:0x000055d781e67db0 @name="UsersLoginTest">, 6.913151000999733] test_login_with_valid_information_followed_by_logout#UsersLoginTest (6.91s) Expected true to be nil or false test/integration/users_login_test.rb:41:in `block in <class:UsersLoginTest>' 25/25: [============================] 100% Time: 00:00:07, Time: 00:00:07 Finished in 7.02619s 25 tests, 57 assertions, 1 failures, 1 errors, 0 skips BCrypt::Errors::InvalidHash: BCrypt::Errors::InvalidHash: invalid hash ダイジェストが無効になっているためかな? Expected true to be nil or false 記憶ダイジェストが空になっていない。 多分ブラウザが違うとログイン状態になったままなのかな?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第九章 ユーザーを忘れる

ユーザーを忘れる forgetメソッドをUserモデルに追加する class User < ApplicationRecord . . . # ユーザーのログイン情報を破棄する def forget # ユーザーがログアウトできるようにするため update_attribute(:remember_digest, nil) # .forgetで.rememberが取り消される # nilに更新される end end テスト ERROR["test_login_with_valid_information_followed_by_logout", #<Minitest::Reporters::Suite:0x0000555ef4c714d0 @name="UsersLoginTest">, 2.2499602300001698] test_login_with_valid_information_followed_by_logout#UsersLoginTest (2.25s) ActionView::Template::Error: ActionView::Template::Error: undefined local variable or method ` ' for #<#<Class:0x0000555ef1c65cc8>:0x0000555ef4b69b50> app/helpers/sessions_helper.rb:32:in `current_user' app/helpers/sessions_helper.rb:43:in `logged_in?' app/views/layouts/_header.html.erb:8 app/views/layouts/application.html.erb:9 test/integration/users_login_test.rb:43:in `block in <class:UsersLoginTest>' 24/24: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.53897s 24 tests, 58 assertions, 0 failures, 1 errors, 0 skips ActionView::Template::Error: undefined local variable or method テンプレートが表示されていない。のか? ActionView::Template::Error: .html.erbからhtmlを生成するときにエラーが起きたという意味 undefined local variable or method 知らない変数やメソッドが使われていますよという意味 多分知らないメソッドができて困っているという意味なのかな? 永続セッションからログアウトする app/helpers/sessions_helper.rb module SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id #sessionメソッドで作成した一時cookiesは自動的に暗号化される # ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成 end . . . # 永続的セッションを破棄する def forget(user) user.forget # nilに更新する cookies.delete(:user_id) cookies.delete(:remember_token) # クッキーからid,記憶トークンを削除する end # 現在のユーザーをログアウトする def log_out forget(current_user) session.delete(:user_id) # sessionのidを削除する @current_user = nil # ユーザーをnilにする end end テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 6763 Started with run options --seed 37792 24/24: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.95184s 24 tests, 61 assertions, 0 failures, 0 errors, 0 skips 演習 1.ログアウトした後に、ブラウザの対応するcookiesが削除されていることを確認してみましょう。 確認
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第九章 ログイン状態を保持

ログイン状態を保持 ユーザーの暗号化済みIDと記憶トークンをブラウザの永続cookiesに保存して、永続セッションを作成する準備ができました。 これを実際に行うにはcookiesメソッドを使います。 authenticated?をUserモデルに追加する app/models/user.rb class User < ApplicationRecord . . . # 渡されたトークンがダイジェストと一致したらtrueを返す def authenticated?(remember_token) BCrypt::Password.new(remember_digest).is_password?(remember_token) # remember_tokenとremamber_digestを比較するもの # bycryptで暗号化 # .is_password?で==の働きをする end end トークンとダイジェストの違いがわからん。 ログインしてユーザーを保持する app/controllers/sessions_controller.rb class SessionsController < ApplicationController . . . def create user = User.find_by(email: params[:session][:email].downcase) # 送信されたメアドを使ってデータベースから取り出す。 #emailを小文字にする if user &.authenticate(params[:session][:password]) # user 取得したユーザーが有効かどうか? # その後にデータベース上にパスワードがあるか? # ユーザーログイン後にユーザー情報のページにリダイレクトする # &.は省略型 log_in user # ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成 remember user # ダイジェストをデータベースに記憶させる redirect_to user # 名前付きルート/userのビューを表示する else flash.now[:danger] = 'Invalid email/password combination' # flash.nowでリクエストが発生後メッセージを消滅する # エラーメッセージを作成する render 'new' # newアクションのビューを表示 end end . . . end ユーザーを記憶する app/helpers/sessions_helper.rb module SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id #sessionメソッドで作成した一時cookiesは自動的に暗号化される # ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成 end # ユーザーのセッションを永続的にする def remember(user) user.remember # userをデータベースに保存 cookies.permanent.signed[:user_id] = user.id # signed.permanent ユーザーidと記憶トークンを結びつける # 攻撃者に奪い取られる可能性があるため cookies.permanent[:remember_token] = user.remember_token end . . . end 永続的セッションのcurrent_userを更新する app/helpers/sessions_helper.rb class User < ApplicationRecord . . . # 記憶トークンcookieに対応するユーザーを返す def current_user if (user_id = session[:user_id]) # ログイン中のidを代入する # 永続セッション(ログイン中)にするため @current_user ||= User.find_by(id: user_id) # カレントユーザーまたは検索結果があったものを代入する elsif (user_id = cookies.signed[:user_id]) # そうでなければ 書名付きクッキーを代入 user = User.find_by(id: user_id) # idでユーザーを探す if user && user.authenticated?(cookies[:remember_token])  # ユーザーが有効であり、記憶トークンも認証されたら log_in user # ログインする @current_user = user # カレントユーザーに代入するかな end end end . . . end テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 8950 Started with run options --seed 17566 ERROR["test_login_with_valid_information_followed_by_logout", #<Minitest::Reporters::Suite:0x000055a0ef5c93c0 @name="UsersLoginTest">, 1.6133994830006486] test_login_with_valid_information_followed_by_logout#UsersLoginTest (1.61s) ActionView::Template::Error: ActionView::Template::Error: undefined local variable or method ` ' for #<#<Class:0x000055a0eef11190>:0x000055a0ef288cb0> app/helpers/sessions_helper.rb:32:in `current_user' app/helpers/sessions_helper.rb:43:in `logged_in?' app/views/layouts/_header.html.erb:8 app/views/layouts/application.html.erb:9 test/integration/users_login_test.rb:43:in `block in <class:UsersLoginTest>' 24/24: [============================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.99939s 24 tests, 58 assertions, 0 failures, 1 errors, 0 skips ActionView::Template::Error: undefined local variable or method 演習 1. ブラウザのcookieを調べ、ログイン後のブラウザではremember_tokenと暗号化されたuser_idがあることを確認してみましょう。 確認 2. コンソールを開き、リスト 9.6のauthenticated?メソッドがうまく動くかどうか確かめてみましょう。 >> user = User.first (1.6ms) SELECT sqlite_version(*) User Load (0.8ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "a", email: "abc@def.com", created_at: "2021-10-05 04:11:41", updated_at: "2021-10-09 07:53:18", password_digest: [FILTERED], remember_digest: "$.."> >> user.authenticated? Traceback (most recent call last): 2: from (irb):2 1: from app/models/user.rb:48:in `authenticated?' ArgumentError (wrong number of arguments (given 0, expected 1)) #引数を一つ欲しいらしい。 >> user.authenticated?("") => false >> user.authenticated?("aaaaaa") => false
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails×vue】ログイン後にuser_idを取得してリンクに仕込む

初学者の備忘録です。もっと良い方法や誤りがございましたらご指摘頂けると幸いです。 vueで作成したログインフォームからログイン後にuserのidを取得してヘッダー等のリンクに仕込む方法です(例 users/1)。 ※vuexがインストール済みという前提です。 実装の流れ ①controller/createでuserのidをjson形式で呼び出す ②vue/login componentでaxios.post後にjsonデータを受け取るようにする ③vuexで受け取ったjson形式のuserのidをuserIdとして保存するmutationを作成する ④vue/header componentでlinkにidを仕込む Rails側の実装 api/sessions.controller class Api::SessionsController < ApplicationController def create @user = User.find_by(email: params[:session][:email].downcase) log_in @user render json: { id: @user.id } end end render json: { id: @user.id}でpostリクエスト後に@userのidを呼び出す。 Vue側の実装 Login.vue <script> import axios from 'axios'; data() { return { session: { email: '', password: '', } } }, createSession: function () { axios.post('/api/login', { session: this.session }) .then(response => { const id = response.data.id this.$store.commit('setId', id), this.$router.push({ path: '/'}) }) </script> const id = response.data.idでjson形式のuser.idを定数idに保存する。 以降の箇所は次のvuexで実装します。 store.js import { createStore } from 'vuex' export const store = createStore ({ state() { return { userId: 0 } }, mutations: { setId: (state, id) => { state.userId = id } } }) mutationsではstateとidを引数としてidには前述のresponse.data.idで取り出した定数idの値を与えます。 ※userId:0は初期値の設定なので0に意味はありません。 これでstate.userId = idによりuserIdには最終的にログインしたuserのidが返り値として保存されるようになります。 後はこれも前述の通り、this.$store.commit('setId', id)でmutationを実行し、ログイン後にuserIdというVueのコンポーネント共通で使用できるkeyにuserのidを保存できます。 Header.vue <template> <router-link :to="{name: 'show', params: {id: $store.state.userId}}">マイページ </router-link> </template> router-linkのparamsに仕組まれた「$store.state.userId」はstore.jsのuserIdの値が代入されます。 従ってこのリンクをクリックすると'/users/1'のようにログイン中のユーザーに応じたページを表示できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サービス設計について①!

①.結論! サービス設計とは、アプリ開発の際に事業戦略や構想の組み立てをし、「企画」から「設計」までする事です! それでは手順について説明します! ②.ペルソナ まずはペルソナを決めます! ペルソナとはマーケティングの用語で、「サービスを使用するユーザー」のことです! 似た言葉に「ターゲット」などがありますが、ペルソナはそれらよりも詳細に「サービスを使用するユーザー」を決めます! 年齢層、職業、性別、現在の境遇などまで決めることで、よりリアルに使用者の目線でサービスを考案できます! ③.ユーザーストーリー ユーザーストーリーとは、「ペルソナの課題に対して、どのような機能で解決していくのか」を明確にしたものです! 例えば、ペルソナの課題が「人気のグルメ情報をストックしたいけど、記事などを貯めておくと場所も取るし管理ができない」という課題だった場合、「人気のグルメの一覧を表示する」という機能がユーザーストーリーになります! ユーザーストーリーを確認して、要件定義に移ります! ④.要件定義 要件定義とは、開発者などがアプリケーションの仕様を把握するために、詳細まで言語化することです! ユーザーストーリーの段階では、人によってそれぞれの機能の「完成形のイメージ」が異なります! このままでは、数人で開発する場合に上手くいきません! したがって、開発に関わる人全員で共通認識を持つために、要件を定義していく必要があります! このようにして開発を進めていきます! ⑤.まとめ 各項目簡単に言うと! ペルソナとは、マーケティングの用語で、「サービスを使用するユーザー」のこと! ユーザーストーリーとは、「ペルソナの課題に対して、どのような機能で解決していくのか」を明確にしたもののこと! 要件定義とは、開発者などがアプリケーションの仕様を把握できるために、詳細まで言語化すること! こんな感じだと思います!開発の状況や仕様によって異なりますが、こんな感じの流れが大体です! しっかりと把握していきたいと思います! 何か説明で間違っていたらご指導お願い致します(_ _)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サービス設計について!

①.結論! サービス設計とは、アプリ開発の際に事業戦略や構想の組み立てをし、「企画」から「設計」までする事です! それでは手順について説明します! ②.ペルソナ まずはペルソナを決めます! ペルソナとはマーケティングの用語で、「サービスを使用するユーザー」のことです! 似た言葉に「ターゲット」などがありますが、ペルソナはそれらよりも詳細に「サービスを使用するユーザー」を決めます! 年齢層、職業、性別、現在の境遇などまで決めることで、よりリアルに使用者の目線でサービスを考案できます! ③.ユーザーストーリー ユーザーストーリーとは、「ペルソナの課題に対して、どのような機能で解決していくのか」を明確にしたものです! 例えば、ペルソナの課題が「人気のグルメ情報をストックしたいけど、記事などを貯めておくと場所も取るし管理ができない」という課題だった場合、「人気のグルメの一覧を表示する」という機能がユーザーストーリーになります! ユーザーストーリーを確認して、要件定義に移ります! ④.要件定義 要件定義とは、開発者などがアプリケーションの仕様を把握するために、詳細まで言語化することです! ユーザーストーリーの段階では、人によってそれぞれの機能の「完成形のイメージ」が異なります! このままでは、数人で開発する場合に上手くいきません! したがって、開発に関わる人全員で共通認識を持つために、要件を定義していく必要があります! このようにして開発を進めていきます! ⑤.まとめ 各項目簡単に言うと! ペルソナとは、マーケティングの用語で、「サービスを使用するユーザー」のこと! ユーザーストーリーとは、「ペルソナの課題に対して、どのような機能で解決していくのか」を明確にしたもののこと! 要件定義とは、開発者などがアプリケーションの仕様を把握できるために、詳細まで言語化すること! こんな感じだと思います!開発の状況や仕様によって異なりますが、こんな感じの流れが大体です! しっかりと把握していきたいと思います! 何か説明で間違っていたらご指導お願い致します(_ _)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]devise passwordの最低文字数を変更する

deviseのpasswordカラムは、デフォルトで最低文字数と最高文字数が設定されている -app/config/initializers/devise.rb devise.rb # 中略 # ==> Configuration for :validatable # Range for password length. config.password_length = 6..128 config.password_lengthの、 6 = 最低文字数 128 = 最高文字数 をそれぞれ表している よって、passwordの最低文字数、最高文字数を変更したい時は、上記数字を変更する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]アソシエーションしたActiveHashのモデルの、特定の要素を取得したい。

例:Itemモデルのfee_payer_idと紐付いた、FeePayerモデルのnameカラムの要素を取得したい。 立てた仮説 ItemモデルとFeePayerモデルはアソシエーションの記述を行っている。 →Itemモデルのインスタンス変数を使用して、ビューファイルに呼び出すことが可能なのでは? 添字は、Itemモデルのインスタンス変数から、fee_payer_idを指定して、代入する。 →指定した出品の画像を表示することが可能なはず。 <ul class='item-lists'> <% if @items != nil %> <% @items.each do |item| %> <li class='list'> <%= link_to "#" do %> <div class='item-img-content'> <%= image_tag item.image %> <%# 商品が売れていればsold outを表示しましょう %> <%#<div class='sold-out'> <span>Sold Out!!</span> </div> <%# //商品が売れていればsold outを表示しましょう %> </div> <div class='item-info'> <h3 class='item-name'> <%= item.name %> </h3> <div class='item-price'> <span><%= item.price %>円<br><%= item.fee_payer.name[item.fee_payer.id] %></span> 作業結果 いという、よくわからない文字が表示されただけだった。 作業結果がうまく行かなかった原因 eachメソッドをすでに実装しているにも関わらず、Itemモデルに紐付いたFeePayerモデルのnameカラムを特定しようとしていたから。 →出品されているもの1つずつに対して、値を取得するので、添字で特定する必要がない。 # [item.fee_payer.id]がいらない(eachメソッドですでにレコードは特定されている) <span><%= item.price %>円<br><%= item.fee_payer.name[item.fee_payer.id] %></span> 正しい記述方法 <span><%= item.price %>円<br><%= item.fee_payer.name %></span>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]正規表現まとめ

正規表現とは 文字列に特定の文字が含まれているかを確認することや、特定の文字を取り除くなどの操作を行うための技術。 さまざまな言語で同じようなコードで表すことができるように設計されている。 それによって、ほとんどの記述をRubyやJavaScriptなどの別言語間でも共有することができる。 表現方法 subメソッド 文字列の置換 第1引数に置き換えたい文字列を指定し、第2引数に変換後の文字列を指定します。 また、操作したい文字列は/で囲みます。 irb(main):001:0> str = "りんごを食べる" => "りんごを食べる" irb(main):002:0> str.sub(/りんご/,"みかん") => "みかんを食べる" 指定した文字列すべてを置換したい場合、gsubメソッドを使用する。 irb(main):001:0> tel = '090-1234-5678' => "090-1234-5678" irb(main):002:0> tel.sub(/-/,'') => "0901234-5678" # 最初のハイフンしか置換されない irb(main):003:0> tel.gsub(/-/,'') => "09012345678" matchメソッド 引数に指定した文字列がメソッドを使用した文字列に含まれているか否かをチェックする 含まれている場合は、指定した文字列がMatchDataというオブジェクトが戻り値で得られる。 含まれていない場合は、戻り値としてnilが得られる。 irb(main):001:0> str = "Hello, World" => "Hello, World" irb(main):002:0> str.match(/Hello/) => #<MatchData "Hello"> irb(main):003:0> str.match(/Good/) => nil マッチした文字列はまず、MatchDataという種類のオブジェクトで返されます。 MatchDataから実際にマッチした文字列などを取り出したい場合は、以下の様に配列から最初のデータを取り出す時と同様の形で取り出すことができます。 irb(main):001:0> str = "Hello, World" => "Hello, World" irb(main):002:0> md = str.match(/Hello/) => #<MatchData "Hello"> irb(main):003:0> md[0] => "Hello" 正規表現を使用したバリデーションの記述方法 [a-z] [ ]が、「角括弧に囲まれた文字のうち、いずれか1つがマッチする」を確認する。 -が、マッチする範囲を設定している。 → アルファベットのaからzのうち、いずれか1文字が含まれているというバリデーションになる。 irb(main):001:0> 'dog'.match(/[a-c]/) => nil \d dは「特殊文字」と呼ばれ、dは数字を表す特殊文字。 特殊文字を使用する場合、\を直前に記載するルールが有る。 → 数字が1つ含まれているというバリデーションになる。 irb(main):001:0> 'I have 3 pens'.match(/\d/) => #<MatchData "3"> {n ,m} 直前の文字が少なくてもn回、多くてもm回マッチすることを確認する。 irb(main):001:0> '12345678'.match(/\d{4,6}/) => #<MatchData "123456"> irb(main):002:0> '123'.match(/\d{4,6}/) => nil i 大文字、小文字を区別することなくマッチングを確認できる。 irb(main):003:0> 'Cat'.match(/cat/) => nil irb(main):004:0> 'Cat'.match(/cat/i) => #<MatchData "Cat"> . ハイフンやピリオドなど含めた全ての英数字において、どの1文字にもマッチします。 irb(main):001:0> 'hoge'.match(/./) => #<MatchData "h"> + 直前の文字が1回以上の繰り返しにマッチします。 irb(main):001:0> 'aaabb'.match(/a+/) => #<MatchData "aaa"> \A 直後に置いた文字を、文字列の先頭に持つ場合にマッチする。 \A\d{3} # 文字列の先頭に、少なくとも数字が3つ以上含まれているというバリデーション \z 直前に置いた文字を、文字列の末尾に持つ場合にマッチする。 \d{4}\z # 文字列の末尾に、少なくとも数字が4つ以上含まれているというバリデーション [ぁ-んァ-ヶ一-龥々ー] UFT-8のUnicodeにおいて、文字列全体が日本語で表記されている場合にマッチする。 irb(main):005:0> full_name = "yamadaハナ子" irb(main):006:0> full_name.match(/\A[ぁ-んァ-ヶ一-龥々ー]+\z/) => nil ?= 直後に設定した文字列でチェックを行い、設定した文字列を含む文字列にマッチする。 irb(main):001:0> name = "マイケルジョーダン" irb(main):002:0> name.match(/マイケル(?=ジョーダン)/) => #<MatchData "マイケル"> irb(main):003:0> name = "マイケルジャクソン" irb(main):004:0> name.match(/マイケル(?=ジョーダン)/) => nil *? チェックした文字列の中で、?の直後に設定した文字が出てきた場合、その段階で1文字目のみを返す。 ?=.*?[\d] # 改行以外のどの文字ともマッチする、数字1文字目を返す。 よくある記述 メールアドレスのバリデーション with_options presence: true do validates :email, format: {with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i, message: "is invalid. Input '@'." } end Rails:メールの正規表現を攻略する! - Qiita パスワードのバリデーション(半角英数字8文字以上) with_options presence: true do validates :email, format: {with: /[a-z\d]{8,}/i, message: "is invalid. Include both letters and numbers." } end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Javascript]ファイルを読み込まない

やりたいこと javascriptのファイルがビューファイルに読み込まれるようにしたい。 エラー内容 items/newがprofit.jsを読み込まない。 ## 調査 ERR_ABORTED=javascriptのファイルが正しいディレクトリに保管されていない。 GET [http://localhost:3000/items/profit.js](http://localhost:3000/items/profit.js) net::ERR_ABORTED 404 (Not Found) →下記の原因が考えられる。 items/newのscript属性の記載が誤っている。 application.jsの直下のディレクトリに配置していない。 ファイル名の記載が間違っている 発火するイベントの記載が間違っている ## 原因 profit.jsを読み込む記述を二重に行っていたため。 application.jsのDOMツリーに、require '../profit'と記述した時点で、自動で読み込まれるようになっている。 →すでに読み込まれているjsファイルを再び読み込もうとしているので、エラーになった。 app/javascript/application.js application.js require("@rails/ujs").start() // require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("../profit"); app/views/items/new.html.erb hew.html.erb <div class="items-sell-contents"> <header class="items-sell-header"> <%= link_to image_tag('furima-logo-color.png' , size: '185x50'), "/" %> <script src="profit.js"></script> 解決策 new.html.erbのscript要素を削除する。 app/views/items/new.html.erb new.html.erb <div class="items-sell-contents"> <header class="items-sell-header"> <%= link_to image_tag('furima-logo-color.png' , size: '185x50'), "/" %> # 削除 <script src="profit.js"></script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] Rspec MySQLを読み込まない

やりたいこと Formオブジェクトの単一テストを成功させたい エラー内容 ActiveRecord::StatementInvalid: Mysql2::Error: MySQL client is not connected 調査 クライアントサイドからサーバーサイドにデータが送られていない。 原因 先に読み込まれるべきデータより前に、後のものを読み込んでしまってエラーが起きている可能性がある。 →処理が重すぎる。 解決策 処理ごとの待機時間を設ける。 処理はsleep(秒数)で定義する。 -app/spec/models/order_address_spec.rb order_address_spec.rb require 'rails_helper' RSpec.describe OrderAddress, type: :model do describe '購入情報の保存' do before do user = FactoryBot.create(:user) item = FactoryBot.create(:item) @order_address = FactoryBot.build(:order_address, user_id: user.id, item_id: item.id) sleep(1) end 参考 【Ruby On Rails】RSpecを用いたテストのエラー MySQL client is not connected - Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]deviseでログインできない

やりたいこと emailとpasswordを入力し、ログインを成功させたい エラー内容 正しくemailとpasswordを入力しているのに、Invalid email or password(メールアドレスかパスワードが異なる)と出てログインできない 調査 form_withメソッドで指定したカラムが間違っている? 例:パスワードを入れるform_withメソッドで、保存先をnicknameカラムで記述していた。 リクエストが失敗している 例:HTTPステータスコードが400代 devise_parameter_sanitizerメソッドで指定してバリデーションに誤りがある。 ストロングパラメーターの指定方法に誤りがある。 例:@を抜くように指定してしまった、passwordの英字が大文字に置換されてしまう ## 調査結果 form_withメソッドで指定したカラムが間違っている? →ビューファイルを確認したところ、カラムの指定は合っていた。 -app/views/devise/registrations/new.html.erb new.html.erb <%= render "shared/second-header"%> <%= form_with model: @user, url: user_registration_path, class: 'registration-main', local: true do |f| %> # 中略 <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">メールアドレス</label> <span class="indispensable">必須</span> </div> <%= f.email_field :email, class:"input-default", id:"email", placeholder:"PC・携帯どちらでも可", autofocus: true %> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">パスワード</label> <span class="indispensable">必須</span> </div> <%= f.password_field :password, class:"input-default", id:"password", placeholder:"6文字以上の半角英数字" %> <p class='info-text'>※英字と数字の両方を含めて設定してください</p> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">パスワード(確認)</label> <span class="indispensable">必須</span> </div> <%= f.password_field :password_confirmation, class:"input-default", id:"password-confirmation", placeholder:"同じパスワードを入力して下さい" %> </div> # 以下略 <% end %> リクエストが失敗している →HTTPステータスコードは200だった →リクエストは正しくできている devise_parameter_sanitizerメソッドで指定してバリデーションに誤りがある。 →誤りはない。  誤りがあったら新規登録の時点でエラーが出るはず。 ストロングパラメーターの指定方法に誤りがある。 →誤りはない。  誤りがあったら新規登録の時点でエラーが出るはず。 ## 原因 sessions/newのform_withメソッドにUserモデルのパラメーターが渡されていなかったため。 →emailとpasswordを正しく入力したとしても、確認する値を持っていないので、何を入力してもエラーになってしまった。 ## 解決策 sessions/newのビューファイルのform_withメソッドに、Userモデルのインスタンス変数を追記。 →入力したパラメーターをUserモデルに渡して、確認することができるようになる。 -app/views/devise/sessions/new.html.erb new.html.erb <%= render "shared/second-header"%> <%= form_with model: @user, url: user_session_path, class: 'registration-main', local: true do |f| %> # @userを渡すことで、値を参照できるようになる。 # 以下略
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]サーバーが立ち上がらない

やりたいこと コードを修正したので、サーバーを再起動したい。 エラー内容 A server is already running. 調査 サーバーがすでに実行中のため、起こるエラー 原因 先に実行していたサーバーが切れていなかったため起こったエラー 解決策 pid番号(プログラム上で現在実行している処理を識別するための番号)を削除する プロセスIDとは - IT用語辞典 - tmp/pids/server.pid server.pid 91024 # これを削除したい ①現在実行中のpid番号を特定 lsof -i:3000 ②実行中のpid番号を削除 kill -9 'pid番号' 参考 rails serverを起動しようとすると "A server is already running."のエラーが表示されるときの対処法 - Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Herokuエラー】Failed to install gems via Bundler. 解決法

本記事は私自身の勉強のインプットとアウトプットを兼ねたものになりますので、分かりにくい点もあるかと思います。 間違っているところがありましたらコメントにて教えてください。 railsチュートリアル第1章のHerokuデプロイ時にエラーが起きたので、誰かの役に立てたらと思いこの記事を書くことにしました。 ローカル環境で学習を進めています。 筆者の環境 Macbook Air(M1, 2020) macOS Big Aur バージョン11.4 ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [arm64-darwin20] Rails 6.1.4.1 Bundler version 2.2.29 heroku/7.59.1 darwin-x64 node-v12.21.0 エラー① Total 6995 (delta 795), reused 0 (delta 0), pack-reused 0 remote: Compressing source files... done. remote: Building source: remote: remote: -----> Building on the Heroku-20 stack remote: -----> Determining which buildpack to use for this app remote: ! Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used. remote: Detected buildpacks: Ruby,Node.js remote: See https://devcenter.heroku.com/articles/buildpacks#buildpack-detect-order remote: -----> Ruby app detected remote: -----> Installing bundler 2.2.21 remote: -----> Removing BUNDLED WITH version in the Gemfile.lock remote: -----> Compiling Ruby/Rails remote: -----> Using Ruby version: ruby-3.0.2 remote: -----> Installing dependencies using bundler 2.2.21 remote: Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4 remote: Your bundle only supports platforms ["arm64-darwin-20"] but your local platform remote: is x86_64-linux. Add the current platform to the lockfile with `bundle lock remote: --add-platform x86_64-linux` and try again. remote: Bundler Output: Your bundle only supports platforms ["arm64-darwin-20"] but your local platform remote: is x86_64-linux. Add the current platform to the lockfile with `bundle lock remote: --add-platform x86_64-linux` and try again. remote: remote: ! remote: ! Failed to install gems via Bundler. remote: ! remote: ! Push rejected, failed to compile Ruby app. remote: remote: ! Push failed remote: Verifying deploy... remote: remote: ! Push rejected to XXX remote: To https://git.heroku.com/XXX ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to XXX このように出てデプロイできませんでした。 内容をみると remote: Your bundle only supports platforms ["arm64-darwin-20"] but your local platform remote: is x86_64-linux. Add the current platform to the lockfile with `bundle lock remote: --add-platform x86_64-linux` and try again. remote: Bundler Output: Your bundle only supports platforms ["arm64-darwin-20"] but your local platform remote: is x86_64-linux. Add the current platform to the lockfile with `bundle lock remote: --add-platform x86_64-linux` and try again. Google翻訳 バンドルはプラットフォーム["arm64-darwin-20"]のみをサポートしますが、ローカルプラットフォームはx86_64-linuxです。 `bundle lock --add-platform x86_64-linux`を使用して現在のプラットフォームをロックファイルに追加し、再試行してください。 Bundlerの出力:バンドルはプラットフォーム["arm64-darwin-20"]のみをサポートしますが、ローカルプラットフォームはx86_64-linuxです。 `bundle lock --add-platform x86_64-linux`を使用して現在のプラットフォームをロックファイルに追加し、再試行してください。 とありました。 言われた通り実行してみます。 % bundle lock --add-platform x86_64-linux そして再度デプロイしてみる… しかし同じエラーが出ました。 さらに調べてみると こちらの記事を発見。 Bundlerのバージョンが2.1.xの時は開発環境がmacOS、デプロイ先のサーバーがLinuxでも設定なしでデプロイができたようですが、 2.2.x以降は設定が必要になるので先程のコマンドと % bundle lock --add-platform ruby を実行する必要があるそうでした。 Gemfile.lock PLATFORMS arm64-darwin-20 ruby x86_64-linux 追加されたか確認します。 エラー② 再度デプロイしてみます。 remote: ! at least twice. One common cause of this behavior is attempting to deploy code from a different branch. 次はこのようなエラーが出てきました。 Google翻訳 リモート:! 少なくとも2回。 この動作の一般的な原因の1つは、別のブランチからコードをデプロイしようとすることです。 このエラーの下にherokuサポートのURLが出てきましたのでそちらをクリックすると、 コミットされていないファイルがあるのにデプロイしようとしているためエラーが出ていたようです。 先程のエラーを解決するために実行したコマンドでGemfile.lockが新たに変更になっていました。 % git status でmasterブランチにいることと変更されたファイルがあることを確認。 % git add -A                                                    #変更された全てのファイルををadd % git commit -m "Update Gemfile.lock" #addしたファイルをコミット % git push #githubにプッシュ で変更されたファイルをコミットしてgithubにプッシュ。 そして再度デプロイ。 エラー③ 成功したようなのでデプロイしたアプリを開くと 何やらエラーっぽいので言われた通りのコマンドを実行してみました。 たくさんのログの中に code=H10 status=503 このような部分を発見。 H10 - App crashed (アプリがクラッシュしました) Web dyno のクラッシュや Web dyno でのブートタイムアウトにより、このエラーが表示されます。 とherokuのエラーコード一覧に書いてあったので検索。 % heroku run console を実行するとエラーが出てきて解決に繋がりそうなログが出てくると言うことを発見したので実行してみました。 Error loading the 'postgresql' Active Record adapter. Missing a gem it depends on? can't activate pg (~> 1.1), already activated pg-0.18.4. Make sure all dependencies are added to Gemfile. (LoadError) Google翻訳 'postgresql' ActiveRecordアダプターのロード中にエラーが発生しました。 依存する宝石がありませんか? pgをアクティブ化できません(〜> 1.1)、すでにpg-0.18.4がアクティブ化されています。 すべての依存関係がGemfileに追加されていることを確認してください。 (LoadError) pgのバージョンがあってないのか〜 と思い、RubyGems.orgでpgと検索。 (今まではgoogle検索で引っかかったRubyGems.orgのpg 0.18.4のコードをコピーしていました。) pg 1.2.3がヒット。 2021年10月現在ではこちらが最新バージョンでした。 めっちゃ前のやつをインストールしていました…情けない! Gemfile gem 'pg', '~> 1.2', '>= 1.2.3' と記述し直して、再度add→commit→push→デプロイ をし直すとデプロイ完了しました! 情けないミスでしたがバージョンの大切さが身に沁みたのでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】class << selfとは

class << selfとは 特異クラス定義式のこと。 クラスメソッドはクラスインスタンスに属する特異メソッドですが、上記の形式でのクラスメソッドの定義は「<<」の後ろにクラスインスタンスを指定し、その「クラスインスタンスの特異クラスを定義している」とみることができる。 https://www.school.ctc-g.co.jp/columns/nakagoshi/nakagoshi12.html クラスメソッドを定義するときに全てのクラスメソッドにself.をつけるのが手間なときに特異クラスを使うと便利。 foo.rb class Foo < ApplicationRecord class << self def bar puts 'bar' end end end foo.rb class Foo < ApplicationRecord def self.bar puts 'bar' end end 上2つのコードは同じ意味になる。 参考 より詳しい特異クラスと特異メソッドの説明は下記参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails ActiveRecordでjsonの値を取り出す

ActiveRecordでjsonの値を取り出す mongoDB query embedded documentsとかを検索ワードにして調べてみました。 https://stackoverflow.com/questions/3954520/mongoid-mongodb-and-querying-embedded-documents https://stackoverflow.com/questions/24749312/mongoid-querying-embedded-docs/24749539 階層化されているjsonデータは.でつなげば取得できそう。 今回やりたかったのはwhereでcptrans内の特定keyの値がnullでない場合のレコードだけを抽出 Networkモデル { _id: 1, cptrans: { _id: ObjectId('61712564a3519d0001fe97aa'), mcc: 440, mnc: 51, tracking_area_code: 41481, cell_id: '68030469', location_area_code: 0, rsrp: -66, rssi: -41, rsrq: -7, rssnr: 0, traffic_channel: 0, band: 18, radio_interface: 'LTE', band_width: '0', } } Networksモデルに上記のようなjsonデータがmongoDBに格納されている ここからwhereでcptrans内の特定keyの値がnullでない場合のレコードだけを抽出したい場合 下記のようになる. 結果 rank_typeに抽出したいkeyをいれてあげる Nerwork.where("cptrans.#{rank_type}" => { '$ne' => nil }) 私はkey毎に抽出を変えたかったのでsocpeを作成し、下記のような感じで対処しました。 scopeって何それって人はこれ参考にするといいかも https://qiita.com/ngron/items/14a39ce62c9d30bf3ac3 scope :rank_type, ->(rank_type) { case rank_type when :rsrp, :rsrq, :rssnr then where('cptrans' => { '$ne' => nil }) when :rssi, :nr_rssi, :nr_rsrp, :nr_rsrq then where("cptrans.#{rank_type}" => { '$ne' => nil }) when :tx_rate then where('iperf' => { '$ne' => nil }) end }  参考 scope https://qiita.com/ngron/items/14a39ce62c9d30bf3ac3 mongoDBのハッシュ値の取得 https://stackoverflow.com/questions/3954520/mongoid-mongodb-and-querying-embedded-documents https://stackoverflow.com/questions/24749312/mongoid-querying-embedded-docs/24749539
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】 ja.ymlについてまとめてみた 【human_attribute_name】【Model.model_name.human】の使い分け!!

概要 ja.ymlについてまとめる 前提 設定してある部分 config/locales/views/ja.yml ja: defaults: submit: '送信' login: 'ログイン' register: '登録' logout: 'ログアウト' users: new: title: 'ユーザー登録' to_login_page: 'ログインページへ' config/locales/activerecord/ja.yml ja: activerecord: models: user: 'ユーザー' attributes: user: email: 'メールアドレス' password: 'パスワード' views/ja.yml config/locales/views/ja.yml(再記) ja: defaults: submit: '送信' login: 'ログイン' register: '登録' logout: 'ログアウト' users: new: title: 'ユーザー登録' to_login_page: 'ログインページへ' 呼び込み方(ターミナルでやってみる) rb(main):002:0> I18n.t 'defaults.login' => "ログイン" こんな感じで使用できる viewで使用すると... views/users/new.html.erb <div class='text-center'> <%= link_to (t '.to_login_pag'), login_path %> </div> こんな感じで使用できる。 t '(ここの部分はusers/new省略できる).to_login_pag' activerecord/ja.yml config/locales/activerecord/ja.yml ja: activerecord: models: user: 'ユーザー' attributes: user: email: 'メールアドレス' password: 'パスワード' こんな感じで使用できる views/user_sessions/new.html.erb <div class="form-group"> <%= f.label :email, User.human_attribute_name(:email) %> <%= f.text_field :email, class: 'form-control'%> </div> human_attribute_name モデルカラムに設定している文字を表示 # t("activerecord.attributes.user.email")と同じ User.human_attribute_name(:email) => "メールアドレス" Model.model_name.human モデルに設定している文字を表示 User.model_name.human => "ユーザー" form_withで使用するとき modelに紐づくform_withのlabelではRailsがよしなに定義を読み込んでくれる!! なので... <%= form_with model: @user, local: true do |f| %> <div class="form-group"> <%= f.label :last_name %> #このままで大丈夫 <%= f.text_field :last_name, class: 'form-control' %> </div> # 省略 application.rbに定義しているコードの意味 今回はこのように定義した。 application.rb config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s] ここの部分について config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s] config.i18n.load_pathの設定でconfig/locales以下のどの階層のディレクトリも読み込ませるようにしておくのがミソ。 こうすることで、ディレクトリを分けることでカテゴリごとにi18nのデータを管理することができる様になる。 lazy lookup記法 この記法を使用すると、controllerでも使用することができるので便利:) 参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rasil ja.ymlについてまとめてみた 【human_attribute_name】【Model.model_name.human】の使い分け!!

概要 ja.ymlについてまとめる 前提 設定してある部分 config/locales/views/ja.yml ja: defaults: submit: '送信' login: 'ログイン' register: '登録' logout: 'ログアウト' users: new: title: 'ユーザー登録' to_login_page: 'ログインページへ' config/locales/activerecord/ja.yml ja: activerecord: models: user: 'ユーザー' attributes: user: email: 'メールアドレス' password: 'パスワード' views/ja.yml config/locales/views/ja.yml(再記) ja: defaults: submit: '送信' login: 'ログイン' register: '登録' logout: 'ログアウト' users: new: title: 'ユーザー登録' to_login_page: 'ログインページへ' 呼び込み方(ターミナルでやってみる) rb(main):002:0> I18n.t 'defaults.login' => "ログイン" こんな感じで使用できる viewで使用すると... views/users/new.html.erb <div class='text-center'> <%= link_to (t '.to_login_pag'), login_path %> </div> こんな感じで使用できる。 t '(ここの部分はusers/new省略できる).to_login_pag' activerecord/ja.yml config/locales/activerecord/ja.yml ja: activerecord: models: user: 'ユーザー' attributes: user: email: 'メールアドレス' password: 'パスワード' こんな感じで使用できる views/user_sessions/new.html.erb <div class="form-group"> <%= f.label :email, User.human_attribute_name(:email) %> <%= f.text_field :email, class: 'form-control'%> </div> human_attribute_name モデルカラムに設定している文字を表示 # t("activerecord.attributes.user.email")と同じ User.human_attribute_name(:email) => "メールアドレス" Model.model_name.human モデルに設定している文字を表示 User.model_name.human => "ユーザー" form_withで使用するとき modelに紐づくform_withのlabelではRailsがよしなに定義を読み込んでくれる!! なので... <%= form_with model: @user, local: true do |f| %> <div class="form-group"> <%= f.label :last_name %> #このままで大丈夫 <%= f.text_field :last_name, class: 'form-control' %> </div> # 省略 application.rbに定義しているコードの意味 今回はこのように定義した。 application.rb config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s] ここの部分について config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s] config.i18n.load_pathの設定でconfig/locales以下のどの階層のディレクトリも読み込ませるようにしておくのがミソ。 こうすることで、ディレクトリを分けることでカテゴリごとにi18nのデータを管理することができる様になる。 lazy lookup記法 この記法を使用すると、controllerでも使用することができるので便利:) 参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第九章 発展的ログイン機構

Remember me機能 ユーザーのログイン状態をブラウザを閉じた後でも有効にする[remember me]機能を実装する。 明示的にログアウトしなければログアウトできない。 ブランチを作成 $ git checkout -b advanced-login 記憶トークンと暗号化 記憶トークンと暗号化、cookiesメソッドによる永続的cookiesの作成や、安全性の高い記憶ダイジェストに トークン認証にこの記憶トークンを活用する。 sessionメソッドで保存した情報は自動的に安全が保たれている。 しかしcookiesメソッドに保存する情報は安全ではない。 盗み出す方法がいくらでもある。 永続的セッションを作成するには 1.記憶トークンにはランダムな文字列を生成して用いる。 2.ブラウザのcookiesにトークンを保存するときには、有効期限を設定する。 3.トークンはハッシュ値に変換してからデータベースに保存する。 4.ブラウザのcookiesに保存するユーザーIDは暗号化しておく。 5.永続ユーザーIDを含むcookiesを受け取ったら、そのIDでデータベースを検索し、記憶トークンのcookiesがデータベース内のハッシュ値と一致することを確認する。 ubuntu:~/environment/sample_app (advanced-login) $ rails generate migration add_remember_digest_to_users remember_digest:string Running via Spring preloader in process 4908 invoke active_record create db/migrate/20211008140754_add_remember_digest_to_users.rb 記憶ダイジェスト用に生成したマイグレーション db/migrate/[timestamp]_add_remember_digest_to_users.rb class AddPasswordDigestToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :password_digest, :string # usersテーブルに # genalateによって生成された絡むとそのデータ型 # カラムとデータ型を追加 end end ランダムな文字列生成 >> SecureRandom.urlsafe_base64 => "ycRDo1SWia84lbXERcV6Bw" >> SecureRandom.urlsafe_base64 => "ncAMbSkaP32MVyNiyPYvZg" >> SecureRandom.urlsafe_base64 => "z-JdrOsqlPQaB2Ukp-PS0w" このメソッドは、A–Z、a–z、0–9、"-"、"_"のいずれかの文字(64種類)からなる長さ22のランダムな文字列を返します。 トークン生成用メソッドを追加する app/models/user.rb class User < ApplicationRecord . . . # ランダムなトークンを返す def User.new_token SecureRandom.urlsafe_base64 # ランダムな文字列を生成 end end rememberメソッドをUserモデルに追加する app/models/user.rb class User < ApplicationRecord . . . # 永続セッションのためにユーザーをデータベースに記憶する def remember self.remember_token = User.new_token # ランダムな文字列がトークンになる。 update_attribute(:remember_digest, User.digest(remember_token)) # remember_digestカラムにランダムな文字列を代入する end end 演習 1. コンソールを開き、データベースにある最初のユーザーを変数userに代入してください。その後、そのuserオブジェクトからrememberメソッドがうまく動くかどうか確認してみましょう。また、remember_tokenとremember_digestの違いも確認してみてください。 >> user = User.first (1.5ms) SELECT sqlite_version(*) User Load (2.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "a", email: "abc@def.com", created_at: "2021-10-05 04:11:41", updated_at: "2021-10-05 04:11:41", password_digest: [FILTERED], remember_digest: nil> >> user.remember (0.1ms) begin transaction User Update (5.4ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2021-10-09 03:47:02.951086"], ["remember_digest", "$2a$12$8L6UrQUhfo/Fru50d81U5.LY3pIkuWZB2Ho1WhfXTUrkVGSZwEhYi"], ["id", 1]] (6.9ms) commit transaction => true >> user.remember_token => "ACeVmrqEjrFZOM8IsV6GNA" >> user.remember_digest => "$2a$12$8L6UrQUhfo/Fru50d81U5.LY3pIkuWZB2Ho1WhfXTUrkVGSZwEhYi" 2. リスト 9.3では、明示的にUserクラスを呼び出すことで、新しいトークンやダイジェスト用のクラスメソッドを定義しました。実際、User.new_tokenやUser.digestを使って呼び出せるようになったので、おそらく最も明確なクラスメソッドの定義方法であると言えるでしょう。しかし実は、より「Ruby的に正しい」クラスメソッドの定義方法が2通りあります。1つはややわかりにくく、もう1つは非常に混乱するでしょう。テストスイートを実行して、ややわかりにくいリスト 9.4の実装でも、非常に混乱しやすいリスト 9.5の実装でも、いずれも正しく動くことを確認してみてください。ヒント: selfは、通常の文脈ではUser「モデル」、つまりユーザーオブジェクトのインスタンスを指しますが、リスト 9.4やリスト 9.5の文脈では、selfはUser「クラス」を指すことにご注意ください。わかりにくさの原因の一部はこの点にあります。 user.rb . . . def self.digest(string) # 渡された文字列のハッシュ値を返す cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost BCrypt::Password.create(string, cost: cost) # fixture用のパスワードを作成します # stringはハッシュ化する文字列 # costはコストパラメータと呼ばれる値 # ハッシュを算出するための計算コストを指定 end # ランダムなトークンを返す def self.new_token SecureRandom.urlsafe_base64 # ランダムな文字列を生成 end . . . テスト ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 5946 Started with run options --seed 45212 24/24: [============================] 100% Time: 00:00:03, Time: 00:00:03 Finished in 3.46418s 24 tests, 61 assertions, 0 failures, 0 errors, 0 skips . . . class << self # selfはユーザーオブジェクトを指す # 何かしら代入、継承などをしている。 # なので省略できる def digest(string) # 渡された文字列のハッシュ値を返す cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost BCrypt::Password.create(string, cost: cost) # fixture用のパスワードを作成します # stringはハッシュ化する文字列 # costはコストパラメータと呼ばれる値 # ハッシュを算出するための計算コストを指定 end # ランダムなトークンを返す def new_token SecureRandom.urlsafe_base64 # ランダムな文字列を生成 end end . . . ubuntu:~/environment/sample_app (advanced-login) $ rails t Running via Spring preloader in process 6604 Started with run options --seed 45054 24/24: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.20782s 24 tests, 61 assertions, 0 failures, 0 errors, 0 skips 確認 成功
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第八章 ログアウト

ログアウト ログアウト機能を追加する。 ユーザーが明示的にログアウトするまではログイン状態を保他なくてはならない。 ユーザーセッションを破棄するための有効なアクションをコントローラで作成。 log_outメソッド app/helpers/sessions_helper.rb module SessionsHelper . . . # 現在のユーザーをログアウトする def log_out session.delete(:user_id) # ユーザーセッションを削除 @current_user = nil # セキュリティ上nilの設定する。 # 本来ならこれはしなくて良い。 end end セッションを破棄する(ユーザーのログアウト) app/controllers/sessions_controller.rb class SessionsController < ApplicationController . . . def destroy # ユーザー削除 log_out # log_outメソッドを発動 redirect_to root_url # ホーム画面に戻る end end ユーザーログアウトのテスト(無効なログインテストも1箇所改良) test/integration/users_login_test.rb require 'test_helper' class UsersLoginTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) # ハッシュで呼び出す。 # なんでもハッシュで呼び出せるようになるのか? end test "login with valid email/invalid password" do get login_path assert_template 'sessions/new' post login_path, params: { session: { email: @user.email, password: "invalid" } } assert_not is_logged_in? assert_template 'sessions/new' assert_not flash.empty? get root_path assert flash.empty? end test "login with valid information followed by logout" do get login_path # ログイン画面にいく post login_path, params: { session: { email: @user.email, password: 'password' } } # データをデータベースに投稿 assert is_logged_in? # ログインされているよね? assert_redirected_to @user # ユーザー情報を取り出すのか? follow_redirect! # もう一回確認 assert_template 'users/show' # ユーザーページに移動する assert_select "a[href=?]", login_path, count: 0 assert_select "a[href=?]", logout_path assert_select "a[href=?]", user_path(@user) delete logout_path assert_not is_logged_in? assert_redirected_to root_url follow_redirect! assert_select "a[href=?]", login_path # これは表示する assert_select "a[href=?]", logout_path, count: 0 assert_select "a[href=?]", user_path(@user), count: 0 # 上2行は非表示になっていることを確認。 end end ubuntu:~/environment/sample_app (basic-login) $ rails t Running via Spring preloader in process 9774 Started with run options --seed 65113 24/24: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.15159s 24 tests, 61 assertions, 0 failures, 0 errors, 0 skips 演習 1. ブラウザから[Log out]リンクをクリックし、どんな変化が起こるか確認してみましょう。また、リスト 8.35で定義した3つのステップを実行してみて、うまく動いているかどうか確認してみましょう 確認 2.cookiesの内容を調べてみて、ログアウト後にはsessionが正常に削除されていることを確認してみましょう。 どうしてもcookiesが消えない。 みている場所が違うのか。 わからない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第八章 ユーザー登録時のログイン

ユーザー登録時のログイン ユーザー登録中にログインを済ませておくことにします。 ユーザー登録中にログインする app/controllers/users_controller.rb class UsersController < ApplicationController def show @user = User.find(params[:id]) # データベースからユーザー情報を取り出す end def new @user = User.new #新しくユーザーオプジェクトを作成する # オブジェクトの属性をつける end def create @user = User.new(user_params) # 外部メソッドを使う if @user.save # 保存の成功をここで扱う。 log_in @user # 登録後に自動ログインするため flash[:success] = "Welcome to the Sample App!" # 成功時一度だけ表示されるメッセージ redirect_to @user # urlを指定して表示する # 保存成功したらurlを表示する else render 'new' # 保存に成功しなければnewアクションに移動する # 失敗したらまた戻る end end private #外部から使えないようにする def user_params # Usersコントローラの内部でのみ実行される # Web経由で外部ユーザーにさらされない params.require(:user).permit(:name, :email, :password, :password_confirmation) end end テスト中のログインステータスを論理値で返すメソッド test/test_helper.rb ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' require 'rails/test_help' require "minitest/reporters" Minitest::Reporters.use! class ActiveSupport::TestCase # Run tests in parallel with specified workers parallelize(workers: :number_of_processors) # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all include ApplicationHelper # Add more helper methods to be used by all tests here... def is_logged_in? !session[:user_id].nil? # ログイン中であるかを確認 # trueを返す end end ユーザー登録後のログインのテスト test/integration/users_signup_test.rb require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do get signup_path # signupのページにアクセス assert_no_difference 'User.count' do # ユーザー数が変わらないかをテストする post users_path, params: { user: { name: "", # ハッシュのハッシュのようになっているように見える email: "user@invalid", password: "foo", password_confirmation: "bar" } } # postリクエストを送信 フォーム送信をテストする # データの投稿した後 assert_no_difference で違いを比べるらしい。 end assert_template 'users/new' end test "valid signup information" do get signup_path assert_difference 'User.count', 1 do # # 第二引数でデータベースとの差異は1である。 post users_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password" } } end follow_redirect! # POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッド assert_template 'users/show' # showアクションのビューを表示させる。 assert is_logged_in? # ログイン中であるかを確認 end end ubuntu:~/environment/sample_app (basic-login) $ rails t Running via Spring preloader in process 6231 Started with run options --seed 6084 24/24: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.63192s 24 tests, 54 assertions, 0 failures, 0 errors, 0 skips 演習 1. リスト 8.29のlog_inの行をコメントアウトすると、テストスイートは red になるでしょうか? それとも green になるでしょうか? 確認してみましょう。 ubuntu:~/environment/sample_app (basic-login) $ rails t Running via Spring preloader in process 6664 Started with run options --seed 61607 FAIL["test_valid_signup_information", #<Minitest::Reporters::Suite:0x00007fa7153721e8 @name="UsersSignupTest">, 2.3977448109999386] test_valid_signup_information#UsersSignupTest (2.40s) Expected false to be truthy. test/integration/users_signup_test.rb:34:in `block in <class:UsersSignupTest>' 24/24: [============================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.83391s 24 tests, 54 assertions, 1 failures, 0 errors, 0 skips ログインされていないからエラーになったらしい。 2. 現在使っているテキストエディタの機能を使って、リスト 8.29をまとめてコメントアウトできないか調べてみましょう。また、コメントアウトの前後でテストスイートを実行し、コメントアウトすると red に、コメントアウトを元に戻すと green になることを確認してみましょう。ヒント: コメントアウト後にファイルを保存することを忘れないようにしましょう。また、テキストエディタのコメントアウト機能については『テキストエディタ編』の 「コメントアウト機能」などを参照してみてください。 ファイルの全部をコメントアウトして確認。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む