今日のコーディング規約 インデント
- 1レベルのインデントに2つの空白を使用する。水平タブは使用不可!!!!!(めっちゃ使ってた!!)
- 式の途中で改行する場合は、2行目以降を1行目より1段深くインデントすること。
# good
User.active.
some_scope(foo).
other_scope(bar)
# bad
User.active.
some_scope(foo).
other_scope(bar)
→badでインデントを1段深くすればgoodになる。
- 長いメソッドチェインの最後のメソッド呼び出しでブロックを渡す場合、
最後のメソッド呼び出しのレシーバをローカル変数として抽出し、
ブロック付きメソッド呼び出しを独立した式として書く。
# good
posts = Post.joins(:user).
merge(User.paid).
where(created_at: target_date)
posts.each do |post|
#ここが最後のブロック
next if staff_ids.include?(post.user_id)
comment_count += post.comments.size
end
# bad
posts = Post.joins(:user).
merge(User.paid).
where(created_at: target_date).each do |post|
#ここが最後のブロック
next if staff_ids.include?(post.user_id)
comment_count += post.comments.size
end
* レシーバ:メソッドが呼び出される対象となるオブジェクトのこと。ここでは「posts」が該当する。
→ posts をローカル変数として抽出し、ブロック付きメソッド呼び出しを独立した式として書く。
(つまりgoodの状態)
badの状態は、長いメソッドチェインであるにも関わらず、ローカル変数として抽出せず、そのままチェインしているところがよくない。
(ここからは補足)
※1 joinsメソッド:Railsのメソッド。Rubyのメソッドではない。「モデル.joins(条件...)」で使用する。複数のテーブルを結合して値を取得する。ここでは、postsテーブルにusersテーブルを結合した結果をpostsに代入している。テーブル結合には内部結合と外部結合があり、joinsメソッドは内部結合が行われる。
内部結合とは、テーブル同士を結合するときに両方のテーブルで結合条件がマッチするレコードのみを取得する結合方法。条件にマッチしないレコードは削除される。SQLで書くと以下の通り。
SELECT カラム名 FROM テーブル名
INNER JOIN 結合するテーブル名
on 結合条件
具体例で表すと、postsテーブルのカラム(例えばuser_id)が外部キーとなって対応するusersテーブルのレコードを参照する。
select * from posts
inner join users
on users.id = posts.user_id
joinsの引数には関連名を入れる。今回の場合はpostsテーブルはbelongs_toの関係だから、引数は単数形を入れる。
※2 mergeメソッド:ハッシュクラスのインスタンスメソッド。インスタンスメソッドとは、インスタンスオジェクトから実行可能なメソッド。インスタンスオブジェクトは、設計図と実物体でいうところの実物体。今回のPost.joins(:user)は設計図ではなく実物体だからインスタンスオブジェクトであり、mergeメソッドはインスタンスメソッド。
mergeメソッドはselfとothersのハッシュの内容を順番にマージした結果を返す。selfとothersに同じキーがあった場合はブロック付きか否かで判定方法が変わる。今回のようにブロック付きでない場合は常にothersの値を使う。
※3 next if:ループ処理で条件式がtrueになった場合は処理をスキップする。今回でいうと、staff_ids.include?(post.user_id)、つまりstaff_idsがpost.user_idを含んでいたら、処理をスキップし、含んでいないときは、if文の中の処理を実行する。
Rails アソシエーションについて
勉強記録です。詳しくは下記参考文献を読んでください。
(参考文献)https://pikawaka.com/rails/association
アソシエーションとは、テーブル同士の関連付け(リレーションシップ)をモデル上の関係として操作できるようにする仕組みのこと。
例えば、postsテーブルのuser_idカラム(外部キー制約)にusersテーブルの主キー(一意の値)を格納することで、どのポストとユーザーが紐づいているかが分かる。
このようなテーブル同士の関連付けでアソシエーションなしで「ユーザー1のポスト」を取得するには、次のようにpostsテーブルのuser_idカラムを使う必要がある。
しかし、アソシエーションを各モデルに定義することで、直感的なコードによるデータの取得が可能になる。
# アソシエーションを使用しない場合
@user = User.find(1)
@posts = Post.where(user_id: @user.id)
# アソシエーションを使用する場合
# ①各モデルにアソシエーションを定義する
# post.rb
class Post < ActiveRecord::Base
belongs_to :user #belongs_toメソッドの引数の関連名は「単数形」になることに注意!!!!!!!!!
end
#user.rb
class User < ActiveRecorc::Base
has_many :posts # has_manyメソッドの引数の関連名は「複数形」になることに注意!!!!!
end
# ②アソシエーションを使用して記述
@user = Uset.find(1)
@posts = @user.posts # 関連名を書く。今回は1ユーザーは複数ポストを持つというhas_manyの関係性なので、返り値は複数のポストが存在する。関連名も複数形になる。
- belongs_toメソッド:参照元テーブル(外部キーを持つテーブル(今回の場合はpostsテーブル))から参照先テーブル(主キーを持つテーブル(今回はusersテーブル))にアクセスする場合に定義する。
- has_manyメソッド:belongs_toメソッドの逆。
上記の例において、@postsの中身を1つずつ取り出す場合は、eachを使用する。
<h2>ユーザー名:<%= @user.name %> </h2>
<ul>
<% @user.posts.each do |post| %>
<li><%= post.body %><li>
<% end %>
補足 中間テーブルについて
上記の例は1対多の状態だったが、多対多の状態になることももちろんある。
その時は中間テーブルが必要になる。
中間テーブルを用意しなくても実装は可能だが、カラムが大量に必要になるケースがある。また、全レコードにカラムが必要というわけでもなく、NULLになるカラムも出てくる。(アンチパターンとされている。)
多対多の関連の定義は、モデルクラスにhas_manyメソッドのthroughオプションを使用する。
class モデル名 < ActiveRecord::Base
has_many :関連名, through: :中間テーブル名
has_many :中間テーブル名 # おまじない
end
例)部署テーブルとメンバーテーブルを結合するとき
class User < ActiveRecord::Base
has_many :departments, through: :authorizations
has_many :authorizations # おまじない
end
class Department < ActiveRecord::Base
has_many :users, through: :authorizations
has_many :authorizations # おまじない
end
authorizationテーブルの外部キーは、usersテーブルとdepartmentsテーブルを参照するため存在する。そのため、中間テーブルであるauthorizationsテーブルに対応するAuthorizatoinモデルには、belongs_toメソッドを定義する。
class Authorization < ActiveRecord::Base
belongs_to :user # 単数形
belongs_to :department # 単数形
end