Slide 1

Slide 1 text

書くときにひと呼吸おいて考えてから 書いてほしいRailsコードの書き方 社内勉強会 2023/02/21 しんくう@shinkufencer

Slide 2

Slide 2 text

今回しゃべること 2 ● 個人的にいままで遭遇した経験値からRailsにおいて、多くは 避けたほうがいいであろう記述をピックアップ ● 前半はRailsというよりかはプログラミング全般として避けた ほうがよさそうなことを紹介 ● 後半はRailsの作り的に避けたほうがいいことを紹介

Slide 3

Slide 3 text

1.変数への再代入 3

Slide 4

Slide 4 text

変数への再代入 ● 一度定義した変数に再び値を入れることを再代入という ● 再代入を回避することのメリットは可読性が大きく占める ● 一度宣言した変数の中身が変わらないということは読みやすいし、他の値 が入る考慮をする必要がないので書くときにも楽 ● 「再代入をしたい!」というシーンがあるとすればパフォーマンスが問題 になるときだが、深く追うと再代入を採用せずとも改善できたりする。 ● 起きうるパターンが増えれば増えるほどバグを生まれやすいので避ける 4

Slide 5

Slide 5 text

2.可変長引数 5

Slide 6

Slide 6 text

可変長引数 ● Rubyでは可変長引数を下記のように書くことができる。 6 def all_print(*args) p args.join(" , ") end all_print("Suzuki") # "Suzuki" all_print("Suzuki","taro") # "Suzuki , taro" ● 可変長引数はメソッド側でどの程度引数が来るかが予想できないのでいろ いろなことを考慮しなければならなくなるので大変(テストも大変) ● 本当に必要なシーンは限られるので、奥の手ぐらいに捉える ● 起きうるパターンが増えれば増えるほどバグを生まれやすいので避ける

Slide 7

Slide 7 text

3.呼び出し元に干渉する記述 7

Slide 8

Slide 8 text

呼び出し元に干渉する記述 ● まずはコード例として以下を紹介する 8 class Monoire def initialize(value) @atai = value end def set_atai(value) @atai = value end def get_atai return @atai end end def change_monoire(target) target.set_atai(target.get_atai + 100) end temp = Monoire.new(100) pp "before atai = #{temp.get_atai}" change_monoire(temp) pp "after atai = #{temp.get_atai}" # 結果は以下のようになる # "before atai = 100" # "after atai = 200" ● tempは呼び出し元なので、メソッドでの変更には干渉してほしくないと ころだが、この記述は干渉されてしまう

Slide 9

Slide 9 text

呼び出し元に干渉する記述 ● いくつか回避方法はあるが、原則は再代入や不変(immutable)を意識する と回避することができる。 ● 先程の例だとMonoireクラスは生成後に中の値を変化させることができる から起き得るバグだったので、set_ataiを廃止すれば防げる 9 class Monoire def initialize(value) @atai = value end def get_atai return @atai end end def change_monoire(target) Monoire.new(target.get_atai + 100) end temp = Monoire.new(100) pp "before atai = #{temp.get_atai}" charged_object = change_monoire(temp) pp "after atai = #{temp.get_atai}" pp "charged = #{charged_object.get_atai}"

Slide 10

Slide 10 text

呼び出し元に干渉する記述 ● 今回の例は素のクラスであったが、ActiveRecordでも似たような事象を 作り出すことはできてしまう ● ActiveRecordの場合は「メソッドを超えて変更処理をしない」「引数で 渡ってきたActiveRecordは参照だけに留める」など気をつけることで想 定外の副作用でバグが発生するケースを防ぐことができる 10

Slide 11

Slide 11 text

4.with_indifferent_access 11

Slide 12

Slide 12 text

