20210916のRailsに関する記事は19件です。

学習メモ(9/16)

学習内容 HTML/CSSコーディング練習 Railsアプリ開発2周目突入(rails new) 学んだこと HTML/CSSコーディング練習 flex関連 flex, flex-wrap, justify-content, aligin-itemsは親要素に付ける 複数の要素を横並びにしたいときは親要素にdisplay: flex;を指定する 要素の間隔を広げたい場合はgap: ○○px;で指定すれば簡単 また、画像とテキストを横並びにするときは子要素の両方にwidth: 100%;を付ける ボタン関連 ボタンを中央寄せしたいときはdisplay: block;とmargin: auto;を指定する display: block;を付けてブロック要素に変更 paddingで余白をとる cursor: pointer;と擬似要素でopacityを指定すれば、よりボタンっぽくなる ボタンの原型クラスと色付けクラスで分けると汎用性アップ inputタグ関連 inputタグを中央寄せするときはwidthで横幅を指定してからmargin: auto;を付ける border-radius: 1000px;を指定すると角が丸くなる footer関連 footerはcopyrightにクラスを指定してtext-alignを指定すれば任意の方向に動かせる Railsアプリ開発2周目突入(rails new) バージョンを指定しながらnewをするには以下を実行する rails _6.0.4.1_ new 任意名 newしてからsourcetreeを用いたGitの管理方法 プロジェクトを作成 rails new 任意名 ディレクトリをVSCodeで開く code . sourcetreeを開いて「メニューバー→開く」からディレクトリを参照 全てのファイルを選択してコミット initial commit GitHubで新規リポジトリを作成「プロジェクト名→ディスクリプション→create」 リポジトリのSSHをコピー sourcetreeを開き「設定→リモート→追加」から、リモートの名前:origin・URL/パス:コピーしたSSHとする mainブランチを右クリックしてoriginにプッシュ GitHubを開いてリポジトリにRailsのファイル群が追加されていれば完了
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】初めての模写コーディングで役立つ豆知識・学習メモ(9/16)

学習内容 HTML/CSSコーディング練習 Railsアプリ開発2周目突入(rails new) 学んだこと HTML/CSSコーディング練習 flex関連 flex, flex-wrap, justify-content, aligin-itemsは親要素に付ける 複数の要素を横並びにしたいときは親要素にdisplay: flex;を指定する 要素の間隔を広げたい場合はgap: ○○px;で指定すれば簡単 また、画像とテキストを横並びにするときは子要素の両方にwidth: 100%;を付ける ボタン関連 ボタンを中央寄せしたいときはdisplay: block;とmargin: auto;を指定する display: block;を付けてブロック要素に変更 paddingで余白をとる cursor: pointer;と擬似要素でopacityを指定すれば、よりボタンっぽくなる ボタンの原型クラスと色付けクラスで分けると汎用性アップ inputタグ関連 inputタグを中央寄せするときはwidthで横幅を指定してからmargin: auto;を付ける border-radius: 1000px;を指定すると角が丸くなる footer関連 footerはcopyrightにクラスを指定してtext-alignを指定すれば任意の方向に動かせる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]Gemを使ってモーダル作成

はじめに 本記事では、Gemを使ったモーダルの作成方法を記述します。 参考動画 ブックマークしているサイトがもろ見えになるので、上は切りました。 「決して」やましいものはありません。 流れ gemのインストールです。 gem 'data-confirm-modal' からの bundle install からの application.js // = require data-confirm-modal コメントアウトでOK <%= link_to food_path(food.id), method: :delete, data: { confirm: '本当に削除しますか?削除すると投稿の復元はできません', cancel: 'やめる', commit: '削除する'}, title: '削除確認' do %> <i class="fas fa-trash-alt"></i> <% end %> dataは{}、titleは{}には入れないようにそれぞれ注意ですね。 検索ボタン押したら検索フォームがブワッと出てくるような おしゃれなモーダルを作成する場合は、CSSとjQueryを使ったモーダルでいいと多いますが、 今回のような削除は、このGem使った方が、 え?ほんとに削除するの?感が出るので、私はこっちが好きです。 適材適所ってやつですかね。 難しいと思ったら思いの外簡単にできたので、覚えておきたいところです。 以上です。 終わりに 色々なGemがあるんですね。 もっといろんなGemを知ってアプリケーションを良くする武器を揃えたいです。 以下参考サイトです。 Ruby on Railsで削除ボタンを押したときに確認ダイアログを表示させる方法 data: {confirm: }の確認ダイアログをいい感じにする【Ruby on Rails】 明日も頑張ります!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

知っておきたいincludesの正体【Rails】

