【Rails】renderとredirect_toはどう使い分けるの?

はじめに

動作が似ているrenderredirect_to

あまり意識せずに両者を使っていたので、それぞれの違いを調べてみました

renderとredirect_toの動き方の違い

redirect_toの方が多くの過程があり、具体的には以下のような過程の違いがあります。

・render: controller → view

・redirect_to: controller → URL → route → controller → view

renderとredirect_toの使い分け

renderはcontrollerで処理した結果を、指定したviewファイルに出力します。

redirect_toはviewファイルに出力する前に、1度ブラウザ上でURL(HTTPリクエスト)を指定し、再度controllerにリクエストを送信してから、viewファイルに出力します。

以上のことから、

データを追加、更新、削除を行うときは「redirect_to」、そうでなければ「render」という使いわけができそうです。

renderとredirect_toに関するエラー

1つのActionからは1つのViewしか返せません。 そのため、renderメソッドを2度呼び出したり、renderメソッドとredirect_toメソッドを両方呼び出すと、DoubleRenderErrorが発生します。

まとめ f:id:haru89:20200531090445p:plain:w70

「render」と「redirect_to」は動作が似ていますが、データの更新が必要な場合は「redirect_to」を、ログインや入力形式に失敗した場合など、ただエラーを表示させるだけの場合は「render」を使用する。というような使い分けができると思います。

参考

https://qiita.com/morikuma709/items/e9146465df2d8a094d78

【Ruby】ローカル変数に代入がされていなくてもエラーにならない

はじめに

Rubyはif文やunless文の場合、実際にローカル変数に代入がされていなくても、代入を行う記述が存在すればローカル変数を宣言したこととなる

以下のコードでは変数「num」が宣言されていないので、エラーが発生する

if num % 2 == 0
  message = "偶数"
end

puts message

