pメソッド、putsメソッド、printメソッドの違い
改行の有無
pメソッド
p 'これは' p 'pメソッド'
結果
"これは" "pメソッド"
putsメソッド
puts 'これは' puts 'putsメソッド'
結果
これは putsメソッド
printメソッド
print 'これは' print 'printメソッド'
結果
これはprintメソッド
まとめると、
- 改行有り
・pメソッド
・putsメソッド
- 改行無し
・printメソッド
になります。
ここで気になるところが、、、
pメソッドで出力した際にダブルクォーテーションが付いたままになっています。
次の章で説明します!
pメソッドにおける型情報の出力
上記のようにpメソッドで出力した際にダブルクォーテーションが付いているのは、
型情報も一緒に出力しているからです。
つまり、ダブルクォーテーションで囲むことで文字列であることを表しています。
数値だとこれはついてきません。
pメソッド
p 'これはpメソッド' p 20200625
結果
"これはpメソッド" 20200625
変数の扱い
これについてはあまり差異は無いかと思いますが、
強いて挙げるなら、上記で説明したようにpメソッドでは型情報も出力されることでしょうか・・・。
今回は一気に見ていきましょう。
apple = 'りんご' juice = 'ジュース' print apple, juice puts apple, juice p apple, juice
結果
りんごジュースりんご ジュース "りんご" "ジュース"
上記のように、
printメソッドは改行しないのでputsメソッドで最初に出力したappleは1段目に出力されています。
また、pメソッドは型情報も出力するのでダブルクォーテーションがついています。
変数を引数に渡すだけで出力することができることはどれも同じですね!
まとめ
- pメソッド
・改行をする
・引数と引数の型情報(文字列or数値)も一緒に出力する
- putsメソッド
・改行する
・引数を出力する
- printメソッド
・改行しない
・引数を出力する
deviseにおけるパスワードの文字数変更方法
いきなり余談だが
railsによるアプリケーション開発でdeviseによる会員登録機能を搭載すると、パスワード数はデフォルトで6文字以上となっている。
どうですか?短くないですか?笑
私は、ほとんどのwebサイトのパスワードが8文字以上になっている印象を受けます。
なので自分で作ったアプリも8文字以上にしようと思い調べてみました!
と、その前に。。。
本当に8文字以上で良いのか気になったので、
まず現代で推奨されるパスワードの最低文字数について調べました。
結論から述べると、15文字以上らしい(長っ!!)
これはFBIが提言しているもので、大文字と小文字の使用や記号の使用は必須としないみたい。
ちなみに、
7文字のパスワードは、ハッキングソフトウェアを使って0.29ミリ秒で破られてしまい、これが12文字になると200年近くかかる計算。そして24文字になると1800万年以上かかるらしい。
引用元
https://www.itmedia.co.jp/news/articles/2003/02/news061.html
変更方法
本題に戻りパスワードの長さの変更方法です。
FBIは15文字以上を推奨していますが少し長すぎるので、現在の慣例である8文字以上に設定することにします。
config > initializers > devise.rb ファイル内の記述を変更することで反映されます。
コメントアウトで、パスワードの長さの範囲と書かれているところの下です。
config > initializers > devise.rb # ==> Configuration for :validatable # Range for password length. config.password_length = 6..128 ← これ
「6..128」が6文字〜128文字ということなので、
最初の数字を8にしてみました。
config > initializers > devise.rb config.password_length = 8..128
これで設定が変更されているはずですが、
念の為アプリで確かめてみました!
6文字で登録しようとしたところ、、、
ちゃんと弾いてくれました!
VSCode内でのDraw.ioの使用
Draw.ioとは
web上でフローチャートやER図を描けるアプリケーションです。
予め図形や矢印などのテンプレートが用意されているので、
あとは文字を打ち込んだり矢印で関係性を示したりするだけで済みます。
私はER図作成のために利用しました。
通っていたプログラミングスクールでチーム開発があったのですが、コロナ禍ということで完全リモートでの開発となりました。
最初は紙に描いていたのですが、チーム全員と共有する必要があった為このアプリを使用することにし、
シンプルで直感的に扱える為、簡単に作成することができました!
そのDraw.ioがVSCodeでも扱えるようになったと聞き、どんな感じなのか調べてみることにしました。
*devise使用:ログイン済みのユーザーのみをアクセス可能にする設定
遷移先へのボタンを隠すだけではダメ
大部分のアプリケーションでは会員登録機能がついており、登録してログイン済みでなければそのアプリのメイン機能を使用できないようになっているかと思います。
私が作成しているアプリでもログイン済みか否かによってナビバーの表示を変更し、ログイン済みでなければ先に進めないようにしてあります。
ログイン前↓
ログイン後↓
これはif文で分岐させることで実装できます。
deviseのヘルパーメソッドであるuser_signed_in?を用いてユーザーがログイン済みかどうかを判定します。
<% if user_signed_in? %> <li class="nav-item"> <%= link_to "#{current_user.username}さんのマイページ", user_path(current_user.id) %> </li> <li class="nav-item"> <%= link_to "助っ人を探す", maps_path %> </li> <li class="nav-item"> <%= link_to "チャット", "#" %> </li> <li class="nav-item"> <%= link_to "ログアウト", destroy_user_session_path, method: "delete" %> </li> <% else %> <li class="nav-item"> <%= link_to "会員登録", new_user_registration_path %> </li> <li class="nav-item"> <%= link_to "ログイン", new_user_session_path %> </li> <% end %>
未ログインではボタンそのものが無いのでアクセスできないと思いきや、
実はURLを直打ちすることで飛べてしまいます。
先程の私のアプリの例で、未ログインのまま "助っ人を探す" のページに飛んでみます!
直接URLを打ち込んでと。。。
飛べてしまいました。
添付画像の右上を見てもらえばわかりますが未ログインのままです。
対策をしましょう!!
before_actionで対策
当該のコントローラにおいてすべてのアクションで実行の前に共通の処理を行いたいときに、before_actionを使用すると全てのアクションが実行される前に指定したメソッドを呼び出すことができます。
つまり上記の例でいうと、
マップを表示させているmapsコントローラーはユーザーがログイン済みでないとアクションが実行されないように設定すれば良いのです。
app > controllers > maps_controller.rb
class MapsController < ApplicationController before_action :login_check # 省略 private def login_check unless user_signed_in? redirect_to root_path end end end
before_actionでlogin_checkというメソッドを定義します。
メソッドを書くのはprivate以下です。
上記のナビバーと同様にuser_signed_in?を使用します。
ユーザーがログインしてなければ "root_path"(TOPページ)にリダイレクトするようにしました。
これで未ログインのユーザーが直打ちで侵入することは防げました。
が、しかし・・・
もっと簡単な方法がありました。
deviseのヘルパーメソッドの一つに
before_action :authenticate_user!
があり、
app > controllers > maps_controller.rb
# authenticate_user!適用前 class MapsController < ApplicationController before_action :login_check # 省略 private def login_check unless user_signed_in? redirect_to root_path end end end #authenticate_user!適用後 class MapsController < ApplicationController before_action :authenticate_user! # 省略 end
上記のように1行で済みます!
会員登録にdeviseを使用していたら断然こっちですね!
ちなみに
デフォルトの設定ではログインしていない場合、ログイン画面に遷移する仕様になっています。
変更したい場合は下記のように上書きをすれば指定ページにリダイレクトされました。
app > controllers > maps_controller.rb
class MapsController < ApplicationController before_action :authenticate_user! # 省略 private def authenticate_user! redirect_to root_path end end
Capistranoによる自動デプロイ設定後に環境変数が読み込まれない件
Caspitranoでデプロイの自動化をすると、~/.bash_profileが読み込まれない
Caspitranoは自動デプロイツールの一種であり、
これを利用することによって、デプロイ時に必要なコマンド操作が1回で済むようになります。
先日個人のアプリを本番環境にデプロイ後、
アップデートを容易に行えるようにCaspitranoを導入しました。
導入後、本番環境のページを確認するとGoogleマップを表示させるページでエラーが発生。(泣)
ログを確認したところAPIキーが読み込まれていなかった。
確かに ~/.bash_profileに書いたはずなのですが。。。
色々調査をしてみると、
Caspitaranoを使用する本番環境では~/.bash_profileは読み込まれないとのこと。
対処法
Capistranoは、~/.bash_profileを読み込まないようにプログラムされているのでetc/environmentを使用します。
# 環境変数書き込み $ sudo vim /etc/environment
ここに環境変数を記述すればOKです!
この後、環境変数を反映させるために一度接続を切ってから確認します。
# 接続を切断 $ exit # EC2インスタンスに接続 $ ssh -i ... # 環境変数が設定できているかの確認 $ env | grep ...
Rspecによるテスト:外部キーが設定されている際のモデルテスト
扱うモデルについて
メッセージ投稿に関するMessageモデルを例に説明していきます!
私の作成しているアプリのmessagesテーブルは以下のようになっており、
user_idとroom_idの2つを外部キーとして設定しています。
また、空での投稿を防ぐためにcontentにバリデーションをかけています。
Column | Type | Options |
------- | ------- | ----------------- |
user_id | integer | foreign_key: true |
room_id | integer | foreign_key: true |
content | string | null: false |
image | string |
外部キーを考慮せずにテストを行うと・・・
今回は"contentとimageがあれば投稿できる"という項目でテストを行います。
factory_botを使ってテンプレートを作成しました。
factory_botの説明についてはこちら↓↓
https://dkdkdk3.hatenablog.com/entry/2020/06/07/005240
spec > factories > messages.rb
FactoryBot.define do factory :message do content {"テストです。"} image {"test.jpg"} end end
テストについては以下のように記述しました。
spec > models > message_spec.rb
require 'rails_helper' describe Message do describe '#create' do it "contentとimageがあれば投稿できる" do message = build(:message) expect(message).to be_valid end end end
この内容でテストを実行すると、
1) Message#create contentとimageがあれば投稿できる Failure/Error: expect(message).to be_valid expected #<Message id: nil, content: "テストです。", image: "test.jpg", user_id: nil, created_at: nil, updated_at: nil, room_id: nil> to be valid, but got errors: Userを入力してください, Roomを入力してください
とテストは通りません。
最下部に、"got errors: Userを入力してください, Roomを入力してください"とあります。
MessageモデルにはUserとRoomモデルを外部キーに設定しており、
これらが無いためにエラーを起こしています。
テストにおける外部キーの設定方法
Messageモデルのテスト用にUserとRoomの情報をfactory_botで作成しました。
Roomに関しては個人間でチャットをするための部屋を用意しただけなので、カラムは作成していません。
spec > factories > users.rb
FactoryBot.define do factory :user do username {"test"} email {"test@gmail.com"} password {"000000"} password_confirmation {"000000"} end end
spec > factories > rooms.rb
FactoryBot.define do factory :room do end end
外部キーを使用するための準備はできました!
テストを書いていきましょう!
spec > models > message_spec.rb
require 'rails_helper' describe Message do describe '#create' do it "contentとimageがあれば投稿できる" do user = create(:user) room = create(:room) message = build(:message, user_id: user.id, room_id: room.id) expect(message).to be_valid end end end
UserとRoomの情報をcreateメソッドであらかじめ登録します。
一時的にテスト用のDBに保存しておく必要があるためです。
*buildメソッドだとDBに登録されないのでテストは通りません。
そのあとにMessageの情報を呼び出して外部キーの設定も記述します。
*この記述でテストが終わるのでbuildメソッドで大丈夫です!
Message #create contentとimageがあれば投稿できる Finished in 1.12 seconds (files took 1.59 seconds to load) 1 example, 0 failures
これでテストが通るようになりました!
ちなみに試行錯誤している途中で、
Messageのfactory_botに外部キーの情報を記述してもテストは通りました。
spec > factories > messages.rb
FactoryBot.define do factory :message do content {"テストです。"} image {"test.jpg"} user {build(:user)} room {build(:room)} end end
テストの記述はこれです。
spec > models > message_spec.rb
require 'rails_helper' describe Message do describe '#create' do it "contentとimageがあれば投稿できる" do message = build(:message) expect(message).to be_valid end end end
こっちでもテストは通るのですが、
テストを記述するファイル(spec > models > message_spec.rb)に外部キーの情報を書いておらず、
全体像が把握できないのであまり良い書き方では無いのかなと思います。
Rspecによるテスト:factory_botによるインスタンスの生成
factory_botとは
Rspecでテストを書いていて、
一回一回対象のインスタンスを生成(User.newのところ)するのは面倒です。
こんな感じで↓↓(例はUserモデルの単体テスト)
require 'rails_helper' describe User do describe '#create' do it "usernameがないと登録できない" do user = User.new(username: "", email: "test@test.com", password: "111111", password_confirmation: "111111") user.valid? expect(user.errors[:username]).to include("が入力されていません。") end it "emailがないと登録できない" do user = User.new(username: "テストさん", email: "", password: "111111", password_confirmation: "111111") user.valid? expect(user.errors[:email]).to include("が入力されていません。") end end end
そこで便利なのが今回の主役 "factory_bot"
予めインスタンスの情報を記述しておけば、
User.newでいちいちインスタンスを生成せずに、
buildやcreateといったメソッドで簡単に呼び出すことができます。
(buildとcreateについては後述します。)
factory_botの導入
まずGemfileに以下を記述します。
Gemfile
gem 'factory_bot_rails'
bundle install を忘れずに!
次にファイルの作成です。
specディレクトリ直下にfactoriesというフォルダを作成し、
その配下にそれぞれのファイルを置いていくというのが慣例みたいです。
今回の例であればusers.rbファイルを新規作成します。
中身を書いていきます。
spec > factories > users.rb
FactoryBot.define do factory :user do username {"テストさん"} email {"test@test.com"} password {"111111"} password_confirmation {"111111"} end end
これであとは呼び出すだけです。
factory_botの呼び出し
user = FactoryBot.build(:user)
作成したインスタンスは上記の記述で生成されます。
今回はbuildメソッドですが、createメソッドでも生成されます。
createメソッドの場合は一時的にテスト用のDBにデータが保存されます。
例えば一意性の確認の時に、最初にcreateメソッドで1人目を登録しておくことで、
2人目が同じ内容を登録した時にバリデーションが働いているかを確認することができます。
そして、FactoryBotという記述を省略することができます。
そのためにはrails_helper.rbファイルに以下を記述しましょう。
このファイルはRspecをrailsにインストール後、
ターミナルにて"rails g rspec:install" で生成されるファイルの一つです。
spec > rails_helper.rb
RSpec.configure do |config| # 下記を追記 config.include FactoryBot::Syntax::Methods
最終的にはこうなります。
# factory_bot導入前 user = User.new(username: "テストさん", email: "test@test.com", password: "111111", password_confirmation: "111111") # factory_bot導入後 user = build(:user)
かなりスリムになりました!
ちなみに、特定の箇所を変更したい場合はこう記述します。
user = build(:user, username: "")
これでusernameが空のインスタンスが生成されることになります。