rails 6.0.2 ruby 2.7.1 includesとは N+1問題を起こさないように、関連テーブルのデータをキャッシュしてくれるメソッド。 しかし、関連テーブルのデータをキャッシュするといっても 一体どのようにして、データを取得してきているのだろうか。 実はincludesは、モデルの関連など見て以下のどちらかの最適とrailsが判断したメソッドを実行している。 (このrailsの判断が正しいとは限らない。*後述) preload eager_load 関連ごとにSQLを発行してキャッシュ。  left_joinを用いて、キャッシュ。 preload、eager_loadがどの様なSQLを発行してるか見ていく。 以下の様なモデルがあるとする。 user.rb class User < ApplicationRecord belong_to :country #国 has_many :posts #投稿 end preload preloadは、関連ごとにSQLを発行してキャッシュする。 いったいどういうことかというと、以下のようなrubyコードでは User.all.preload(:posts,:country) 以下のようなsqlが発行される。  --user.allのsql select * from users; --postsのキャッシュのsql in句は、user.id select * from posts where user_id in (1,2,3,4,5,6.....); --countryのキャッシュのsql in句は、user.county_id select * from countries where id in (1,2,3,4,5,6.....); eager_load left_joinを用いて、キャッシュ。 いったいどういうことかというと、以下のようなrubyコードでは User.all.eager_load(:posts,:country) 以下のようなsqlが発行される。  --user.allのsql兼、postsのキャッシュ兼、countryのキャッシュ select * from users u left join posts p on p.user_id = u.id left join countries c c.id = u.countries ; 以上の様にsqlが発行される訳だが、 railsのincludesがpreload,eager_loadの使い分けを完璧にできる訳ではない。 例えば、以下のように複数のテーブルをキャッシュしたい時、 countryだけ、preloadで他は全てeager_loadみたいなことは出来ない。 全部preload or 全部eager_load のどちらかである。  User.all.includes(posts: [:comments],:country,:profies) preloadでキャッシャしたらまずいところ eager_loadでキャッシュしたらまずいところがそれぞれあり、 railsでも使い分けをしている訳だが、 このようなことが起きると、意図せぬところでパフォーマンスの低下が起きてしまう。 したがって、各メリットデメリットを理解しておく必要がある。 メリットデメリット preload メリット 1.メモリの圧迫を防げる。  例えば、中間テーブルを持つN対Nの関係のテーブルをeaager_loadを使ってキャッシュした場合。  その実行結果のレコード数は単純に取得したかったデータ量よりもはるかに多くなってしまい、メモリを圧迫してしまうこ とになる。   デメリット  ① IN句が大きくなりすぎる場合がある。  例えば、例で用いたrubyコードのUser.allが1万件あった場合、in句が膨大な長さになり、  以下の様なことが起きる。  ・ ネットワークI/Oを圧迫する。  ・ rdbmsによってin句に指定できる数には限界がある、Oracleは、1000個らしい。 参考 https://oreno-it.info/archives/816 ② 関連先テーブルを使って絞り込みなどができない。 別のsqlでキャッシュをしてるので、関連テーブルのカラムをwhereで使うことはできない。 つまり以下の様なrubyコードはエラーになる。 irb(main)> User.preload(:country).where(countries: {name: '日本'}) ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "members") LINE 1: SELECT "users".* FROM "users" WHERE "members"."name" = $1 LI... eager_load メリット ① 関連先テーブルを使って絞り込みなどが出来る。 以下が実行可能。 irb(main)> User.eager_load(:country).where(countries: {name: '日本'}) ② 一対一,N対一の関連テーブルを取得するのに良い。  ・一対一,N対一の関連の場合、結合の結果のレコードが増えるわけではないので、   結合によるメモリの圧迫は少ない。  ・sqlの発行回数が少ない。(通信回数、rdbmsの構文解析などの時間が減る。) デメリット ① 関連がN対ー,N対Nの場合のパフォーマンス left_joinなので無駄なデータを沢山とってきてしまい、パフォーマンスは下がる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerの開発環境から踏み台サーバー経由でdbに接続 [ Rails ]

目的 開発マシン上のdockerで動作するrailsアプリケーションのdb接続を 踏み台サーバー経由でrdsに接続したい。 方法 前提 ◦踏み台サーバー ・エンドポイント ip: 12.12.12.12 ・pemファイル /Users/hoge/.ssh/hoge.pem ・接続ユーザー root ◦dbサーバー ・エンドポイント private domain: hogehoge.internal (踏み台から見て) ・postgreSQL(portは、5432) ① macターミナルから以下コマンドを実行、SSHポートフォーワーディングを行う。 ssh -N -L 5434:hogehoge.internal:5432 -i /Users/hoge/.ssh/hoge.pem -p 22 root@12.12.12.12 ② database.ymlの行き先をmacの5434に設定。 database.yml host: host.docker.internal port: 5434 注) docker上で動くrailsアプリの場合なので、 localhost でなく host.docker.internal で指定 ③ rails サーバーを立ち上げる 正しくdbに接続されてることを確認。  sshポートフォーワーディングとは sshポートフォーワーディングとは、sshコマンドのポート転送機能のこと。 sshコマンドは通常以下のように使用されるが、 ssh 10.10.10.10 ここに-L オプションをつけるとポートの転送先を設定できる。 例えば、 ssh -L ポート番号1:10.10.10.11:ポート番号2 10.10.10.10 これを実行すると、  localhost:ポート番号1への接続が、10.10.10.10を経由して 10.10.10.11:ポート番号2に転送されるようになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル 第五章 html 

