Upgrade to Pro — share decks privately, control downloads, hide ads and more …

書くときにひと呼吸おいて考えてから 書いてほしいRailsコードの書き方 / Rails programing code that I hope you will consider if you really need it

ShinkuFencer
February 23, 2023

書くときにひと呼吸おいて考えてから 書いてほしいRailsコードの書き方 / Rails programing code that I hope you will consider if you really need it

社内勉強会で喋ったスライドです。

下記資料内に登場するリンクです

Railsで with_indifferent_access を使うときは一呼吸おいて考えて欲しい - コード日進月歩
https://shinkufencer.hateblo.jp/entry/2019/08/18/000000

Rails: メールをActive Recordのコールバックで送信しないこと(翻訳)
https://techracho.bpsinc.jp/hachi8833/2019_09_12/76762

ShinkuFencer

February 23, 2023
Tweet

More Decks by ShinkuFencer

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. 1.変数への再代入
    3

    View Slide

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

    View Slide

  5. 2.可変長引数
    5

    View Slide

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

    View Slide

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

    View Slide

  8. 呼び出し元に干渉する記述
    ● まずはコード例として以下を紹介する
    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は呼び出し元なので、メソッドでの変更には干渉してほしくないと
    ころだが、この記述は干渉されてしまう

    View Slide

  9. 呼び出し元に干渉する記述
    ● いくつか回避方法はあるが、原則は再代入や不変(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}"

    View Slide

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

    View Slide

  11. 4.with_indifferent_access
    11

    View Slide

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

    View Slide

  13. 5.分岐の多いpartial
    13

    View Slide

  14. 分岐の多い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

    View Slide

  15. 分岐の多い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

    View Slide

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

    View Slide

  17. 6.before_action
    17

    View Slide

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

    View Slide

  19. 7.after_saveと後続処理
    19

    View Slide

  20. 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

    View Slide

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

    View Slide