=> undefined local variable or method `num' for main:Object (NameError)

変数「num」を宣言し、再度実行してみるとnil」が返ってきた

num = 1

if num % 2 == 0
  message = "偶数"
end

p message

=> nil

変数「num」を宣言しても、if文の内容が実行されない(変数「message」の代入が行われない)ため、エラーが発生すると思っていたが、そうではなかった

Rubyはif文やunless文の場合、そのコードが実行されなくても(変数に代入がされなくても)ローカル変数を宣言したこととなる

まとめf:id:haru89:20200524090613j:plain:w70

実際に代入がされていなくても、代入を行う記述が存在すればローカル変数を宣言したととなることがわかった。ただ、可読性を高めるためにも、if文やunless文の外で変数を代入し、変数が宣言されていることを明確にしたほうが良さそう

参考

https://qiita.com/jnchito/items/b4793d719892c8860423

【SQL】WHEREとHAVINGの違い

どちらも絞り込みを行うが、WHEREとHAVINGはなにが違うのか

→ 呼ばれるタイミングが違う

SQLが実行される順序

FROM → WHERE → GROUPBY → HAVING → SELECT → ORDERBY

呼ばれるタイミングが違うとどう変わる?

WHEREとGROUP BYを併用した場合

WHERE → GROUP BYの順でクエリが評価されるため、グループ化された結果に対しWHEREで条件を指定することができない

HAVINGとGROUP BYを利用した場合

GROUP BY → HAVINGの順でクエリが評価されるため、グループ化された結果に対しHAVINGで条件を指定することができる

SQLの実行結果の違いを確認してみる

以下の家計簿テーブルから、出金項目別出金額合計を取り出す

出金項目 出金額
食費 1000
光熱費 3000
交通費 3000
食費 2000
交通費 1000
食費 2000
交際費 0
WHEREを使ったSQL例)
SELECT 出金項目, SUM (出金額) AS 出金項目別の出金額合計 
FROM 家計簿
WHERE SUM(出金額) > 0
GROUP BY 出金項目
実行結果
-> エラー

WHEREとGROUP BYを併用した場合、WHERE → GROUP BYの順でクエリが評価される。 そのため、グループ化された結果(GROUP BY 出金項目)に対し、WHEREは使えないのでエラーとなる

HAVINGを使ったSQL例)
SELECT 出金項目, SUM (出金額) AS 出金項目別の出金額合計
FROM 家計簿
GROUP BY 出金項目
HAVING SUM(出金額) > 0
実行結果
出金項目 出金額
食費 5000
光熱費 3000
交通費 4000

HAVINGとGROUP BYを利用した場合、GROUP BY → HAVINGの順でクエリが評価される。 そのため、グループ化された結果(GROUP BY 出金項目)に対し条件を指定することが可能

まとめ f:id:haru89:20200516163445p:plain:w70

「WHERE」と「HAVING」違いは、呼ばれるタイミング(「WHERE」→「GROUP BY」→「HAVING」)にあった。 したがって「GROUP BY」が関わらなければ、同じ結果を得られる。 また、集計関数は対象のデータ数が少ないほど効率性が高まる。集計関数を行う際は、絞り込みの条件に「GROUP BY」が関わらないのであれば、HAVINGではなく、WHEREにより条件を絞り込んでから集計関数を実行することで、SQLの効率性を高めることができる。 WHEREとHAVINGの特性を理解した上で、効率の良いSQLを書くようにしたい。

参考

https://www.atmarkit.co.jp/ait/articles/0706/21/news128.html

メールアドレスの正規表現を考えてみた

メールアドレスの正規表現をネットで検索してみると、、、

f:id:haru89:20200509140025p:plain:w70短くシンプルなものから

[^\s]+@[^\s]+

f:id:haru89:20200509140055p:plain:w70このような長いものまであった。

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/

自分でもメールアドレスのルールを以下のように仮定し、正規表現を考えてみた。

メールアドレスのルール

  1. 使える文字はアルファベット大文字小文字 (a~z, A~Z) 、数字 (0~9)、ピリオド「.」、アンダースコア「_」、ハイフン「-」、アットマーク「@」

  2. @の前後に1文字以上の文字列が存在する

  3. 文字列の最初の文字は、アルファベットか数字であること

  4. @は1個

  5. @の後の最初の文字は、ピリオド(.)以外

  6. @から2文字以降から末尾の間にピリオド(.)が1つ以上存在する

これらの条件を満たすよう考えた正規表現がこちら

/^[a-zA-Z\d]+[\w\.-]*@[\w-]+\.[\w\-]+[\w\.-]*$/

それぞれ分解して意味を確認していく

① / /で囲んだ間に正規表現のパターンを記載する

② ^ 先頭を表す

③ [a-zA-Z\d] アルファベットと数字のいずれかを表す

④ + 直前の文字が1個以上であることを表す

⑤ ^[a-zA-Z\d]+ 先頭には文字列(アルファベットと数字のいずれか)が1個以上存在することを表す

⑥ \w アルファベットと数字とアンダーバー「_」を表す

⑦ * 直前の文字が0個以上存在することを表す

⑧ [\w.-]* 文字列(アルファベットと数字とアンダーバー「.」とピリオド「.」とハイフン「-」のいずれか)が0個以上存在することを表す

⑨ @[\w-]+ アットマーク「@」の後ろに文字列(アルファベットと数字とアンダーバー「_」とハイフン「-」いずれか)が1個以上存在することを表す

⑩ .[\w-]+ ピリオド「.」の後ろに文字列(アルファベットか数字かアンダーバー「_」かハイフン「-」)が1個以上存在することを表す

⑪ $ 末尾を表す

⑫ [\w.-]*$ 末尾に文字列(アルファベットか数字かアンダーバー「_」かピリオド「.」かハイフン「-」)が0個以上存在することを表す

冒頭の2つの正規表現と比べてみる

①シンプルなもの

[^\s]+@[^\s]+

②少し長め

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/

③今回自分が作ってみたもの

/^[a-zA-Z\d]+[\w\.-]*@[\w-]+\.[\w\-]+[\w\.-]*$/

3つの正規表現で共通している点は

  • 文字列の間にアットマーク「@」が1個存在すること
  • アットマーク「@」の前後に文字列が1個以上存在すること

反対に主な相違点は、先頭の文字列

①では

[^\s]
スペース以外の文字であれば良いというシンプルな表現

②では

\^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]
①の正規表現ほどではないが、英数字以外にもたくさんの記号が使える

③では

\^[a-zA-Z\d]+[\w\.-]*
先頭の文字には英数字しか使えない制限が存在する

また、②の[a-zA-Z0-9]と③[a-zA-Z\d]は少し形は違うが、意味としては全く同じ(アルファベットと数字を表す)となる

まとめf:id:haru89:20200509141621p:plain:w70

メールアドレスの正規表現は上記以外にもたくさんネット上に転がっていた。理由としては、メールアドレスの形式が共通化されていないことや、正規表現には、同じ意味でも異なった表現方法が多々存在することなどが考えられる。表現したいことに合わせてルールを厳しくしたり、逆に簡易的なものにしたり、柔軟に対応できるようにしたい。

参考

https://qiita.com/matsubo/items/3dfb1ddb6e155c44643b https://qiita.com/sakuro/items/1eaa307609ceaaf51123 https://qiita.com/jnchito/items/893c887fbf19e17d3ff9

テーブル設計をする際に気をつけたいこと

テーブル設計を試みた際に、テーブルの構造をどうすれば良いのか不明確だったため、調べつつ4つのルールにまとめてみた。

テーブル設計のルール

ルール1

テーブルはある共通の属性を持ったモノの集合であること

表1はテーブルとはいえない。表2はテーブルといえる

表1 共通の属性を持たない

列1 列2 列3 列4
食べ物 刺し身 焼き肉 そば
佐藤さん 田中さん 福田さん 塩田さん
ボーナス ゲーム 旅行 温泉

表2 共通の属性を持つ

原子番号 元素記号 元素名
1 H 水素
2 He ヘリウム
3 Li リチウム

ルール2

レコードを1行に特定できる主キーが存在すること

表3は同性同名・同年齢・同じ性別の人の区別がつかない。表4は主キーにより特定することが可能

表3 主キーが存在しない

名前 年齢 性別
鈴木一郎 25
鈴木一郎 25
田中修 30

表4 主キー(社員ID)が存在する

社員ID 名前 年齢 性別
001 鈴木一郎 25
002 鈴木一郎 25
003 田中修 30

ルール3

1つのセルに複数の値が存在してはいけない

表5は1つのセルに複数の値が存在している。表5を表6と表7に分割することで、セルの値を1つにできる

表5 1つのセルに複数の値が存在している

社員ID 名前 年齢 性別 兄弟
001 鈴木一郎 25 次郎
002 鈴木一郎 25 悦子
003 田中修 30 聡, 小春

表6 社員

社員ID 名前 年齢 性別
001 鈴木一郎 25
002 鈴木一郎 25
003 田中修 30

表7 社員・兄弟

社員ID 兄弟番号 兄弟の名前
001 1 次郎
002 1 悦子
003 1
003 2 小春

ルール4 「推移関数従属」が存在しないこと

「推移関数従属」とは、主キーから見て2段階の関数従属が存在しているもの。

表8は「推移関数従属」が存在しており、新しく業界名を追加しようとしてもできない。表8を表9と表10に分割することで、「推移関数従属」を無くすことができる。

表8 2段階の関数(推移関数従属)が存在している

企業ID 企業名 業界コード 業界名
001 A商社 001 不動産
002 B株式会社 002 電力
003 C研究所 003 医療

表9 企業テーブル(推移関数従属が存在しない)

企業ID 企業名 業界コード
001 A商社 001
002 B株式会社 002
003 C研究所 003
004 Dテクノロジー 004

表10 業界テーブル(推移関数従属が存在しない)

業界コード 業界名
001 不動産
002 電力
003 医療
004 IT

まとめ f:id:haru89:20200510113505p:plain:w70

テーブル設計をする際に気をつけるポイントがわかった。特に推移的関数従属が存在するかどうか見極め、正しくテーブルを分解したい。

参考

おうちで学べるデータベースのきほん ミック (著), 木村 明治 (著)

【Rails】nil? empty? blank? present? 比較してみた

nil? empty? blank? present? 比較表

nil? empty? blank? present?
nil true NoMethodError true false
true false NoMethodError false true
false false NoMethodError true false
0 false NoMethodError false true
123
数字
false NoMethodError false true
''
false true true false
' '
スペース
false false true false
'string'
文字列
false false false true
[]
空配列
false true true false
['dog', 'cat']
配列
false false false true
{}
ハッシュ(空)
false true true false
{:key => value}
ハッシュ
false false false true

nil?

値がnilの場合はtrue、それ以外はすべてfalseと判定する。

empty?

配列や文字列、ハッシュの値が空である場合にtrueと判定する。特徴的なのが、文字列のスペース(' ')に対してfalseと判定するところ。スペースも存在してるんだと認める女神的な?メソッド。ただし、値が空かどうか既に明白なもの(nil、true、false、数字)に対して行うとエラー発生させる厳しい側面もある。

blank?

英語「blank」の意味は「白紙、空、虚ろな」といったイメージ。そのイメージ通りfalse、空の配列、空の文字列(スペースも含む)をtrueと判定する。

present?

present?は、blank?と真逆。

まとめ f:id:haru89:20200419123533j:plain:w70

blank?とempty?ってややこしい。blank?の方が使い勝手がよさそう。empty?と違ってエラーが起きる心配も少ないし。でも意識して使い分けることができれば、コードの理解が深まる気がする。

参考

rubyの真偽判定メソッド(nil?/empty?/blank?/present?)を検証してみた結果、興味深いことがわかった - Qiita

【Ruby】mapメソッドと&とシンボルを使ってリファクタリングしてみた!

f:id:haru89:20200412235246p:plain:w200

標準入力される整数 3, 100(この順で半角スペース区切り)の値を、int型にしてそれぞれの変数に代入する!

やることはこれだけですが、リファクタリングする前と後でコードが見違えた。

リファクタリングする前

input_value = gets.split(' ')
value1 = input_value[0].to_i
value2 = input_value[1].to_i
p input_value # => ["3", "100"]
p value1 # => 3
p value2 # => 100

to_iを2回やってるのことにやや冗長さを感じる

mapメソッドでまとめてto_i

input_value = gets.split(' ').map{ |n| n.to_i }
value1 = input_value[0]
value2 = input_value[1]
p input_value # => [3, 100]
p value1 # => 3
p value2 # => 100

ちなみに以下のようにmapメソッドを使わずにやるとエラーが出ます

input_value = gets.split(' ').to_i
undefined method `to_i' for ["3", "100"]:Array (NoMethodError)

&とシンボルを使ってさらに簡潔に!

input_value = gets.split(' ').map(&:to_i)
value1 = input_value[0]
value2 = input_value[1]
p input_value # => [3, 100]
p value1 # => 3
p value2 # => 100
ただし&とシンボルを使ったこの記法は、次の条件を満たしている必要があります
  1. ブロックの引数が1つ
  2. ブロックの中で呼び出すメソッドには引数がない
  3. ブロックの中では、ブロック引数に対してメソッドを1回呼び出す以外の処理がない

まとめ

繰り返し処理をやるときはeachを使いがちだけど、ループした結果を空の配列に入れるような処理はmapメソッドを使うことで簡潔に書ける。

参考

プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発デバッグ技法まで 伊藤 淳一 技術評論社 2017-11-25 p96〜P99