レイアウトを確認する この章では、アプリケーションにBootstrapフレームワークを組み込み、 、カスタムスタイルを追加。 これまで作成したページ(HomeやAboutなど)へのリンクをレイアウトに追加。 パーシャル Railsのルーティング Asset Pipeline Sassを学習する。 章の最後に、ユーザーをサイトにログインさせる サンプルアプリケーションにレイアウトを追加、修正の部分に注力 本章ではテキストエディタによる修正とブラウザによる確認がほとんど。 最後に、新しいテスト手法「統合テスト(Integration Test)」について紹介 統合テストを使って、最終的なレイアウトやリンクが正しいかどうかをチェック。 構造を追加する レイアウトにいくつかの構造とCSSを与えて、最小限のスタイルを追加。 カスタムCSSルールの他に、Twitter社によるオープンソースのWebデザインフレームワークとして公開しているBootstrapも利用します。また、コードそのものにもスタイルを与えます。 つまり、散らかり始めたレイアウトのコードを、パーシャル(Partial)機能を使って整えていく Webアプリケーションを作成するときに、ユーザーインターフェイスの概要を知っておく。 そこで本書では、モックアップ(Webの文脈ではよくワイヤーフレームと呼ばれます)を使っていく。 サイトロゴ、ナビゲーションヘッダー、サイトフッターを含む静的ページを開 ナビゲーション ンプルアプリケーションにリンクとスタイルを追加するために、サイトのレイアウトファイルapplication.html.erb(リスト 4.3)にHTML構造を追加し、レイアウトファイルを更新 構造を追加したWebサイトのレイアウト <!DOCTYPE html> <html> <head> <title><%= full_title(yield(:title)) %></title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> <!--[if lt IE 9]> <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js"> </script> <![endif]--> </head> <body> <header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <%= link_to "sample app", '#', id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", '#' %></li> <li><%= link_to "Help", '#' %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> </div> </header> <div class="container"> <%= yield %> </div> </body> </html> <!--[if lt IE 9]> <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js"> </script> <![endif]--> [if lt IE 9]は、Railsの一部ではありません。 条件付きコメントと呼ばれる。 今回のような状況のためにInternet Explorerで特別にサポートされている。 これによりFirefox、Chrome、Safariなどの他のブラウザに影響を与えずに、IEのバージョンが9未満の場合にのみHTML5 shimを読み込めるため、非常に好都合です <header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <%= link_to "sample app", '#', id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", '#' %></li> <li><%= link_to "Help", '#' %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> </div> </header> headerタグは、ページの上部に来るべき要素を表す。 このheaderタグには、 navbar、 navbar-fixed-top、 navbar-inverseという3つのCSSクラスがスペース区切りで与えられています class="container" id: "logo" すべてのHTML要素には、クラスとid 6 の両方を指定することができます CSSでスタイルを指定するときに便利です クラスとidの主な違いは、クラスはページ内で何度でも使えるのに対し、idは一度しか使えない点 divタグは一般的な表示領域を表し、要素を別々のパーツに分けるときに使われます。 divタグはサイト内のよく使われる領域ごとに細分化 header要素、nav要素、section要素が新たに使えるようになりました。 <%= link_to "sample app", '#', id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", '#' %></li> <li><%= link_to "Help", '#' %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> リンクを生成するために、Railsヘルパーのlink_toを使います link_toの第1引数はリンクテキスト、第2引数はURLです。このURLは名前付きルート(Named Routes) 今はWebデザインで一般に使われるスタブ(=一種のダミー)用のURLである「'#'」を置いておきます <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", '#' %></li> <li><%= link_to "Help", '#' %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> navタグには「その内側がナビゲーションリンクである」という意図 ulタグに付与されているnavやnavbar-nav、navbar-rightクラスもBootstrapにおいて特別な意味を持ちます Railsが埋め込みRubyを評価し、レイアウトを描画すると、上のリストは次のように置き換わりま <nav> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Home</a></li> <li><a href="#">Help</a></li> <li><a href="#">Log in</a></li> </ul> </nav> <div class="container"> <%= yield %> </div> ```yield```メソッドはWebサイトのレイアウトにページごとの内容を挿入 #####サインアップページへのリンクがあるHomeページ ```rb <div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", '#', class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"), "https://rubyonrails.org/" %> link_toで次のような仮のリンクを生成します。 <a href="#" class="btn btn-lg btn-primary">Sign up now!</a> 上で挙げた divタグのCSSクラスjumbotronや、 signupボタンのbtnクラス、 btn-lgクラス、 btn-primaryクラスはすべて、Bootstrapにおいて特別な意味を持ちます 2番目のlink_toでは、引数として画像ファイルのパスと任意のオプションハッシュをとるimage_tagヘルパーの能力が示されています。 このヘルパーでは、シンボルを使ってalt属性やwidth属性などを設定できます。 一方で画像を表示するためには、rails.svgというRailsのロゴ画像ファイルを加える必要があります。 <img alt="Rails logo" width="200px" src="/assets/rails-<long string>.svg"> src属性には "images" というディレクトリ名が含まれていないことにも注目してください assetsディレクトリ内の他のディレクトリも同様です これは高速化のための仕組み Railsはassetsディレクトリ直下の画像をapp/assets/imagesディレクトリにある画像と紐付けています alt属性は、画像がない場合に代わりに表示される文字列 演習 1.画像をダウンロード 2.mvコマンドを使い、適切なアセットディレクトリに移動 ファイル名と移動先を書く mv kitten.jpg app/assets/images 3.image_tagを使って、kitten.jpg画像を表示 <%= link_to image_tag("kitten.jpg") %> 困ったこと ブランチを作成できない。 ubuntu:~/environment/sample_app (master) $ git checkout -b filling-in-layoutgit checkout -b filling-in-layout fatal: 'checkout' is not a commit and a branch 'filling-in-layout' cannot be created from it ubuntu:~/environment/sample_app (master) $ git fetch --prune Password for 'https://***@github.com/***/sample_app.git': remote: Invalid username or password. fatal: Authentication failed for 'https://***@github.com/***/sample_app.git/' ubuntu:~/environment/sample_app (master) $ git checkout -b filling-in-layoutgit checkout -b filling-in-layout fatal: 'checkout' is not a commit and a branch 'filling-in-layout' cannot be created from it ubuntu:~/environment/sample_app (master) $ git fetch Password for 'https://***@github.com/***/sample_app.git': remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information. fatal: Authentication failed for 'https://***@github.com/***/sample_app.git/' ubuntu:~/environment/sample_app (master) $ git branch -a * master rails-flavored-ruby static-pages remotes/heroku/master remotes/origin/master remotes/origin/static-pages ubuntu:~/environment/sample_app (master) $ git checkout -b filling-in-layoutgit checkout -b filling-in-layout fatal: 'checkout' is not a commit and a branch 'filling-in-layout' cannot be created from it ubuntu:~/environment/sample_app (master) $ git checkout -b filling-in-layout Switched to a new branch 'filling-in-layout' インターネットで書いてあることを行ってみたがダメだった。 ダメもとで同じことをしてみるとなぜか成功した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】同名のパスのviewで表示するインスタンスを使い分ける

rails初学者によるポートフォリオ作成中の学びのアウトプットです。 実現したい事 クイズアプリを作成中に複数のモードを全てquestionモデルとanswerモデルで賄いたい(ジャンルAのクイズ集とジャンルBのクイズ集を実装する)。下記の二つのページのようにnew_answer_pathからアクセスした時に異なるジャンルの問題が表示されるようにしたい。 (モードA) ジャンルAの問題を表示 回答欄(answerモデルに保存される) (モードB) ジャンルBの問題を表示 回答欄 実装の流れ ①questionモデルにmode_numカラム(integer)を追加して問題のジャンルを番号で識別するようにする。 ②new_answer_path(mode_num: <識別番号>)とし、newアクションにmode_numを渡すようにする。 ③newアクションで渡された識別番号からquestionモデルを検索するよう実装する。 実際のコード (②のステップから) 問題ジャンルAはquestion.mode_numを1に設定。 問題ジャンルBはquestion.mode_numを2に設定。 home.html.erb <%= link_to "問題を解く", new_answer_path(mode_num: 1) %> . . . <%= link_to "問題を解く", new_answer_path(mode_num: 2) %> answer_controller.rb def new @questions = Question.where(mode_num: params[:mode_num]) @question = @questions.find(@questions.pluck(:id).sample ) @answer = @question.answers.new end @questions = Question.where(mode_num: params[:mode_num])で受け取ったmode_numから該当するジャンルのquestionを配列で取得。 @question = @questions.find(@questions.pluck(:id).sample )で 取得した配列の中からランダムで一つのquestionを取得してそれをviewに表示する。 new.html.erb <%= @question.content %> <%= form_with(model: @answer, local: true) do |f| %> <%= f.text_area :content %> <%= f.submit "次の問題へ進む" %> <% end %> これでhome.html.erbの上のリンクと下のリンクからそれぞれアクセスした際、 <%= @question.content %>に違う内容が表示される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

行単位でRubocopを無効にする方法

