はじめに
初心者、実務未経験者には何が足りないのかという視点で書いてみました
初心者レベルを脱したい人、他の駆け出しエンジニアと差別化したい人には役に立つ内容かと思います
いろいろ書きますが、一気に全部理解しようとはせず使えそうなものから試してみてください🔥
-
対象者
- プログラミング学習を始めてポートフォリオアプリを制作している人
- 基礎的な内容の理解ができており、問題なく動くアプリを作れる人
(ちゃんと動くアプリを作れずエラー解決に四苦八苦する人は対象外)
- プログラミング学習を始めてポートフォリオアプリを制作している人
-
内容
- 主にサーバーサイド(rubyファイル)
初めてポートフォリオアプリを作成すると、とりあえず期待通りに動くことが目標になるため、実際の現場での作法をいろいろ無視してしまうケースが多くあるかと思います。
まずは動くコードを書くことが最重要ではありますが、それだけだと現場では通用しないのも事実です
現場で教えてもらってできるようになれば問題ない気もしますが、余裕のある人は先に知っておくと良いかと思います。
さらに、知っていることと使えることは全く別ものなので、ポートフォリオアプリで1度は練習しておくと良いです
用意されたリソースだけを使ったルーティング
重要度: ★★★★★
ルーティングは基本的に7つのアクションのみを使うようにします
(index new create edit update show destroy)
Railsの生みの親DHHも推奨しています(以下参考)
https://postd.cc/how-dhh-organizes-his-rails-controllers/
例えば投稿(post)の公開ステータスを更新したい場合、
✕ : postsコントローラにupdate_statusアクションを追加
○ : post_statuses_controllerを新たに作り、updateアクションでステータス更新
# これはあまりよろしくない
resources :posts do
collection do
put :update_status
end
end
# こうするとupdateのリソースが使えて、処理がわかりやすい
resources :posts do
scope module: :posts do
resource :post_statuses, only: :update
end
end
メソッドが1つしかないコントローラがたくさんできちゃうよ。。。😿 なんて思った人もいるかと思います
大丈夫です。そのほうがわかりやすいので気にせずコントローラ作っちゃってください!
if文をスッキリ書く
重要度: ★★★★★
if文は階層構造になりやすく、深くなればなるほどわかりにくくなります。
動けば良いという意識ではなく、読みやすいことを心がけるといい感じ👍
工夫はいろいろあるので、まずは以下の内容で解決できないか考えてみましょう!
早期リターン
# ↓これはイケてない
if @post.save
redirect_to posts_path
else
render :new
end
# ↓このように書くとスッキリ
return redirect_to post_path if @post.save
render :new
三項演算子
# ↓これはイケてない
if case_a
'条件はAです'
else
'条件はAではありません'
end
# ↓このように書くとスッキリ
case_a ? '条件はAです' : '条件はAではありません'
ただし三項演算子は複雑な条件分岐や、処理を書こうとすると可読性が下がるので注意が必要です
参考
https://qiita.com/lasershow/items/160c854e4256ba596ec5
変数を条件ごとに設定
# ↓は少しイケてない
if case_a
message = '条件はAです'
else
message = '条件はAではありません'
end
# ↓こうしたほうがDRYになる
message = if case_a
'条件はAです'
else
'条件はAではありません'
end
アソシエーションが組まれたレコードの保存
重要度: ★★★★★
アソシエーションを使えば、関連するレコードの取得だけではなく、インスタンスを作成することも可能です。
例えばPostモデルのレコードとCommentモデルのレコードを同時に作成、更新するときに、片方のレコードの処理と同時にもう片方の処理もやってくれるようにします(片方だけ処理に失敗して、データ不整合になることを避ける)
# post has_many :comments
# comment belongs_to :post
# ↓は良くない
def create
post = Post.new(post_params)
comment = Comment.new(comment_params)
if post.save && comment.save # 片方だけ保存される可能性もある
# return処理
end
render :new
end
# ↓のように書く
def create
post = Post.new(post_params)
comment = post.comments.build(comment_params)
if post.save # postが保存されるとcommentも同時に保存される
# return処理
end
render :new
end
1対1、1対多などアソシエーションの種類で使うメソッドが違うので、詳しくは以下の記事を参考にしてください
https://techtechmedia.com/build-method-rails/
transaction(トランザクション)
重要度: ★★★★★
データを一度にたくさん作成または更新するときに使ったりします
(例えば、全てのユーザーに対してポイントを追加するなどの処理です)
目的はデータの不整合を無くすためです
ポイント追加の例でいくと、全ユーザーレコードを取得してeachでupdateアクションを適用するかと思いますが、何らかの不具合でエラーが出るとポイントが正常に付与されたユーザーと付与されないユーザーが出てきてしまいます
これではあとからどこで失敗したかをたどって、ポイント付与されなかったユーザーを探して更新処理を手作業でやるしかありません😨
これを防ぐには失敗したらそれまでのデータ更新をなかったことにすればOK(ロールバックと言います)で、これを実現するのがtransactionです
つまり
- 全部のデータ作成(または更新)が成功
- 1件も作成(または更新)されない
のどちらかの結果にしたいときに使えます
def update
ActiveRecord::Base.transaction do
User.all.each do |user|
user.update!(point: user.point + 100)
end
end
# 成功した場合の処理はtransactionの外に書く
# 失敗したらエラーが出る(エラーページが表示される)
# エラーを出したくない場合はrescueを使う(ただし使い時に注意)
end
例外(エラー)を吐かないとロールバックされないので、updateではなくupdate!を使います
詳しい使い方は調べてみてください
(特にrescueはむやみに使うべきではありませんのでご注意ください)
参考
https://qiita.com/huydx/items/d946970d130b7dabe7ec
モデルメソッド
重要度: ★★★★★
モデルにメソッドを生やすことで、次のようなメリットがあります🉐
- DRYな書き方ができて、ファイルの肥大化を抑えられる
- あとでロジックを変えたいときに、変更箇所を1箇所にまとめられる
- メソッドを呼び出すときに、メソッド名からどんな処理をしてる直感的にわかる
使い方は以下のようにモデルファイルにメソッドを定義し、モデルクラスのインスタンスに対してメソッドを呼び出すイメージ
例は超簡単なメソッドなので旨味は感じにくいかもしれませんが、判定の処理が複雑になったり、計算させるようなメソッドだとありがたみが分かると思います
class User < ApplicationRecord
# usersテーブルにgenderカラムがあるとすると
def male?
gender == 'male'
end
end
# 以下のように呼び出す
user = User.first
user.male?
# => true
scope
重要度: ★★★★☆
データの絞り込みをするメソッドというイメージです🔍
コントローラ内でいちいちwhereやjoinsなどを駆使してレコードを取得していると、記述量が多くなるだけでなく、どんなデータを取得しているのか一発でわかりにくくなります
# ↓このように書くといちいち条件を読まないといけない
users = User.where('age > ?', 19).where(gender: 'male')
# ↓検索条件をモデルに書いておけば以下のように書けて、スッキリしてわかりやすい
users = User.adult_male
使い方
class User < ApplicationRecord
scope :male, -> { where(gender: 'male') }
# ↓のように引数も使える
scope :age_limit, ->(limit) { where('age > ?', limit) }
# ↓組み合わせて別の名前に
scope :adult_male, -> { male.age_limit(19) }
end
User.male
# => 男性のユーザーが全件取得される
User.age_limit(19)
# => 20歳以上のユーザーが全件取得される
User.adult_male
# => 成人男性のユーザーが全件取得される(成人年齢の設定を変えたいときはscopeを変更するだけ)
参考
https://qiita.com/ozin/items/24d1b220a002004a6351
https://pikawaka.com/rails/scope
サービスクラス
重要度: ★★★★☆
目的はコントローラの記述量を減らして、見通しをよくするためです
コントローラにいろいろロジックを書くとファットコントローラになって見通しが悪くなります
データをごにょごにょする処理はサービスクラスに切り分けて処理すると良いです
(簡単な処理であればモデルにメソッドを生やすだけでOK)
# ↓のように書くとわかりにくい
def create
@post = Post.new(post_params)
if # 条件
# 処理1
# 処理2
:
:
end
return redirect_to posts_path if @post.save
render :new
end
# ↓のように書くといい感じ
def create
@post = Post.new(post_params)
CalcHogeService.new(post: @post).execute
return redirect_to posts_path if @post.save
render :new
end
# サービスクラス(例えば何かの計算)
CalcHogeService
def initialize(post:)
@post = post
end
def execute
# 処理1
# 処理2
:
:
end
end
サービスクラス内では保存に成功したらtrue
、失敗したらfalse
を返すようにして、コントローラではif
で条件分けして成功・失敗パターンの処理だけ書くともっとスッキリします
いろいろ試してみてください
使い方参考
https://qiita.com/chrischris0801/items/58a12d17a440b842db02
参考
https://qiita.com/chrischris0801/items/58a12d17a440b842db02
コントローラ継承
before_action
をコントローラ間で共通で使いたいときなんかは継承を使うと良いです
例えばマイページ内で使う各コントローラは基本的にログインしていなければ使えないので、Mypageコントローラにrequire_login
などのbefore_action
を用意しておき、各コントローラにはbefore_action
を記述しないという感じです(不要なときだけskip_before_action
で発動しないようにする)
class MypageController < ApplicationController
before_action :require_login
private
def require_login
return if user_signed_in?
redirect_to root_path
end
end
class UpdateEmailsController < MypageController
# before_actionはMypageControllerのものが呼び出される
def update
# メール更新処理
end
end
resource(ルーティング)
重要度: ★★★★☆
indexアクションがないものはidで識別する必要がないので、resourcesを使う必要がありません
代わりにresourceを使います
resourceはresourcesと比べて次のような違いがあります
- resourceはindex以外のアクションが用意される
- URLにidが含まれなくなる
例えばマイページはcurrent_userのみしか登場しないので、resourceで問題ないです
参考
https://qiita.com/Atsushi_/items/bb22ce67d14ba1abafc5
https://qiita.com/Tamitchao/items/6f45aa6daf1412b78d10
%記法
重要度: ★★★☆☆
%w
や%i
を使うと文字列やシンボルをスッキリ書けます
慣れないうちは使いにくいですが、慣れて少しでも楽しましょう
routesやbefore_actionのonlyに渡すシンボルでよく使います
['inu', 'neko', 'kuma']
[:inu, :neko, :kuma]
# 上の2つは下の2つに書き換えられます
%w[inu neko kuma]
%i[inu neko kuma]
# よく見るやつ
# routes.rb
resources :posts only: %i[index new create]
# controller
before_action :set_post, only: %i[show edit update]
参考
https://qiita.com/mogulla3/items/46bb876391be07921743
おまけ
フロントエンドでよく使うものです
知っている方からすれば当たり前ですが、僕がスクールで教わらなかった(見落としていた可能性が高い笑)ので、軽く紹介します
require
重要度: ★★★★☆
フォームにrequireをつけると、入力しないと送信ボタンを押せないようになります
バックエンドはバリデーションで未入力データを防いで、フロントではrequireを使って防ぐイメージです
data confirm
重要度: ★★★★★
送信ボタンを押したときに、確認のポップアップが表示されます
削除ボタンなんかは必須でこれをつけるべきです
例は以下のような感じです
= form_with model: @post, url: post_path, local: true do |f|
.content-form
= f.label :content
= f.email_field :content, class: 'content-field', required: true # ← これ
.submit
= f.submit '送信', data: { confirm: "送信しますか?"} # ← これ
最後に
上記の全てをポートフォリオアプリに適用する必要はないかと思いますが、1度は使ってみることをオススメします
これを利用して実はまともなコード書けますアピールをしてください笑