- 投稿日:2021-01-24T23:55:53+09:00
undefined method `upload' for nil:NilClassが出た時の解決法
今回のエラー
きっかけ
S3を作成中のアプリに導入しようとしたところ、このエラーが出ました。
結論
開くべきシェルが間違っていた。
S3を導入する際には、環境変数を指定しなければならないのですが、その際にvimメソッドのインサードモードの中に記述したのですが、その時にターミナルに
ターミナルvim ~/.zshrcとしなければならないところを
ターミナルvim ~/.zshrと打ち込んで、その中に記述していました。
要するにスペルミスですね。
散々原因を探した後だったので、「今更スペルミスなんか見逃すかよ」、とスペルミスの可能性を削除していたが故のミスでした。これからはもっと謙虚な気持ちでエラーと向き合っていきます。
- 投稿日:2021-01-24T23:28:48+09:00
Railsのpresentメソッドの使い方
presentメソッドとは
オブジェクトであるレシーバーの値が存在すればtrue、存在しなければfalseを返すメソッドです。条件分岐(if文等)をプログラムで書くときによく使います。
例えばアプリの購入機能において「もし選択した商品に紐づく購入記録が存在していたら(空ではなかったら)、"sold out"と表示する」を実装したい場合は該当のビューファイルにおいて
index.html.erb<% if item.purchase.present? %> <div class='sold-out'> <span>Sold Out!!</span> </div> <% end %>という様に記述します。
<% if item.purchase.present? %>
の部分ですが
itemモデルとpurchaseモデルにテーブル間のアソシエーションが組んであれば
商品(item)に紐づく購入記録(purchase)が存在していたら(空ではなかったら)=true
という解釈になります。
- 投稿日:2021-01-24T23:28:48+09:00
Railsのpresent?メソッドの使い方
present?メソッドとは
オブジェクトであるレシーバーの値が存在すればtrue、存在しなければfalseを返すメソッドです。条件分岐(if文等)をプログラムで書くときによく使います。
例えばアプリの購入機能において「もし選択した商品に紐づく購入記録が存在していたら(空ではなかったら)、"sold out"と表示する」を実装したい場合は該当のビューファイルにおいて
index.html.erb<% if item.purchase.present? %> <div class='sold-out'> <span>Sold Out!!</span> </div> <% end %>という様に記述します。
<% if item.purchase.present? %>
の部分ですが
itemモデルとpurchaseモデルにテーブル間のアソシエーションが組んであれば
商品(item)に紐づく購入記録(purchase)が存在していたら(空ではなかったら)=true
という解釈になります。以下のサイトにはpresent?メソッドと逆のblank?メソッドも紹介されています。
- 投稿日:2021-01-24T23:21:59+09:00
ActionMailerを使用した問い合わせ機能の実装
実装機能
問い合わせフォームからメールを送信、それを指定したメールアドレスで受信する。
(アプリの利用者から管理者に向けてのメール機能として実装)ActionMailerでは他にもメルマガ配信のように運営側からユーザーに対してメール送信を行ったり、問い合わせに対するメールの返信機能なども実装が可能とのことだが、今回は一番シンプルなこちらの機能を実装。
またGmailアカウントでのメールの受信方法の為、前提としてGmailのアカウントを持っている体で話を進めていく。完成イメージ
実装内容
1.コントローラの作成
使用するビューファイルはnewのみな上にルーティングの修正が面倒な為、アクションの指定はせずにコントローラファイル単体で作成。
$ rails g controller inquiries2.ルーティングの作成
今回使用するアクションは以下の2つ
・問い合わせ作成画面に使用するnewアクション
・問い合わせのデータを作成する為のcreateアクション
のみconfig/routes.rbresources :inquiries, only[:new, :create]3.モデルの作成
最低限必要な情報としてユーザーの名前とメッセージ内容を保存するカラムを用意するが、今回は送信したユーザーが問い合わせに関して返答を要求する場面を想定し、emailのカラムも作成しておく。
$ rails g model Inquiry name:string email:string message:textマイグレーションファイルに特に変更がなければ
bundle install
を実行してテーブルを作成。その後必要なカラムにバリデーションをかけておく。
app/models/inquiryclass Inquiry < ApplicationRecord validates :name, presence: true validates :email, presence: true validates :message, presence: true end4.Mailerの作成
ActionMailerという機能がRailsに標準搭載されており、下記のコマンドで作成が出来る
terminal$ rails g mailer inquiry以下のファイルが作成されるのを確認
terminalcreate app/mailers/inquiry_mailer.rb invoke erb create app/views/inquiry_mailer invoke test_unit create test/mailers/inquiry_mailer_test.rb create test/mailers/previews/inquiry_mailer_preview.rb
このうちの
inquiry_mailer.rb
は、メール送信機能を実装するための空のクラス5.Mailerにメソッドを定義
InquiryMailer
内でメソッドを定義する。
ここで定義したメソッドを実際にデータの作成、送信を実行するInquiriesコントローラ
内で使用するapp/mailers/inquiry_mailer.rbclass InquiryMailer < ApplicationMailer def send_mail(inquiry) @inquiry = inquiry mail to: 'メールアドレス', subject: '【サイト名】お問い合わせ通知' end end上記のmailメソッドで指定されているプロパティの内容は以下の通り
・to = 送信先(メールアドレス)の指定
・subject = メールの件名
要はここで指定したアドレスから指定した件名でメールが届くということなる。
6.コントローラにメソッドを定義
app/controllers/inquiries_controller.rbclass InquiriesController < ApplicationController def new @inquiry = Inquiry.new end def create @inquiry = Inquiry.new(inquiry_params) if @inquiry.save InquiryMailer.send_mail(@inquiry).deliver redirect_to new_inquiry_path flash[:email] = "Your message was successfully sent." else render 'new' end end private def inquiry_params params.require(:inquiry).permit(:name, :email, :message) end endアクション内の処理の流れは通常の投稿作成とほぼ同じだが、createアクション内の処理で先程作成した
InquiryMailer
のメソッドを使用する。InquiryMailer.send_mail(@inquiry).deliverコードの文末にあるdeliverメソッドはメールの送信に関わるメソッドになる。
因みに、当然の事ながらこの行の処理が完了するまで以降のredirect等の処理が行われない為、フラッシュメッセージが現れるまで数秒間掛かることもある。
それを回避する為にActiveJob
と連携して非同期にメール送信を行う為のdeliver_laterというメソッドも存在するらしいが、今回はこちらを採用。7.development.rbにメール送信設定を記述する
ドメインの指定等の設定をconfigディレクトリ配下のdevelopment.rbファイルに記述する。
config/environment/development.rbconfig.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.gmail.com', port: 587, domain: 'gmail.com', user_name: 'メールアドレス', password: 'パスワード', authentication: 'plain', enable_starttls_auto: true }このあたりはこちらの参考記事の説明がわかりやすかったのでそのまま引用させて頂くことにする。
ここでは、config.action_mailerというパラメーターに色んなオプションを指定してます。
1行目 raise_delivery_errors
メールの送信に失敗した時にエラーを出すかどうか (出したいので true)2行目 delivery_method
メールの送信方法。 デフォルトで :smtd なので気にする必要もないのですが、
わたしみたいに「なにそれ!?」ってなった方は以下の引用を読んでみてください。「SMTP」とは「Simple Mail Transfer Protocol(シンプル・メール・トランスファー・プロトコル)」の略で、あえて>訳せば「簡単なメールの送信の手順」というところだろうか。お約束ごとと考えてもいい。
あなたがメールを書き、宛先のアドレスを入力して「送信」アイコンをクリックする。このとき、あなたのスマホやパソコン>は、この「SMTP」のお約束ごとに従って、あなたが契約しているメールサーバーと、こんなやり取りをするのである。
「メールを送るよ〜」「ええで!」「宛先は〇◯だよ」「りょ」「本文はかくかくしかじかだよ」「受け取ったで!」――とまぁそんな具合。出典: メール設定で最初につまずく「SMTP」「POP」「IMAP」。その意味&設定方法は?
3行目 smtp_settings
smtpの詳細設定って感じです。port => SMTPサーバーのポート番号
address => SMTPサーバーのホスト名
domain => HELOドメイン
user_name => メール送信に使用するgmailのアカウント
password => メール送信に使用するgmailのパスワード
authentication => 認証方法
enable_starttls_auto => メールの送信にTLS認証を使用するか※前項のMailerやここで使用するメールアドレスやパスワードはGithubにpushする際には環境変数化しておく必要があるので注意
8.ビューを作成する
問い合わせフォーム用のビュー画面を作成する。
コードは冒頭で添付した画面で使用したものなので、ご自身のアプリに合わせて作成。
通常のフォーム画面の作成と変わらないはず。app/views/inquiries/new.html.erb: <div class="contact_form text-center"> <%= form_with model: @inquiry, local:true do |f| %> <%= render 'layouts/error_messages', model: f.object %> <h3>NAME</h3> <%= f.text_field :name %> <h3>EMAIL</h3> <%= f.email_field :email %> <h3>MESSAGE</h3> <%= f.text_area :message %> <div> <p><%= f.submit "送信" %></p> </div> <% end %> </div> :9.メール画面を構成するファイルを作成
上記とは別に実際に送られてくるメールのレイアウトを構成するファイルも作成する必要がある。
ActionMailerのコマンドを実行した際にviewsディレクトリ
の中にinquiry_mailerディレクトリ
が作成されているため、そこにファイルを作成する。
今回はアプリの管理者が受け取る想定の為、構成には必要最低限しか気を配っていない。因みに場合によってはHTML形式のメールを受け取ることができない場合もあるらしく、そのためテキスト形式のファイルも用意しておくのがベスト。
HTML形式
app/views/inquiry_mailer/send_mail.html.erb<h5>お問い合わせ内容</h5> <P>-----------------------------------------</P> <p>名前:<%= @inquiry.name %></p> <p>メールアドレス:<%= @inquiry.email %></p> <p>お問い合わせ内容:<%= @inquiry.message %></P> <p>-----------------------------------------</p>テキスト形式
app/views/inquiry_mailer/send_mail.text.erbお問い合わせ内容 ----------------------------------------- 名前:<%= @inquiry.name %> メールアドレス:<%= @inquiry.email %> お問い合わせ内容:<%= @inquiry.message %> -----------------------------------------実際に届くメール画面がこちら(HTML形式)
10.環境変数を使用
Githubにpushしたり、本番環境でアプリを使用する際はこれまでに使用したメールアドレスやパスワードはセキュリティを考慮し環境変数化しておく必要がある。
gemのdotenv-rails
をinstallしている前提での説明な為、gemをインストールしていない場合は導入する。
.envファイル
に実際のメールアドレスやパスワードを記述.envSEND_MAIL="使用するメールアドレス" GMAIL_PASSWORD="使用するGmailアカウントのパスワード"定義した環境変数をコード内で使用
app/mailers/inquiry_mailer.rb: mail to: ENV['SEND_MAIL'], subject: '【サイト名】お問い合わせ通知' :config/environment/development.rb: user_name: ENV['SEND_MAIL'], password: ENV['GMAIL_PASSWORD'], :
.envファイル
を.gitignoreファイル
に記述して完了補足
gmailのアカウントの設定の関係でアクション実行の際に以下のようなサーバーエラーが発生する可能性がある。
・
Net::SMTPAuthenticationError (535-5.7.1 Username and Password not accepted. Learn more at
・Net::SMTPAuthenticationError 534-5.7.9 Application-specific password required.
上記エラーが現れた際は、別途Gmailアカウントの設定変更が必要となる可能性が高い(パスワードやメールアドレスのスペルミス等の可能性もあるため、まずはそこを疑う)。
個人的には実装よりもむしろこれらのエラーの解消に手こずったので、参考になれば。参考記事
ActionMailer参考記事
・Action Mailer でメール送信機能をつくる
・【Rails入門説明書】Action Mailerについて解説
サーバーエラーの解決のために使用
・Googleで2段階認証を使っているときにRailsのActionMailerでGmailを使う方法
・[Rails5]deviseでgmailを送ろうとしたがsmtp認証のエラーが出た時にした事。
- 投稿日:2021-01-24T21:52:45+09:00
ユーザー登録時のウィザード形式に変更
はじめに
オリジナルアプリ制作にあたり、ユーザー登録時にウィザード形式(対話するように順番に操作が進んでいく方式)を用いるように変更しました。
ウィザード形式にすることで、ユーザーからすると見やすく使いやすいとのことだったため、変更することにしました。変更前はユーザー登録と本人確認が以下の画像のように一緒の画面にある状態。
1ページが縦長になってしまい、少しわかりづらい。(スクショにも全部納まっていないし…。)
なので、今回はユーザー情報登録画面と本人確認登録画面に2ページに分けていきたいと思います。
変更前・変更後のテーブルの状態
変更前
現状、会員情報入力と本人確認が同じテーブルになっている。
・Userテーブル
Column Type Options nickname string null: false string null: false password string null: false last_name string null: false first_name string null: false last_name_kana string null: false first_name_kana string null: false phone_number string null: false
変更後
ウィザード形式にするために以下の形にテーブルを変更したい。
・Userテーブル(viewでは会員情報入力画面)
Column Type Options nickname string null: false string null: false password string null: false ・Identificationテーブル(viewでは本人確認入力画面)
Column Type Options last_name string null: false first_name string null: false last_name_kana string null: false first_name_kana string null: false phone_number string null: false user references optional: true 1.テーブル・モデルの変更・追加
1.Userテーブルの不要なカラム(last_nameやphone_numberなど)を以下のコマンドで削除していく。
完了したらマイグレート(rails db:migrate)を実行する。ターミナル% rails g migration Removeカラム名From削除元テーブル名 削除するカラム名:型 ↓ (例) % rails g migration RemoveLastNameFromUsers last_name:string2.Identificationモデル・マイグレーションファイルを作成する
ターミナル% rails g model identification3.作成したIdentificationのマイグレーションファイルを編集し、マイグレート(rails db:migrate)を実行する。
2021***********_create_identificationsclass CreateIdentifications < ActiveRecord::Migration[6.0] def change create_table :identifications do |t| t.string :last_name, null: false t.string :first_name, null: false t.string :last_name_kana, null: false t.string :first_name_kana, null: false t.string :phone_number, null: false t.references :user t.timestamps end end end4.Identificationモデルを編集する。
app/models/idetification.rbclass Identification < ApplicationRecord validates :phone_number, presence: true, length: { maximum: 11 }, format: { with: /\A[0-9]+\z/, message: 'ハイフンは必要ありません' } with_options presence: true, format: { with: /\A[ぁ-んァ-ン一-龥]+\z/, message: '全角文字を使用してください' } do validates :first_name validates :last_name end with_options presence: true, format: { with: /\A[ァ-ン]+\z/, message: '全角カナを使用してください' } do validates :first_name_kana validates :last_name_kana end belongs_to :user, optional: true end4.Userモデルを編集する。
userテーブルとidentificationテーブルは1対1の関係なのでhas_oneを使用。app/models/user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # 中略 has_one :identification #追記 end2.ルーティングの変更
本人確認を登録するページを表示するnew_identificationと情報を保存するcreate_identificationを追記する。
app/config/routes.rbRails.application.routes.draw do devise_for :users, controllers: { registrations: "users/registrations", } #以下を追記する。 devise_scope :user do get 'identification', to: 'users/registrations#new_identification' post 'identification', to: 'users/registrations#create_identification' end # 中略 end3.Userコントローラーの変更
1.ユーザー情報登録に関するコントローラー追記
app/controllers/users/registrations_controller.rbclass Users::RegistrationsController < Devise::RegistrationsController def new @user = User.new end def create @user = User.new(sign_up_params) render :new and return unless @user.valid? session['devise.regist_data'] = { user: @user.attributes } session['devise.regist_data'][:user]['password'] = params[:user][:password] @identification = @user.build_identification render :new_identification end endrender :new and return unless @user.valid?
→ユーザー登録情報の送られてきたパラメータがバリデーションに引っかかっていないかを確認している。session['devise.regist_data'] = { user: @user.attributes }
session['devise.regist_data'][:user]['password'] = params[:user][:password]
→ユーザー登録情報をsessionに保持させている。
1行目だけでは、パスワード情報が含まれていないため、2行目でパスワード情報を保持させる。@identification = @user.build_identification
render :new_identification
→buildで次の本人確認で使用するインスタントを生成し、そのページで遷移させている。
2.本人確認登録に関するコントローラー追記
app/controllers/users/registrations_controller.rbclass Users::RegistrationsController < Devise::RegistrationsController #中略 def create_identification @user = User.new(session['devise.regist_data']['user']) @identification = Identification.new(identification_params) render :new_identification and return unless @identification.valid? @user.build_identification(@identification.attributes) @user.save session['devise.regist_data']['user'].clear sign_in(:user, @user) redirect_to root_path end private def identification_params params.require(:identification).permit(:last_name, :last_name_kana, :first_name, :first_name_kana, :phone_number) end endrender :new_identification and return unless @identification.valid?
→本人確認情報の送られてきたパラメータがバリデーションに引っかかっていないかを確認している。@user.build_identification(@identification.attributes)
@user.save
→本人確認情報と先ほどsessionに保持していたユーザー登録情報を保存している。session['devise.regist_data']['user'].clear
→保存が完了し、sessionに保持していたユーザー登録情報が不要になったので、削除している。sign_in(:user, @user)
redirect_to root_path
→ログインし、トップページに遷移している。4.viewの変更
1.views/devise/registrationsに本人確認を登録するためのviewファイル(new_identification.html.erb)を作成する。
→views / devise / registrations / new_identification.html.erb2.作成したnew_identification.html.erbに本人確認用の入力フォームを記述していく。
views/devise/registrations/new_identification.html.erb<%= form_with model: @identification, url: identification_path, method: :post, local: true do |f| %> <div class='form-wrap'> <div class='form-header'> <h1 class='form-header-text'> 本人確認 </h1> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">お名前(全角)</label> <span class="indispensable">必須</span> </div> <!-- 中略 --> </div> <div class='hoge-btn'> <%= f.submit "会員登録" ,class:"hoge-btn" %> </div> </div> <% end %>5.ウィザード形式に変更完了
これで、ユーザー情報登録画面と本人確認登録画面の2ページに分けることが出来た。
ユーザー情報登録画面
最後に
今回の実装を通して、deviseのことも一緒に学ぶことが出来ました。
これまでdeviseはユーザー登録をするための便利なgemという認識しかなくsessionなどといった知識がほとんどなかったため、いい機会になりました。参考
以下の記事を参考にさせて頂きました。ありがとうございました。
sessionについて https://qiita.com/nakanishi03/items/51ff2ade4cec078280d6
ウィザードについて https://qiita.com/Tatsu88/items/7447a669b788b011e96b
- 投稿日:2021-01-24T20:22:52+09:00
listen tcp 0.0.0.0:xxx: bind: address already in us の対処法
目的
Ruby on Railsでポートフォリオを作成していた途中にdocker-compose upをした際に、listen tcp 0.0.0.0:xxx: bind: address already in usとエラーが出たのでその時の対処方法を忘れないように書きます。
エラー内容
$ docker-compose up -d ------------------------- ------------------------- Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use ERROR: Encountered errors while bringing up the project.対処方法
最初にこのコマンドを打ちます。
$ sudo lsof -i -P | grep "LISTEN" name1 633 ~~~~~~ 10u IPv4 0xc46fc72c3028ba51 0t0 TCP localhost:49362 (LISTEN) name2 634 ~~~~~~ 53u IPv6 0xc46fc72c50688ca1 0t0 TCP *:3000 (LISTEN) name3 96145 ~~~~~~ 54u IPv6 0xc46fc72c506892c1 0t0 TCP *:3306 (LISTEN) name4 72412 ~~~~~~ 82u IPv4 0xc46fc72c4cf4e591 0t0 TCP localhost:62741 (LISTEN)エラー内容は3306ポートはすでにあるよと言われています。
なので3306ポートをkillします。$ sudo kill -9 96145これでdocker-compose upができます!!
- 投稿日:2021-01-24T20:17:36+09:00
pg_hba.confファイル内のクライアント認証の優先順位
「現場で使える Ruby on Rails 5 速習実践ガイド」のChapter 6-9-3 「production環境用のデータベースを作成する」で詰まったのでメモ書きします。
実行環境
PostgreSQL 10.15
Ruby 2.7.1
Vagrant 2.2.9
CentOS 7.8.2003問題点
production環境用のデータベースを作成する以下のところでエラー発生
RAILS_ENV=production bin/rails db:create db:migrate原因はPostgreSQLのユーザー認証がパスワード認証になっていないことのようだった。
修正点
修正にはpg_hba.confというファイルを書き換えてパスワードを使った認証ができるように、設定する必要があるようだった。
ユーザーtaskleafはパスワード認証、元々使っていたユーザーvagrant, rootはそのままにしておきたかったので、できないか調べたところ、pg_hba.confは上方にある記述が優先されるとの記載があった。
接続形式、クライアントアドレス、要求されたデータベース、およびユーザ名に一致する最初のレコードが認証処理に使用されます。
以下のようにmd5(パスワード認証にするための設定)の行を追加、
vagrant, rootユーザーは元のとおりpeerに設定して意図どおり修正できた。
修正前pb_hba.conf# listen on a non-local interface via the listen_addresses # configuration parameter, or via the -i or -h command line switches. local all all peer修正後
pb_hba.conf# listen on a non-local interface via the listen_addresses # configuration parameter, or via the -i or -h command line switches. local all vagrant peer local all root peer local all all md5
- 投稿日:2021-01-24T17:58:46+09:00
RailsにおけるSQLインジェクション対策について - Rails Tutorial リスト13.46
はじめに
Rails Tutorialを進めていく中で気になったところを記事にして残してます。
記事に間違いがある場合は教えてください
用語解説
SQLインジェクションとは
SQLインジェクションは、Webアプリケーションのパラメータを操作してデータベースクエリに影響を与えることを目的とした攻撃手法です。SQLインジェクションは、認証をバイパスする目的でよく使われます。他にも、データを操作したり任意のデータを読み出したりする目的にも使われます。
つまり、ログインフォームや投稿フォームなどでSQLのデータを処理する際に、不正(サーバーからみると正常)なデータが実行されてしまうことです。
これによってログイン偽装やデータの抜き取りが起こります。エスケープ処理とは
「'」「"」「NULL」「改行」などのSQL文において都合の悪い文字を使えなくすることです。
悪い例
不正に認証が通ってしまう
以下のコードでログイン処理をするとしましょう。
User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")一見問題はなさそうに見えますが、もし以下のパラメータが入っていたとします。
params = { name: "' OR '1'='1", password: "' OR '2'>'1" }これを実行すると以下のようなSQL文が呼ばれることになります。
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1要約すると
1=1
,2>1
が成り立つ時usersから1人取り出してくださいということです。
ログインとしての機能を果たさなくなってしまいました。原因
こうなってしまう原因はエスケープ処理をしていないからです。
対策
対策としてSQL文を書くときには直接文字列を代入するのではなく、必ずエスケープ処理をするようにしましょう。
エスケープ処理には以下の方法があります。
- 配列、ハッシュとして渡す。(モデルのインスタンスのみ)
- sanitize_sql()を使う。(それ以外)
対策1
モデルの場合はこちらが楽です。
# 配列 Model.where("login = ? AND password = ?", entered_user_name, entered_password).first # もしくはハッシュ Model.where(login: entered_user_name, password: entered_password).first対策2
sanitize_sqlというメソッドでエスケープできます。
# 3つの例 sanitize_sql(["name=? and group_id=?", "foo'bar", 4]) sanitize_sql(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4]) sanitize_sql("name='foo''bar' and group_id='4'")参照
Rails セキュリティガイド - Railsガイド
ActiveRecord::Sanitization::ClassMethods - Rails API
- 投稿日:2021-01-24T16:50:53+09:00
bundle exec
- 投稿日:2021-01-24T16:31:14+09:00
refileで画像を複数投稿したい!
今回はrefileで画像を複数投稿できる機能を作ります。
調べたところ、画像複数投稿には gem 'carrierwave'を使用する記事がたくさん出てきましたが、うまくいかなかったので今回はrefileで実装してみました。画像投稿用のテーブルを作成
画像1枚だけの投稿だと、対象のモデルにimageカラムを追加するだけでいいのですが、複数枚となると別でテーブルを作成する必要があります。
私はpet_imageモデルを新規作成しました。
create_table "pet_images", force: :cascade do |t| t.integer "pet_id" t.string "image_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["pet_id"], name: "index_pet_images_on_pet_id" endリレーションを組む
1:Nの関係性を持たせます。
pet.rbhas_many :pet_images, dependent: :destroy accepts_attachments_for :pet_images, attachment: :imagepet_image.rbbelongs_to :pet attachment :imageコントローラーを編集
pet.controller.rbprivate def pet_params params.require(:pet).permit(:name, :birthday, :gender, :introduction, :genre_id, :prefecture_id, :age, :is_active, :image, pet_images_images:[] ) end新規投稿フォーム編集
pet.new.html<%= form_with model: @pet, url: pets_path, method: :post, local: true do |f| %> <%= f.attachment_field :pet_images_images, multiple: true %> <% end %>ペット一覧には画像を一枚だけ表示させたいので以下のように記述
pet.index.html<% pet.pet_images.first(1).each do |image| %> <%= attachment_image_tag image, :image, size: '200x200' %> <% end %>画像を複数枚表示させたいところには↓
pet.show.html<script type="text/javascript" src="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script> <div id="slider"> <% @pet.pet_images.each do |image| %> <%= attachment_image_tag image, :image, size: '350x350', class: "rounded mt-4" %> <% end %> </div>プラスでスライドショーにする
application.html<head> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.css"/> </head>application.js$(function() { $('#slider').slick({ dots: true, autoplay: true, autoplaySpeed: 4000, }); });application.css.slick-next { right: 10px z-index: 100; } .slick-prev { left: 10px z-index: 100; }以上です!!
- 投稿日:2021-01-24T15:57:57+09:00
Ubuntu上で作ったRails環境をVSCodeで書けるようにする(Windows)
はじめに
今回はUbuntu上に構築したRails環境を
VSCode上でコードなどを書くことができる手順となります。
Ubuntu上にRails環境を構築する方法は以下の記事をご参照ください。
Ubuntu Rails Widowsで環境構築をしたよ(2021年版)
VSCodeについては以下のリンクからダウンロードできます。
VscodeVScodeのセットアップ
拡張機能>入力欄に「Remote WSL」を入力。インストールします。
次にメニューバーから
ファイル>ユーザ設定>設定>設定(JSON)を開きます。
設定の中に以下のコマンドを入力
VSCode// デフォルトの起動するターミナルをbashにする "terminal.integrated.shell.windows": "C:\\WINDOWS\\Sysnative\\bash.exe",入力したら、一度VSCodeを再起動させると左下に以下のマークが出ると思います。
このマークを押すとフォルダーを選択できると思うので「Remote-WSL:Open Folder 」を選択し
開発用のフォルダを選択します。
最後にメニューバーのターミナルを実行させて以下の画面が出れば無事終了です。
お疲れ様でした!
- 投稿日:2021-01-24T12:32:06+09:00
Rspecでテストを作成する時、毎回 require 'rails_helper' と書くのを省略する
環境
Mac OS X
Ruby: 2.7.1
Rails: 6.0.3.4課題
RSpecでテストを作成する時、何も設定しないと以下のように記述すると思う
model_spec.rbrequire 'rails_helper' Rspec.describe Model, type: :model do ~以下テスト~しかしrequire 'rails_helper'と毎回記述するのは面倒くさいので省略できないか調査した
解決法
.rspecに以下のコードを追加する
.rspec--require rails_helper結果
以下のようにしてもテストが動き、コードを削減することができた
model_spec.rbRspec.describe Model, type: :model do ~以下テスト~参考記事
https://qiita.com/yuta-ushijima/items/ffb34823b8bba2180c94
最後に
お読みいただきありがとうございました。
- 投稿日:2021-01-24T12:32:06+09:00
Rspecでテストを作成する時、各ファイル毎に require 'rails_helper' と書くのを省略する
環境
Mac OS X
Ruby: 2.7.1
Rails: 6.0.3.4課題
RSpecでテストを作成する時、何も設定しないと以下のように記述すると思う
model_spec.rbrequire 'rails_helper' Rspec.describe Model, type: :model do ~以下テスト~しかしrequire 'rails_helper'と毎回記述するのは面倒くさいので省略できないか調査した
解決法
.rspecに以下のコードを追加する
.rspec--require rails_helper結果
以下のようにしてもテストが動き、コードを削減することができた
model_spec.rbRspec.describe Model, type: :model do ~以下テスト~参考記事
https://qiita.com/yuta-ushijima/items/ffb34823b8bba2180c94
最後に
お読みいただきありがとうございました。
- 投稿日:2021-01-24T11:27:19+09:00
[Rails + jQuery]初学者向けAjaxを取り敢えず飛ばしてみたい
課題
初学者が感覚掴むために取り敢えずAjax飛ばしてみたい。
例として以下フォームで入力された文字をサーバに送ってみる。
form.erb<%= text_field_tag :myname %>output<input type="text" name="myname" id="myname">結論
以下コードで取り敢えず飛ぶ。
ajax.js$("#myname").change(function(){ let name = $(this).val(); $.ajax({ type: 'GET', url: "/api/v1/users", data: { name: name }, dataType: 'json' }) .done(function (response) { console.log(response) } .fail(function (){ }); })受け口のRouteやControllerは一般的なRailsの範囲なので割愛します。
参考情報
Ruby on RailsのAjax処理のおさらい
https://qiita.com/ka215/items/dfa602f1ccc652cf2888
- 投稿日:2021-01-24T10:01:10+09:00
(備忘録)Everyday Rails 第2章「RSpecのセットアップ」
「Everyday Rails - RSpecによるRailsテスト入門」
Railsを使用したアプリケーションを作成したものの、テストの記述が全然分からない、という状態になったので、RSpecの勉強をできる教材を探していたところ、なんとプロを目指す人のためのRuby入門 (チェリー本)の著者が翻訳を手がけた本を発見しました。
こちらのサイトから購入できます!
販売方法はなんと、購入金額を自由に設定できるという、画期的なシステムでした(最低購入価格は$19からですが)。
先日、なんとか1周は終わらせたのですが、全然理解できていない部分が多いので、復習がてらアウトプットしていきたいと思います!
第1章 「イントロダクション」
第1章はイントロダクションということで、この本のアウトラインや著者の想いなどが書かれています。
著者の思い
その中で、著者の考える基本的な信条というものがあり、
・テストは信頼できるものであること
・テストは簡単に書けること
・テストは簡単に理解できること(今日も将来も)と書かれており、また
・とはいえ結局、一番大事なことはテストが存在することです。信頼性が高く、理解しやすいテストが書いてあることが大事な出発点になります。
とありました。
テストはどちらかと言うと、とっつきにくいイメージがあったので、学習することから逃げていた節もあったのですが、心を入れ替えてしっかりと取り入れていきたいと思います!サンプルコードについて
GitHubからサンプルコードを入手できます。
第2章 「RSpecのセットアップ」
早速、本に従って進めていきたいところですが、いきなり、「テストスイート」という聞いたこともない単語に出くわしました。
スルーしてもいいのですが、最初が肝心なので、調べてみました。
テストスイートとは、ソフトウェアテストの目的や対象ごとに複数のテストケースをまとめたもの。自動化テストにおいては、テストの実行単位となる。
ソフトウェアテスト(動的テスト)の最小単位はテストケースといえるが、実際にテスト作業ではいくつものテストケースを組み合わせることによって不具合をあぶり出したり、確率的に十分なテストを行ったという結論を出したりする。このようなテスト目的やテスト対象に応じて、多数のテストケースを束ねたものをテストスイートという。らしいです。
僕は知りませんでしたので、勉強になりました。Gemfileのインストール
まずはRSpecをインストールする必要があります。
Gemfilegroup :development, :test do gem 'rspec-rails', '~> 3.6.0' end開発環境とテスト環境の両方で、rspec-railsを読み込みますが、本番環境では読み込みません。
テストデータベースの作成
config/database.ymltest: <<: *default database: db/test.sqlite3接続可能なデータベースの作成を行います。
$ bin/rails db:create:all
RSpecの設定
続いて、RSpecのインストールを行います。
$ bin/rails generate rspec:install
インストール時に作成された
.rspec
ファイルを以下のように変更すると、RSpecの出力がドキュメント形式になるので、非常に読みやすくなります。.rspec--require spec_helper --format documentationrspec binstubで起動時間の短縮を図る
Gemfilegroup :development do gem 'spring-commands-rspec' end
bundle install
後、新しいbinstubを作成します。
$ bundle exec spring binstub rspec
RSpecを実行してみる
まだテストファイルは一個も作成していませんが、RSpecのインストールを確認する上でも、以下のコードで一度RSpecを起動してみるのがいいと思います。
$ bin/rspec
ジェネレータの設定
今後、アプリを作成していく中で、
$ rails g
コマンドを使用してコードを追加する際に、RSpec用のテストファイルも同時作成できるように設定を変更します。config/application.rbrequire_relative 'boot' require 'rails/all' Bundler.require(*Rails.groups) module Projects class Application < Rails::Application config.load_defaults 5.1 config.generators do |g| g.test_framework :rspec, fixtures: false, view_specs: false, helper_specs: false, routing_specs: false end end end
fixtures: false
テストデータベースにレコードを作成するファイルの作成をスキップ
view_specs: false
ビュースペックを作成しないことを指定
helper_specs: false
ヘルパーファイル用のスペックを作成しないことを指定
routing_specs: false
config/routes.rb
用のスペックファイルの作成を省略まとめ
これでRSpecのテスト実行するためのセットアップが整ったことになります。
色々と設定があって、大変ですね。
本によると、既存のtestディレクトリがあった場合は、rails test
コマンドでテストの有無を確認し、必要ならRSpecへの移行を検討するべき、とのことでした。
あくまで準備段階が終わっただけですが、一苦労でした。
- 投稿日:2021-01-24T09:55:43+09:00
Ruby on Railsでアプリを作ってみよう④
ビュー
MVCの役割の一つでブラウザにレスポンスとして見た目を返す役割を持っています。
ブラウザに表示するのはHTMLで書かれた物ですが、Rubyの記述も使って表現したい物です。
しかしそのままHTMLの文法で書いてしまうと問題があるので特別な書き方をします。ERB(Embedded Ruby)
テンプレートエンジンと呼ばれる仕組みを持った物で
HTMLの中にRubyを埋め込むことができます。
ERBファイルはファイル名.html.erbとhtmlの拡張子に追加してerbを記入することで作れます。
中身の記述をする時に
<%= %>というタグでRubyのコードを挟むことで埋め込んで表示することができます。
埋め込んだコードをブラウザに表示する必要のない場合(条件式など)は<% %> のタグを使います。コントローラーにインスタンス変数を定義することで
アクションに対応するビューファイルでその値を使うことができます。#コントローラーのファイル class PostsController < ApplicationController def index @post = "こんにちは" #インスタンス変数@postを定義 end endビュー入るでアクションに定義したインスタンス変数を使ってみましょう。
#ビューファイル <%= @post %> #こんにちは と表示される#ビューファイル <% @post %> #何も表示されない以上です。
- 投稿日:2021-01-24T03:58:05+09:00
Rails deviseの使い方
devise(超初心者の備忘録)
どうも、はじめまして!yukkyです。今回が初投稿でQiitaの書き方もよくわかっていないですが記録を残さないとすぐ忘れてしまうため、頑張って書いていきたいと思います。題名にも書きましたが自分は超がつくレベルの初心者ですので間違えがあったら(多分いっぱいあります。。)コメントで教えてくれると嬉しいです。さっき調べてみたら初めてvisual studioを入れた日(=初めてプログラムに触れた日)が2020/9/9でした(-_-;)まだ4.5か月ほどしかしてないですね。。
deviseでできること
ログイン機能が使えるようになる
使い方
まず練習用アプリとしてlogin_appを作る
rails new login_appgemにdeviseを追加
Gemfilegem 'devise'bandle install rails g devise:installすると以下の画面が出てくれば成功
Depending on your application's configuration some manual setup may be required: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. * Required for all applications. * 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" * Not required for API-only Applications * 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> * Not required for API-only Applications * 4. You can copy Devise views (for customization) to your app by running: rails g devise:views * Not required * ===============================================================================何やらセットアップをしなきゃいけないらしい(1番をしてなくて後で痛い目にあいました..)
1 config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }をconfig\environments\development.rbに追加2 ここは後でhomeコントローラを作るとき自動でやってくれるので大丈夫
3
<%= notice %>
<%= alert %>
これをつけるとポップアップが出るよ4 後でやるので大丈夫
rails g devise Userでdevise用のユーザーモデルが作れる。今回は名前でログインしたいのでmigrateする前にnameカラムを作る
**_devise_create_users.rb(**には日付が自動で入る)t.string :nameを追記。その後migrate
rails db:migrate新規登録フォーム、ログインフォーム作成
rails g devise:viewsこれでviewが出来る。
新規登録フォームのviewはapp\views\devise\registrations\new.html.erb
ログインフォームのviewはapp\views\devise\sessions\new.html.erb
中身を見るとデフォルトではメアドでログインになっているので名前に変更。
(VSCcodeを使っている方は、cmd(ctrl) + p
を押すとファイル名で検索できます.)
(VSCcodeを使っている方は、cmd(ctrl) + f
を押すと文字列で検索できます)app\views\devise\sessions\new.html.erb<h2>Log in</h2> <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <!-- ここから追加 --!> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name, autofocus: true, autocomplete: "name" %> </div> <!-- ここまで追加 --!> <!-- ここから削除 --!> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <!-- ここまで削除 --!>さらに新規登録画面でも名前を追加(注:さっきは変更だったけど今度は追加)
app\views\devise\registrations\new.html.erb<h2>Sign up</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <!-- ここから追加 --!> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name, autofocus: true, autocomplete: "name" %> </div> <!-- ここまで追加 --!>を追加。これだとまだ名前を送れないので新規作成(singn up)名前の情報を送ることを許可する設定を行う
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base <!-- ここから追加 --!> before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up,keys:[:name]) end endさらにログイン(sign in)するとき名前の情報を送ることを許可する設定を行う
config\initializers\devise.rb<!-- ここを削除 --!> # config.authentication_keys = [:email] <!-- ここを追加 --!> config.authentication_keys = [:name]しかしこれではログインしてもホームパス(Yay! You’re on Rails!のところ)に行ってしまうため画面を作る。
rails g controller home top (ログインした後行く場所)app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? <!-- ここから追加 --!> def after_sign_in_path_for(resource) home_top_path end private <!-- ここまで追加 --!> def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up,keys:[:name]) end endさらにホームトップにログアウトボタンを作る。
app\views\home\top.html.erb<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>これで一通り機能が完成!ありがとうございました。
- 投稿日:2021-01-24T02:10:47+09:00
ActiveRecordのwhere notの挙動について
はじめに
普段、RailsはActiveRecord使っているんですが、条件を複数書いたときに、
where.not()の挙動が直感と反しており、なかなかバグに気づかなかったので、共有します。環境
Ruby : 2.7.1
Rails : 6.0.2
Posgresql : 13.1where.notの挙動
まず、where.not()の単数条件で名字が山田以外のuserを取得したい時を考えてみます。
発行されるSQLは以下のようになります。User.where.not(last_name: '山田') => SELECT "users".* FROM "users" WHERE "users"."last_name" != $1 [["last_name", "山田"]]これは期待通りに
last name
が山田でないuserを取得してくれます。では次に「名字が山田」かつ「名前が太郎」以外のuserを取得するとしましょう。
僕の直感では以下のような記述で取得できると思ってましたが...User.where.not(last_name: '山田', first_name: '太郎') => SELECT "users".* FROM "users" WHERE "users"."last_name" != $1 AND "users"."first_name" != $2 [["last_name", "山田"], ["first_name", "太郎"]]上記、SQLを見てみると、「山田ではない」かつ「太郎ではない」というSQLが発行されています。でも欲しかったのは
「名字が山田」かつ「名前が太郎」
ではない userです。集合記号を使うと以下のようにかけます。\overline{ 山田 \cap 太郎} \neq \overline {山田} \cap \overline {太郎}
取得したいuserは赤と青の共通部分(山田太郎)以外の箇所です。
しかし、発行されるSQLで取得しているのは、赤と青以外、すなわち白の部分のuserです。
悲しいかな、「山田孝之」や「麻生太郎」は取得してくれません。解決
では、山田孝之や麻生太郎を取得するにはどうすれば良いかというと、以下のようになります。
User.where.not(last_name: '山田').or(User.where.not(first_name:'太郎')) => SELECT "users".* FROM "users" WHERE ("users"."last_name" != $1 OR "users"."first_name" != $2) [["name", "山田"], ["public_id", "太郎"]]上記は、書き方的には「山田ではない」または「太郎ではない」になります。これはドモルガンの法則を用いて
\overline{A \cap B} = \overline A \cup \overline Bとなることを使っています。A={山田},B={太郎}です。
よって、山田孝之は「山田ではある」ものの「太郎ではない」ので取得することができます。もっとスマートに書きたいんじゃ!と思っておりますので、良い方法あったら教えてくだされー!
- 投稿日:2021-01-24T01:56:40+09:00
【Rails】ルーティングの基礎的な書き方【Ruby】
ルーティングの役割
railsガイド( https://railsguides.jp/routing.html )によると
Railsのルーターは受け取ったURLを認識し、適切なコントローラ内アクションやRackアプリケーションに割り当てます。
とのこと。
「route」を直訳すると「経路」ですから、リクエストの道案内役といったところでしょうか。
基礎的な書き方について記してみたいと思います。
基本の書き方
rootの指定
resourcesを用いた書き方
基本の書き方
HTTPリクエスト 'URIパターン', to: 'コントローラー名#アクション名'
もしくは
HTTPリクエスト 'URIパターン', controller: 'コントローラ名', action: 'アクション名'
Rails.application.routes.draw do get 'profile', to: 'users#show' post 'tweets', controller: 'tweets', action: 'create' endrootの指定
root(ルート)とは、Railsアプリを実行する上で基本となる場所(パス)のことです。
例えばローカル環境にてアプリを実行した時に、「 http://localhost:3000 」のURLを指定した際に遷移するページを設定することができます。
root to: 'コントローラー名#アクション名'
もしくは
root 'pages#main'
Rails.application.routes.draw do root to: 'pages#main' root 'pages#main' endresourcesを用いた書き方
resourcesメソッドを使用すると、
index, new, create, edit, update, show, delete
の七つのアクションへの割り当てを自動的に行うことができます。Rails.application.routes.draw do resources :articles endこちらは、以下の7行のルーティングと同義になります。
get '/articles', to: 'articles#index' get '/articles/:id', to: 'articles#show' get '/articles/new', to: 'articles#new get '/articles/:id/edit', to: 'articles#edit post '/articles', to: 'articles#create patch '/articles/:id', to: 'articles#update' delete '/articles/:id', to: 'articles#destroy'参考文献
https://railsguides.jp/routing.html
https://web-camp.io/magazine/archives/16815
- 投稿日:2021-01-24T01:22:16+09:00
bundle installしてもエラーが続き沼にハマる。(備忘録として)
bundle installしてもエラーが続き沼にハマる。(備忘録として)
rails newしてbundle installすると
Make sure that `gem install #### -v '"#.#.#' --source https:・・・・`のようなエラーが延々と続く。
gemを指定してinstallしてもエラーは解消されず。
ググった情報を頼りにカタカタするも、エラーは解消されず。結局解決方法は以下でした。
1rbenvでrubyを管理する
2xcodeのupdateをする1rbenvでrubyを管理する。
デフォルトのrubyを使っていたが、toolのアップデートかなにかで齟齬が出てしまった?のかもしれない。
標準rubyは一つしか入っていないためrbenvでrubyのバージョンを管理する。
基本的に開発ではrbenvを用いてrubyを管理する。
参考:https://qiita.com/hujuu/items/3d600f2b2384c145ad122xcodeのupdateをする。
これが今回最も遠いようで近いエラーの原因でした。
エラー分には
you have to ....のような文字があったがなんのことやらと無視していた。
しかしそれはtoolをupdateしてくれと言うメッセージでした。
xcodeをupdateしないとだめだった。
bundle installでめっちゃ変更が多くなった。。
3k+!?gitignoreに
/vendor/bundle/*/
vendor配下のbundleを追加してあげることで解決
- 投稿日:2021-01-24T01:06:29+09:00
パーシャルで繰り返し処理
教えていただいたことの備忘録として残します。
bar.html.erb<%= render partial: 'foo', hoges: @hoges %>_foo.html.erb<% hoges.each do |hoge| %> <% hoge.name %> <% hoge.price %> <% end %>これでも動作確認はできましたが、以下のようにします。
bar.html.erb<%= render partial: 'foo', collection: @hoges, as: 'hoge' %>_foo.html.erb<% hoge.name %> <% hoge.price %>こうすることで読み込み速度が向上します。
collectionオプションを使うと1回しか読み込まれないみたいです。
- 投稿日:2021-01-24T00:27:24+09:00
RSpecでJavaScript等で挿入されるiframe内のコンテンツを確認する
概要
RSpecでテストを書いている時に、Twitter、Youtube等のリンクを貼り付けた時に、JavaScript等で時間差で挿入されるiframe内のコンテンツを確認する際に詰まったのでメモ。
方法
以下の通り。
# iframeがあるか確認。この場合、挿入されるまでデフォルトのwait timeだけ待ってくれる expect(page).to have_selector 'iframe' # iframe内のコンテンツを確認 within_frame find('iframe') do expect(page).to have_content 'コンテンツ' endデフォルトのwait time内で挿入されない場合は以下の様に待ち時間を指定する。
expect(page).to have_selector 'iframe', wait: 51行目はチェックする必要がなければ
until
でJavaScriptでiframeが挿入されるまで待ってもいい。