行ごとに無効にする場合 行末にコメントアウトで# rubocop:disable ルール名を記述する。 例. ~処理~ # rubocop:disable Rails/OutputSafety 複数行にまたがって無効にする場合 ルールを無効にしたい処理の前後を以下の様に囲む。 例. # rubocop:disable Rails/OutputSafety ~処理~ # rubocop:enable Rails/OutputSafety
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザーごとの投稿一覧を表示させたい

患者(user)ごとの記録一覧ostomy(index)を医療者(admin)が見れるようにしたいが、nullデータが持ってこれない Staff/Ostomies.controller.rb class Staff::OstomiesController < ApplicationController before_action :authenticate_staff! def index #患者さんごとのindex @ostomies = Ostomy.where(patient_id: params[:patient_id]) end routes.rb namespace :staff do #医療者のコントローラー内 : resources :ostomies,:only => [:show],:index] do  #患者さんが書いた記録 resources :comments, only: [:create, :destroy] #記録へのコメント resource :favorites, only: [:create, :destroy]  #記録へのいいね end resources :patients,:except => [:destroy,:new,:create] do resources :records,:except => [:destroy] #患者ごとに記録するためネストさせる end end idをurlにぶち込むことでid指定して持ってこれるようになる   参照 routes.rb namespace :staff do get '/ostomies/index/:patient_id', to: 'ostomies#index', as: 'ostomy_index' get '/ostomies/show/:ostomy_id/:patient_id',to: 'ostomies#show', as: 'ostomy_show' : end staff_ostomy_index GET /staff/ostomies/index/:patient_id(.:format) staff/ostomies#index staff_ostomy_show GET /staff/ostomies/show/:ostomy_id/:patient_id(.:format) staff/ostomies#show
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails の Custom Validator を RSpec で柔軟かつ簡単にテストする方法