with_indifferent_access ● hash.with_indifferent_access をすると、文字列でもシンボルでもアク セスできるようになる ● 一見便利そうだが、受け取った側はどっちでも使えるので文字列アクセス とシンボルアクセスが乱立して可読性を下げる ● メソッド提供側としても、どっちを主として使ってほしいかコードから読 み解きにくくなるので、避けたほうが無難 ● 起きうるパターンが増えれば増えるほどバグを生まれやすいので避ける ● 代替として何を使うといいかなどはブログ記事を参考のこと 12 該当のブログ記事は下記 Railsで with_indifferent_access を使うときは一呼吸おいて考えて欲しい - コード日進月歩 https://shinkufencer.hateblo.jp/entry/2019/08/18/000000

Slide 13

Slide 13 text

5.分岐の多いpartial 13

Slide 14

Slide 14 text

分岐の多いpartial ● 下記のようなerbがあったとする 14 <%= render :partial => "content", :locals => { title: @content.title , body:@content.body } %>

タイトルは<%= title %>です

<%= body %>

● partialの使い方としてlocalsで値を与えてpartial側で当て込むやりかたが 機能としては存在する contents/index.html.erb contents/_content.html.erb

Slide 15

Slide 15 text

分岐の多いpartial ● 「bodyの中身がないパターンもあるのでそのときにも使いたい」という ユースケースが増えたとする 15 <%= render :partial => "content", :locals => { title: @content.title } %>

タイトルは<%= title %>です

<% if defined?(body) do %>

<%= body %>

<% else %>

本文はありません

<% end %> ● Partial側では「bodyが定義されていない」と認識できれば、値の未セッ トを検知することができるので defined? を使うと実現はできる。が… contents/no_body.html.erb contents/_content.html.erb

Slide 16

Slide 16 text

分岐の多いpartial ● partial側でどんな変数が渡ってきたかを逐一チェックしないといけないの で、「partialとして必須の値」と「必須ではない値」が一見してわかりに くくなる。 ● 使い回しを想定するので、partial側の分岐も増えるためどんどん場合わけ のケースも増えていくため、この手法はオススメしづらい。 ● 可能であれば分岐は呼び出し元で行い、partialそのものを複数作るほうが 良いと考えています。 ● 起きうるパターンが増えれば増えるほどバグを生まれやすいので避ける 16

Slide 17

Slide 17 text

6.before_action 17

Slide 18

Slide 18 text

before_action ● サンプルコードではbefore_actionでインスタンス変数セットをやること があるが、コードが肥大化すると読みづらくなるケースのほうが多いので オススメしないです。 ● before_actionなど、コールバックメソッドは見極めて使うと良い ● 詳しくはKaigi on Rails 2021の発表を参考のこと 18 https://speakerdeck.com/shinkufencer/how-to-using-before-action-with-happy-in-rails

Slide 19

Slide 19 text

7.after_saveと後続処理 19

Slide 20

Slide 20 text

after_saveと後続処理 ● 「記事が更新されたらメールを送信する」というような処理のときに、記 事のActiveRecordのafter_saveでメール送信処理を書く、としたくなる ときがある ● after_saveのときを使えば便利そうに見えるが、saveのときにはなんで もかんでも発火してしまうので後で苦しむことが多い ● 例えば「Railsコンソールで直接修正をかける」や「関連モデルの保存の兼 ね合いで一度saveをする」などでも発火してしまうので予期せぬことがお きる ● after_saveに後続処理を書きたくなったら、本当に毎回必須でやる処理な のかを見直すと良い。 20 詳しくは下記記事で説明されているので参考のこと Rails: メールをActive Recordのコールバックで送信しないこと(翻訳) https://techracho.bpsinc.jp/hachi8833/2019_09_12/76762

Slide 21

Slide 21 text

まとめ 21 ● 多くの話は「複雑さ」を減らして、使い方や使われ方をシン プルに保つということが大事。 ● メソッドや機能として使い方のバリエーションが多いと、連 動してメソッドの振る舞いも発生するケースが増えて考慮す ることが増える。 ● その結果として「分岐が増える」「想定外のルートが増え る」「バグが増えやすい状態になる」という連鎖が起きる。 ● Railsは使い方次第で「複雑さ」が簡単に増えるので気をつけ る。