はじめに カスタムバリデータのテストを RSpec で記述するにあたり、いくつかの技術記事を参考にしたが、どれも一部情報が欠けているものばかりだった。 そこで、カスタムバリデータのテストの書き方について、個人的につまずいた部分を解消したものをまとめておく。 目標 以下のようなカスタムバリデータがあり、そのテストコードを RSpec で記述できるようにする。 app/validators/inclusion_in_array_validator.rb # frozen_string_literal: true # 配列の中身に特定の値が含まれている、または、特定の値の範囲内かを検証するカスタムバリデーション class InclusionInArrayValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return if value.all? { |v| options[:in].include?(v) } record.errors.add(attribute, options[:message] || :inclusion_in_array) end end ちなみに、使い方としては、以下のようなものを想定している。 validates :prefecture, inclusion_in_array: { in: (1..47) } 都道府県を複数選択し、[1, 13, 27, 47] のような配列が送信されたとき、各々の数値が都道府県 ID の範囲内に含まれるか検証するカスタムバリデータである。 CustomValidatorHelper の作成 カスタムバリデータを単体でテストするためには、ダミーモデルを生成し、それを呼び出す。 いくつかのカスタムバリデータを実装している場合は、それぞれに全く同じダミーモデル生成の処理を書くのは DRY ではない。 そこで、CustomValidatorHelper というヘルパーを作り、そこにダミーモデルを生成する処理を書く。 spec/support/helper/custom_validator_helper.rb というファイルを生成し、以下のコードを書く。 spec/support/helper/custom_validator_helper.rb # frozen_string_literal: true # カスタムバリデータを簡単にテストできるようにするためのモジュール module CustomValidatorHelper def build_validator_mock(attribute: nil, record: nil, validator: nil, options: nil) record ||= :record attribute ||= :attribute validator ||= self.described_class.to_s.underscore.gsub(/_validator\Z/, '').to_sym options ||= true Struct.new(attribute, record, keyword_init: true) do include ActiveModel::Validations def self.name 'DummyModel' end validates attribute, validator => options end end end RSpec.configure do |config| config.include CustomValidatorHelper, type: :model end CustomValidatorHelper をロード CustomValidatorHelper を用意しただけでは、各 spec ファイルで読み込んでくれない。そのため、全 spec 内で CustomValidatorHelper が自動的に読み込まれるようにする。 spec/rails_helper.rb に以下のコードを追加する。 spec/rails_helper.rb RSpec.configure do |config| Dir[Rails.root.join('spec/support/config/*.rb')].each { |f| require f } Dir[Rails.root.join('spec/support/helper/*.rb')].each { |f| require f } end RSpec.configure do |config| ... end はすでに書かれていると思うので、そのブロック内の任意の箇所 (末尾など) に中身を記述すれば良い。 使い方 目標 の項で提示した InclusionInArrayValidator のテストを以下に示す。 spec/validators/inclusion_in_array_validator_spec.rb # frozen_string_literal: true require 'rails_helper' RSpec.describe InclusionInArrayValidator, type: :model do describe '#validate_each' do # valid? または invalid? を呼び出さないとエラーメッセージが取得できないので before do mock.valid? end let(:mock) { build_validator_mock(options: options).new(attribute: value) } # 例として都道府県を想定 (1 〜 47 まで) context '正常な場合' do context '範囲内の整数を複数指定した場合' do let(:value) { [1, 13, 27, 47] } let(:options) { { in: (1..47) } } specify 'バリデーションを通過すること' do expect(mock).to be_valid end end end context '異常な場合' do context '0 を指定した場合' do let(:value) { [0] } let(:options) { { in: (1..47) } } specify 'バリデーションを通過しないこと' do expect(mock).to be_invalid end specify 'エラーメッセージが表示されること' do expect(mock.errors.added?(:attribute, :inclusion_in_array)).to be_truthy end end context '負の数を指定した場合' do let(:value) { [-5] } let(:options) { { in: (1..47) } } specify 'バリデーションを通過しないこと' do expect(mock).to be_invalid end specify 'エラーメッセージが表示されること' do expect(mock.errors.added?(:attribute, :inclusion_in_array)).to be_truthy end end end end end 注目すべき点は以下の 1 行だけ。 let(:mock) { build_validator_mock(options: options).new(attribute: value) } 説明のわかりやすさのため、options と value に適当な値を入れたものを以下に示す。 let(:mock) { build_validator_mock(options: { in: (1..47) }).new(attribute: [1, 13, 27, 47]) } 先ほど作成した CustomValidatorHelper の build_validator_mock メソッドを呼び出している。 このメソッドを呼び出すことにより、アプリケーション内で以下のようにバリデータを呼び出したことになる。 validates :attribute, inclusion_in_array: { in: (1..47) } attribute や inclusion_in_array がどこから出てきたのか不思議に思うかもしれないが、これは build_validator_mock メソッド内で暗黙的に値を設定しているためである。 InclusionInArrayValidator のテストを行いたいため、validates に渡す第 2 引数のキーの名前は必然的に inclusion_in_array となる。それを build_validator_mock メソッドで暗黙的に行っている。 これらを明示的に指定したい場合は、以下のようにする。 let(:mock) do build_validator_mock(attribute: :prefecture, validator: :inclusion_in_array, options: { in: (1..47) }). new(prefecture: [1, 13, 27, 47]) end attribute の値を :prefecture に変えたので、attribute: [1, 13, 27, 47] の部分が prefecture: [1, 13, 27, 47] になったことに注意すること。 すると、以下のようにバリデータを呼び出したことになる。 validates :prefecture, inclusion_in_array: { in: (1..47) } 基本的に、カスタムバリデータ単体のテストを書くときは、属性名 (DB に紐付いている場合はカラム名のこと) は何でも良いはず1なので、通常は省略して構わない。 また、呼び出すバリデータの名称に関しても、RSpec.describe InclusionInArrayValidator, type: :model do ... end のように書いたら、ふつうは inclusion_in_array で呼び出すはずなので、こちらも省略して構わない。 options を省略した場合 なお、options: { in: (1..47) } の部分を省略して、 let(:mock) { build_validator_mock.new(attribute: [1, 13, 27, 47]) } のように書いた場合、以下のようにバリデータを呼び出したことになる。 validates :prefecture, inclusion_in_array: true options を省略すると、代わりに true が入る。ここに指定する値として、よく true が来ることが多い印象なので、省略時は true が入るように CustomValidatorHelper で実装している。 他の属性 (カラム) と依存関係のあるカスタムバリデータをテストしたい場合 今までの説明 (都道府県 ID) のように、1 つの属性単体で完結する場合はこれで十分だろう。しかし、他の属性と依存関係がある場合はどのように書けば良いだろうか。 たとえば、サブカテゴリという属性があり、それがメインカテゴリと依存関係にあった場合のことを考える。 サブカテゴリ ID はメインカテゴリ ID と関係があり、ユーザから送信されるサブカテゴリ ID は、同じくユーザから送信されるメインカテゴリ ID に属しているものでなければならないとする。 もう少し具体的に説明するため、以下のリスト (以下、※ 1 とする) を参照してほしい。 ゲーム (ID: 1) アクション (ID: 1) パズル (ID: 2) RPG (ID: 3) アニメ (ID: 2) 日常系 (ID: 4) アドベンチャー (ID: 5) ほのぼの (ID: 6) ... (ID: 3) ... (ID: 7) ... (ID: 8) ... (ID: 9) それぞれ、ネストされていないのがメインカテゴリを表し、ネストされているのがサブカテゴリを表している。 ユーザからはメインカテゴリ ID とサブカテゴリ ID が送られてくるが、サブカテゴリ ID が、メインカテゴリ ID に属している必要がある。 たとえば、メインカテゴリで「ゲーム」(ID: 1) を選択しているのに、サブカテゴリで「ほのぼの」(ID: 6) を選択していたら弾くようなバリデータを想定している。 これを、以下のように実装したとする。 app/validators/sub_category_dependency_validator.rb # frozen_string_literal: true # 指定されたサブカテゴリの ID が正しいかどうかをチェックするカスタムバリデーション # # 送信されたすべてのサブカテゴリ ID からメインカテゴリ ID を調べた際に # それらがすべて、送信されたメインカテゴリ ID に含まれていれば OK # そうでなければメインカテゴリとサブカテゴリに不整合が起きているので弾く # class SubCategoryDependencyValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return if (SubCategory.find(value).pluck(:main_category_id) - record.main_category).empty? record.errors.add(attribute, options[:message] || :sub_category_dependency) end end 送信されたサブカテゴリに対してバリデーションをかけるカスタムバリデータの中で、送信されたメインカテゴリを参照している。record.main_category の部分が該当する。 その際、メインカテゴリをテスト内でどのように指定するか、ということである。 その場合は、build_validator_mock を以下のように呼び出せば良い。 let(:mock) do build_validator_mock(attribute: :sub_category, record: :main_category). new(sub_category: [3, 6], main_category: [1, 2]) end build_validator_mock を呼び出す際の引数として、record: :main_category というものを追加する。 そして、attribute と record に指定したそれぞれのキーを使って、new(sub_category: [3, 6], main_category: [1, 2]) のようにインスタンスを生成する。 これにより、バリデータ内で、value には、attribute として指定した sub_category の値 ([3, 6]) が入り、record.main_category には、record として指定した main_category の値 ([1, 2]) が入ることになる。 ちなみに、上記のサブカテゴリ ID は、※ 1 のリストによれば、メインカテゴリ ID に属しているので、正常系となる。 new(sub_category: [3, 6], main_category: [1, 2]) の部分を、たとえば new(sub_category: [6], main_category: [1]) のようにした場合、メインカテゴリ「ゲーム」と、メインカテゴリ「ゲーム」に属さない (メインカテゴリ「アニメ」に属する) サブカテゴリ「ほのぼの」が指定されているため、異常系となる。 参考サイト カスタムバリデータのテストの書き方について 【Rails】まだValidatorのテストで消耗してるの? Rails の Custom Validator をテストする RailsでCustom validatorをテストする RSpec 用のモジュールの置き場所と読み込み方について RSpecコトハジメ ~初期設定マニュアル~ Struct の文法について Class: Struct (Ruby 2.7.0) 一応、エラーメッセージ内に属性名が含まれるので、エラーメッセージの文字列が正しいかどうかをテストする際には関係してくるかもしれないが、そもそもエラーメッセージの文言をそのままテストで書くのはあまり良い作法ではないと個人的には思う。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] jQueryの導入

以下、Ruby on Rails 6.0.0における jQueryのインストール方法です。 gemの導入 application.jsに記述追加 application.html.erbのhead部に記述追加 任意の内容で動作確認 1. gemの導入 以下の記述を追加し、bundle installを打ちます。 Gemfile gem 'jquery-rails' 2. application.jsに記述追加 ディレクトリがない場合は apps/assets/javascripts/application.js 作成します。 app/assets/javascripts/application.js //= require jquery //= require jquery_ujs 3. application.html.erbのhead部に記述追加 以下の一行を追加します。 views/layouts/application.html.erb <head> #(中略) <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> 4. 任意の内容で動作確認 ~.html.erb <p>jQuery適用前</p> <script type="text/javascript"> $(document).ready(function(){ $("p").text("jQuery適用後"); }); </script> 表示内容が "jQuery適用後" として表示されていれば成功です。 今回は以上です。 お読みくださりありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cannot load such file -- rexml/documentと出た時の対処法!

環境 macOS Big Sur 11.5.2 rails 6.1.4 railsチュートリアル6章学習中 実行 $ rails test:models エラー rails aborted! LoadError: cannot load such file -- rexml/document 解決法 Gemfileに Gemfile gem 'rexml' を追加し $ bundle install 再度 $ rails test:models ↓ Run options: --seed 62060 # Running: . Finished in 0.316916s, 3.1554 runs/s, 3.1554 assertions/s. 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips できました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】TypeError (nil can't be coerced into Integer):の対処法

症状 RailsAPIモードでメソッド作成し叩いたところ、以下のエラーが表示されてしまいました。 翻訳すると、「TypeError(nilを整数に強制変換することはできません):」でした。 nilを整数に変換しようとして、失敗しているようです。 error Completed 500 Internal Server Error in 21ms (ActiveRecord: 0.4ms | Allocations: 18292) TypeError (nil can't be coerced into Integer): app/controllers/hoges_controller.rb:112:in `+' app/controllers/hoges_controller.rb:112:in updateHoge 該当のメソッドは以下です。 paramsからidを受け取り、DBからHogeを検索。 その検索したHogeのvaluationとparamsからvaluationを受けとり、その二つの値の平均値を再度Hogeに更新するメソッドです。 Hoge.rb def updateHoge hoge = Hoge.find(params[:hoge_id]) newValuation = hoge.valuation + params[hoge_valuate] newValuation = newValuation / 2 if hoge.update!(:valuation newValuation) render json: { hoge: hoge },status: :ok else render json: {},status: :ng end end 解決方法 paramsで受け取るときの指定先を修正したら、解決しました。 paramsから受け取るときに指定先が間違っていると「nil」になり、nilとintegerの値を足そうとしていたため、エラーになっていました。 Hoge.rb def updateHoge hoge = Hoge.find(params[:hoge_id]) //integer + nilになっているためエラーだった newValuation = hoge.valuation + params[hoge_valuation] newValuation = newValuation / 2 if hoge.update!(:valuation newValuation) render json: { hoge: hoge },status: :ok else render json: {},status: :ng end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveStorage と variant と それから N+1

概要と対策手順 Active Storageを使ってポートフォリオを作成 作成後のレビューでActive StorageでN+1を確認する 原因はActive Storageのvariantだった 現環境では対処できないのと発覚。 variantをやめてCSSでの制御に変更し解決。 ※ポートフォリオの詳細に関してはこちらに投稿してあります。 実行環境 Ruby ( 2.7.2 ) Ruby on Rails ( 6.1.4.1 ) PostgreSQL Active Storage と N+1 対象になったのはこちら(記事の投稿者を示すユーザーアイコン) 当時のコントローラーの処理は下記 @articles = Article.published.includes(:tag_maps, :tags, :keeps, user: { avatar_attachment: :blob }).order(created_at: :desc).page(params[:page]).per(PER_PAGE) avatar取得時のN+1には下記を参考に対応しているつもりだった。 参考: Active StorageのN+1問題に対処する 原因は variant だった ローカルに接続した時のログを眺めていると ActiveStorage::VariantRecord Load が記事の表示件数分弱表示されていた。 アイコンの画像を場所ごとにサイズを分けて表示したかったのでapplication_helper.rbに下記の処理をしていた。 application_helper.rb def user_icon(user, size) case size when "mypage" user.avatar.variant(resize: "72x72").processed when "profile" user.avatar.variant(resize: "64x64").processed when "list" user.avatar.variant(resize: "25x25").processed end end このバリアント画像をN+1しているらしい。 現環境では対処できない 自分で調べてみたが座礁したので、相談をし調査継続 週刊RailsウォッチさんとRuby on Rails APIに辿り着き、下記を見て対処方法とvariantが原因のN+1だと気づく。 現在のActive Storageではvariantトラッキングで添付ファイルごとにvariantが存在するかどうかをチェックするクエリが走る。通常のRailsのN+1防止策(includes)ではこれを防止できない。 このプルリクはwith_all_variant_recordsメソッドを追加し、かつincludesがActive Storageの添付ファイルで期待どおり動作するようになる。 解決の糸口は見えてきたので、try&error で挑戦するも一向に解決できず。 scope :with_all_variant_records, -> { includes(blob: :variant_records) }という記述をみた際に先ほど参考にしたActive StorageのN+1問題に対処する内で出てきたscope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }を参考にしたがずっとundefind methods with_all_variant_recordsと表示される(今思うと感の良い人なら気付きそうなログですね) どうやら自分の使用しているRailsのverではwith_all_variant_recordsが実装されていないという事を教えてもらいました。(githubの英語訳を見てた時にRails7に対応する的な事が書いてあったのはこの事かと後で合点がいく) 確かに github の main にはscope :with_all_variant_records, -> { includes(blob: :variant_records) }が記載され 6.1.4.1の方には記載がなかった。(通りでundefind methodsな訳ですね) CSS制御に変更し 件の根本的な原因と現状は分かりましたが、現状の改善はできていません。 今回はvariantをやめてcssで調整する方向に転換しました。 変更手順 application_helper.rbで使用しているvariant(resize: "サイズ").processedの記述を削除 CSSで画像調整する記述を用意 CSSでの画像調整 今回はSCSSを利用 html.erb内でvariantを使用していた部分のクラス名を変更 同じ記述が増えるので@mixinと@includeを使用する @mixin icon_size($size: 25px) { border-radius: 50%; width: $size; height: $size; } /* アイコンサイズ */ .list-user-icon { @include icon_size; } .profile-user-icon { @include icon_size(64px); } .mypage-user-icon { @include icon_size(72px); } スッキリ!! 解決 これでログを見てもActiveStorage::VariantRecord Load が減りました。 無事解決!! 今回の件で解決方法を正規ルートに拘りすぎず、違う形でも解決できる方法も一緒に考えることができるようになる力も必要だなと感じました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

イベントclose機能を作ってみた(論理削除)

やりたいこと ポートフォリオとしてイベント投稿アプリを作成中イベントの開催日が過ぎたイベントの扱いをどうしようかと調べていたら論理削除の存在を知ったのでイベントを論理削除してイベントを閉鎖する機能を自作した。 機能要件 実装の前に今回の機能要件をまとめると、 ① イベントの期日が過ぎたイベントを論理削除する 具体的には、 イベントカラムにbooleanを持たせる 開催日が過ぎたイベントのbooleanの状態でViewの表示を変更する ② 論理削除はイベント期日が過ぎたら自動で実行 具体的には、 イベント開催日が過ぎたらbooleanを変更するメソッドを定義 そのメソッドをrakeタスクに登録 rakeタスクを定期実行する 実装 それでは実装開始! ① イベントの期日が過ぎたイベントを論理削除する イベントカラムにbooleanを持たせる 命名は今回はstatusとしてデフォルトをfalseに設定する コンソール $ rails g migration AddStatusToEvents status:boolean class AddStatusToEvents < ActiveRecord::Migration[6.1] def change add_column :events, :status, :boolean, default: false end end コンソール $ rails db:migrate 開催日が過ぎたイベントのbooleanの状態でViewの表示を変更する やることは簡単 viewの表示を条件分岐するだけ view - if @event.status? .btn.btn-danger 終了 - else .btn.btn-primary 参加する 画面で表すとこんな感じ falseの時(通常時) trueのとき(開催日が過ぎた) これでイベントの開催日が過ぎたことが一目瞭然! ② 論理削除はイベント期日が過ぎたら自動で実行 イベント開催日が過ぎたらbooleanを変更するメソッドを定義 ここがなんだかんだ一番悩んだ場所 日付の処理をどうするか色々調べた結果以下のような形に railsはDate.todayで今日の日にちが取れることを初めて知った。 Event.where('start_time < ?', Date.today).where(status: false).update_all(status: true) そのメソッドをrakeタスクに登録 あとは定期実行するためにrakeタスクに登録 ターミナル $ rails g task close_event lib/tasks/close_event.rake namespace :close_event do desc "イベント開催日が過ぎたらイベントをcloseする" task close_event: :environment do Event.where('start_time < ?', Date.today).where(status: false).update(status: true) end end 一応ここで動作確認 ターミナル $ rake close_event:close_event rakeタスクを定期実行する 定期実行にはwheneverを使う Gemfile gem 'whenever', require: false ターミナル $ bundle install 設定ファイルをインストール ターミナル $ bundle exec wheneverize . [add] writing `./config/schedule.rb' [done] wheneverized! 設定を書いてrakeタスクが毎朝6時に実行されるようにセット config/schedule.rb require File.expand_path(File.dirname(__FILE__) + '/environment') # cronを実行する環境変数 rails_env = ENV['RAILS_ENV'] || :development # cronを実行する環境変数をセット set :environment, rails_env # cronのログの吐き出し場所 set :output, "#{Rails.root}/log/cron.log" # 時間指定 every 1.day, at: '6am' do rake 'close_event:close_event' end cronを更新して完成 ターミナル $ bundle exec whenever --update-crontab まとめ 実装する前に機能要件と機能実装の流れを具体的にすることでスムーズに実装することができた。 機能を自作するのは大変だったがとても楽しかった!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RSpec】結合テストを実施した際に出たchromedriverに関するエラーの解消方法

対象者 chromedriverに関するエラーが発生している方 RSpe結合テストを実施中の方 JavaScriptのテストが不要な方 GoogleChromeを使用中の方 目的 エラーを解消してRSpecテストを実行できるようにする 実際の手順と実例 1.前提 RSpecテスト実装 Ruby 2.6.3 Rails 5.2.5 Gemfileは下記の通り Gemfile. : : group :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 2.15' gem 'selenium-webdriver' # Easy installation and use of chromedriver to run system tests with Chrome gem 'webdrivers' gem 'rspec-rails' gem 'factory_bot_rails' gem 'faker' end : : 2.現状(エラー内容) 下記のようなエラーが出ています。 調べてたところによると、chromedriver-helperというgemがないことが問題みたいです。 しかし、chromedriver-helperのサポートはgemは2019年3月31日に終了しています。 webdriversというgemへ移行してくださいとのことですが、 下記の記事を参考にさせて頂いた際にうまく行かなかったので、 渋々chromedriver-helperをアプリケーションにインストールすることにしました。 3.Chromedriverをインストールして解決する方法 Chromedriverのインストール rails_helper.rbの修正 上記2点を実行していきます。 1. Chromedriverのインストール まず手始めにGoogleChromeをupdateしていきます。 Terminal. $ sudo yum -y update $ sudo yum -y install google-chrome-stable $ sudo yum -y install libX11 GConf2 fontconfig libOSMesa google-noto-cjk-fonts $ google-chrome --version その後下記のコマンドでChromedriverをインストールしていきます。 Terminal. sudo yum -y install unzip curl -O -L https://chromedriver.storage.googleapis.com/85.0.4183.38/chromedriver_linux64.zip unzip chromedriver_linux64.zip sudo mv chromedriver /usr/local/bin rm chromedriver_linux64.zip chromedriver -v 最後のコマンドを実行するとChromedriverのバージョンが表示されます。バージョンは下記のサイトで確認できます。 ここまで実行したら再度、テストを実行します。 実行後は更に下記のようなエラーがでました。 Terminal. Selenium::WebDriver::Error::UnknownError: unknown error: Chrome failed to start: exited abnormally. (unknown error: DevToolsActivePort file doesn't exist) (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.) 2. rails_helper.rbの修正 rails_helper.rb RSpec.configure do |config| : : : config.include FactoryBot::Syntax::Methods config.before(:each) do |example| if example.metadata[:type] == :system if example.metadata[:js] driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400] else driven_by :rack_test end end end # Capybara.javascript_driver = :selenium end 上記を追加したらRSpecテストが正常に動作しました。下記の記事を参考にさせて頂いております。 参考にさせて頂いた記事 Selenium::WebDriver::Error::UnknownError:の対処法(seleniumではなくrackを指定してテストを走らせる) サポートが終了したchromedriver-helperからwebdrivers gemに移行する手順 WSL2 環境で RSpec (Capybara) で「Webdrivers::BrowserNotFound: Failed to find Chrome binary.」エラーが出るとき ChromeDriver - WebDriver for Chrome - Downloads
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第4章 ユーザークラス

ユーザークラス class User attr_accessor :name, :email def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] end end attr_accessor :name, :email ユーザー名とメールアドレス(属性: attribute)に対応するアクセサー(accessor) をそれぞれ作成 アクセサーを作成すると、そのデータを取り出すメソッド(getter)と、データに代入するメソッド(setter)をそれぞれ定義 インスタンス変数は常に@記号で始まり、まだ定義されていなければ値がnil initializeは、Rubyの特殊なメソッドです。これは User.newを実行すると自動的に呼び出されるメソッド def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] end attributes変数は空のハッシュをデフォルトの値として持つため、名前やメールアドレスのないユーザーを作ることができます def formatted_email "#{@name} <#{@email}>" end このメソッドは、文字列の式展開を利用して、@nameと@emailに割り当てられた値をユーザーのメールアドレスとして構成 requireして、自作したクラスを試しに使ってみましょう。 >> require './example_user' => true >> example = User.new => #<User:0x00007f410082a2b0 @name=nil, @email=nil> >> example.name => nil >> example.name = "Example User" => "Example User" >> example.email = "user@example.com" => "user@example.com" >> example.formatted_email => "Example User <user@example.com>" require './example_user' requireのパスにある’.’は、Unixの “カレントディレクトリ” ’./example_user’というパスは、カレントディレクトリからの相対パスでexample_userファイルを探すように指示 example = User.new 次のコードでは空のexample_userを作成します 演習 1.クラスを作成 class User attr_accessor :first_name, :last_name, :email def initialize(attributes = {}) @first_name = attributes[:first_name] @last_name = attributes[:last_name] @email = attributes[:email] end def full_name "#{@first_name} #{@last_name}" end def formatted_email "full_name <#{@email}>" end def alphabetical_name "#{@last_name}, #{@first_name}" end end 2.alphabetitcal_nameメソッドを作成 def alphabetical_name "#{@last_name}, #{@first_name}" end 3.full_name.splitとalphabetical_name.split(', ').reverseの結果を確認 >> user.full_name.split => ["Michael", "Hartl"] >> user.alphabetical_name.split(', ').reverse => ["Michael", "Hartl"] 同じになった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第4章 コントロールクラス

コントロールクラス クラスや継承を勉強してきた。 lass StaticPagesController < ApplicationController def home end def help end def about end end クラス階層を調べよう。 >> controller.class => StaticPagesController >> controller.class.superclass => ApplicationController >> controller.class.superclass.superclass => ActionController::Base >> controller.class.superclass.superclass.superclass => ActionController::Metal >> controller.class.superclass.superclass.superclass.superclass => AbstractController::Base >> controller.class.superclass.superclass.superclass.superclass.superclass => Object Object ↑ AbstractController::Base ↑ ActionController::Metal ↑ ActionController::Base ↑ ApplicationController ↑ StaticPagesController という風になっている。 >> controller.home => nil 。Railsのアクションには戻り値がありません 演習 toy_appを消していたから解けなかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RSpec]コメント機能の単体テスト

はじめに 本記事では、コメント機能の単体テストのコードや説明を記述します。 前回いいね機能について投稿しているので、こちらも参照ください。 前提 コメント機能の実装ずみ 私は、投稿に対するコメントを実装しています。 コード 早速ですが、コードを記述します。 初めの方のsleep 0.2は必要ありません。 自宅のWi-Fiがとんでもないぐらい遅いので記述しています。一応記事を添付します。 spec/models/comment_spec.rb require 'rails_helper' RSpec.describe Comment, type: :model do before do user = FactoryBot.create(:user) food = FactoryBot.create(:food) @comment = FactoryBot.build(:comment, user_id: user.id, food_id: food.id) sleep 0.2 end describe 'コメント機能' do context 'コメントを保存できる場合' do it "コメント文を入力済みであれば保存できる" do expect(@comment).to be_valid end end context 'コメントを保存できない場合' do it "コメントが空では投稿できない" do @comment.text = '' @comment.valid? expect(@comment.errors.full_messages).to include "Text can't be blank" end it "ユーザーがログインしていなければコメントできない" do @comment.user_id = nil @comment.valid? expect(@comment.errors.full_messages).to include "User must exist" end it "投稿したものがなければコメントできない" do @comment.food_id = nil @comment.valid? expect(@comment.errors.full_messages).to include "Food must exist" end end end end textはコメントするテキストのことです。ちょっと命名下手くそすぎたかな。。。 comment.rb FactoryBot.define do factory :comment do text {'aaaa'} association :user association :food end end 私の中でテストする項目は以下の4つだと思っています。 - コメント文を入力済みであれば保存できる - コメントが空では投稿できない - ユーザーがログインしていなければコメントできない - 投稿したものがなければコメントできない 下2つのテストは、 it "ユーザーがログインしていなければコメントできない" do @comment.user_id = nil @comment.valid? expect(@comment.errors.full_messages).to include "User must exist" end it "投稿したものがなければコメントできない" do @comment.food_id = nil @comment.valid? expect(@comment.errors.full_messages).to include "Food must exist" end FactoryBotのところで、 以下を記述します。 association :user association :food さらに、以下を記述することでテストすることが可能になりました。 spec/models/comment_spec.rb before do user = FactoryBot.create(:user) food = FactoryBot.create(:food) @comment = FactoryBot.build(:comment, user_id: user.id, food_id: food.id) sleep 0.2 end 以上です。 いかがでしょうか。 どうにかテストできない場合は、空欄ではコメント送信できないようバリデーションできているかも確認してみます。 追記 @github0013@github さんからご意見いただき修正しました。ありがとうございます。 comment.rb FactoryBot.define do factory :comment do text {'aaaa'} user food end end spec/models/rails_helper.rb RSpec.configure do |config| config.include FactoryBot::Syntax::Methods ←一番下の行に記述しました。 end spec/models/comment_spec.rb before do user = create(:user) food = create(:food) @comment = build(:comment, user_id: user.id, food_id: food.id) sleep 0.2 end spec/rails_helper.rb # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } ↑コメントアウトする また、一段と賢くなりました。 終わりに テストは奥が深く、やればやるほど、また考えれば考えるほどどんどん浮かんできます。 作成されたアプリケーションもテストされている項目が多ければその分、使っていてバグが起こりにくいアプリケーションに仕上がるので、ここは注意できればなと考えました。 以下、参考サイトです。 コメント機能 モデル単体テスト 実装 明日も頑張ります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む