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

Rails Good Parts, Bad Parts

Rails Good Parts, Bad Parts

「MedBeer -Rails開発での技術的負債との付き合い方」での発表資料です

Shinichi Maeshima

September 12, 2018
Tweet

More Decks by Shinichi Maeshima

Other Decks in Technology

Transcript

  1. Rails Good Parts, Bad
    Parts
    @willnet

    View Slide

  2. 自己紹介
    » 前島真一 aka @willnet or @netwillnet
    » ginza.rb から来ました
    » メドピアさんで技術顧問しています
    » https://github.com/willnet
    » https://twitter.com/netwillnet
    » https://blog.willnet.in

    View Slide

  3. 技術顧問として
    主に負債を減らしたり、負債の増加を防ぐために頑
    張っています
    !

    View Slide

  4. 技術顧問について詳しく知りたい方は
    こちらをどうぞ
    » Rails Developers Meetup で喋った
    » https://speakerdeck.com/willnet/ji-shu-gu-
    wen-toiudong-kifang
    » ブログ書いた
    » https://blog.willnet.in/entry/
    2018/04/09/101808

    View Slide

  5. 今日のテーマは技術的負

    !

    View Slide

  6. 負債へのアプローチは2

    View Slide

  7. できてしまった負債を減
    らす⬇

    View Slide

  8. 新規開発で負債を増やさ
    ない⬆
    !

    View Slide

  9. 今日は負債を増やさない
    話をします

    View Slide

  10. 負債の増加を防ぐにはど
    うしたらよいか

    View Slide

  11. Railsのレールに乗る

    View Slide

  12. レールに乗る、とは?
    » みんなよく言ってるけどふわっとした概念
    » レールについてきちんと説明している文章がない
    ように見える

    View Slide

  13. Railsが提供しているべんりな機能を活
    用する = レールに乗る
    ここではこういう定義とします

    View Slide

  14. Railsのべんりな機能
    » たくさんある
    » こういうメソッドないかな?と思って調べると大
    抵定義済み

    View Slide

  15. べんりな機能を知らずに
    スクラッチで実装して微
    妙な感じになるケースが
    よくある
    !

    View Slide

  16. べんりな機能の具体例
    » あまり知られていない&&知ってると便利なもの
    » 複数のお手伝い先で何回も言ってるやつ

    View Slide

  17. has_many

    View Slide

  18. みんな知ってる

    View Slide

  19. 定義することで使えるよ
    うになる機能がたくさん
    ある

    View Slide

  20. has_manyで使えるようになるものたち
    collection
    collection<<(object, ...)
    collection.delete(object, ...)
    collection.destroy(object, ...)
    collection=(objects)
    collection_singular_ids
    collection_singular_ids=(ids)
    collection.clear

    View Slide

  21. has_manyで使えるようになるものたち
    collection.empty?
    collection.size
    collection.find(...)
    collection.where(...)
    collection.exists?(...)
    collection.build(attributes = {}, ...)
    collection.create(attributes = {})
    collection.create!(attributes = {})
    collection.reload

    View Slide

  22. 全種類知ってますか?

    View Slide

  23. collection=(objects),
    collection_singular_ids=(ids)
    » 既存のcollectionをいい感じに置き換える
    » has_many through の場合は中間テーブルを置き
    換える

    View Slide

  24. 合わせ技でつかえるview helper
    » collection_check_boxes
    これらをスクラッチで書いてしまう人めっちゃ多い
    (当社比)

    View Slide

  25. コード例
    ビールの銘柄にタグ付けをするコードを考えてみま

    View Slide

  26. View Slide

  27. modelはこんな感じ
    class Beer < ApplicationRecord
    has_many :taggings
    has_many :tags, through: :taggings
    end

    View Slide

  28. 素直に書くと
    <% Tag.all.each do |tag| %>
    <%= form.label tag.name do %>
    <%= check_box_tag 'beer[tag][id][]', tag.id,
    beer.tags.find { |t| tag.id == t.id } %>
    <%= tag.name %>
    <% end %>
    <% end %>

    View Slide

  29. 素直に書くと
    def update
    @beer = Beer.find(params[:id])
    @beer.attributes = beer_params
    original_taggings = @beer.taggings
    taggings_ids = params[:beer][:tag][:id]
    delete_taggings = original_taggings.reject { |bt| taggings_ids.include?(bt.tag_id.to_s) }
    delete_taggings.each(&:destroy)
    add_tags = taggings_ids.reject { |id| original_taggings.map(&:tag_id).include?(id.to_i) }
    add_tags.each do |tag_id|
    @beer.taggings.build(tag_id: tag_id)
    end
    if @beer.save
    redirect_to @beer, notice: 'Beer was successfully updated.'
    else
    render :edit
    end
    end
    private
    def beer_params
    params.require(:beer).permit(:name)
    end

    View Slide

  30. 一瞬では読めないです
    ね…
    !

    View Slide

  31. 既存のタグと入力値との
    差分を見て、必要なもの
    だけinsert, deleteして
    いる

    View Slide

  32. 読みづらいしバグも入り
    込みやすそう

    View Slide

  33. 便利メソッドたちを使うと
    <%= form.collection_check_boxes :tag_ids,
    Tag.all, :id, :name %>

    View Slide

  34. 便利メソッドたちを使うと
    def update
    @beer = Beer.find(params[:id])
    if @beer.update(beer_params)
    redirect_to @beer, notice: 'Beer was successfully updated.'
    else
    render :edit
    end
    end
    private
    def beer_params
    params.require(:beer).permit(:name, tag_ids: [])
    end

    View Slide

  35. 普通に書ける
    beer.tag_ids = params[:beer][:tag_id]
    » has_many :tagsでtag_ids=メソッドが生えてい

    » いい感じに差分を見て中間テーブルをinsert,
    deleteしてくれる

    View Slide

  36. これ、初めて知った人

    View Slide

  37. どうやってべんりな機能を知るのか
    » 公式のドキュメントやRailsガイドを読みましょ

    » もしくは強い人にレビューやペアプロしてもらい
    ましょう

    View Slide

  38. ここまでの話をまとめる

    View Slide

  39. Railsが提供するべんり
    な機能を使えば負債を作
    らずに開発できる!

    View Slide

  40. なんですけど…

    View Slide

  41. Railsが提供している機能が全部無条件
    に便利というわけではない
    » 安易に使うとやけどする機能もある
    » ググるといろいろでてくるはず
    » callback
    » default_scope
    » nested attributes

    View Slide

  42. gemも似たように、安易に採用すべきで
    はないものがある
    » devise
    » simple_form
    » activeadmin
    » etc

    View Slide

  43. この手のやつ、どう取り扱うべきか
    » チーム開発のときは特に慎重になったほうが良い
    » 禁止にしておいたほうが無難なことも多い
    » 利用シーンやチームの状況によって使えることも
    あるので、できれば吟味して決めたい

    View Slide

  44. 例えばwebpacker
    » 最近よくdisられている
    » しかしフロントエンド専任がおらず、片手間でjs
    を書く&&とりあえずes6使いたい、というケース
    だと大変便利
    » フロントエンド専任がいて、webpackの設定をち
    ゃんとしたい、というケースでは必要ない

    View Slide

  45. 例えばdevise
    » 昔からよくdisられている
    » devise wayから外れるとつらい
    » 管理画面だけ認証が必要、などdevise wayで問題
    ないならサッと導入できて便利

    View Slide

  46. 要注意なものは
    » なぜ注意が必要なのか
    » どう避けるか
    » どういうときなら使ってもよいのか
    を把握してまとめておくと良い

    View Slide

  47. 例えばhas_manyのcollection<<は要注

    class User
    has_many :posts
    end
    これはイマイチな書き方
    user.posts << Post.new(title: 'hello world!')

    View Slide

  48. なぜか
    collection<<は、レシーバのオブジェクトがDBに
    保存済みか否かで挙動が異なる(クエリが発行され
    たり発行されなかったりする)

    View Slide

  49. どう避けるか
    user.posts.build(title: 'hello world!')
    user.posts.create(title: 'hello world!')
    のように書いたほうが可読性が高く、事故りにくい

    View Slide

  50. どういうときなら使ってもよいのか
    collection.build や collection.createが適切
    でない、かつチームメンバー全員が挙動を理解して
    いればギリギリOKかと
    tag = Tag.create(name: 'Ϗʔϧ')
    Beer.each { |beer| beer.tags << tag }

    View Slide

  51. (こうも書けるのであんまり例が良くな
    い)
    tag = Tag.create(name: 'Ϗʔϧ')
    Beer.each { |beer| beer.taggings.create!(tag: tag) }

    View Slide

  52. このように
    » なぜ注意が必要なのか
    » どう避けるか
    » どういうときなら使ってもよいのか
    を把握してチーム内で共有したい

    View Slide

  53. 負債を作らないために
    は、負債になりそうな要
    素の対応法をみんなが理
    解している必要がある

    View Slide

  54. 少なくとも、レビューで
    負債になりそうな要素を
    止められるようにしてお
    きたい

    View Slide

  55. 負債を作らないイコール
    社内教育を頑張る

    View Slide

  56. メドピアでは
    » 社内読書会
    » ペアプロ
    » ふりかえり会
    などしています

    View Slide

  57. ところで、さっきの例は
    簡単すぎ

    View Slide

  58. 現実ではもっと判断に悩
    むケースが多い

    View Slide

  59. 判断に悩む例: コールバックをどう回
    避するとよいか?
    » フォームオブジェクト?
    » サービスクラス?
    » その他
    ↑方針を決めたとして、具体的にどうやって作ると
    いいの?

    View Slide

  60. なるべく具体的に「こう
    だ!」と道を示さない
    と、それぞれ思い思いの
    実装になってそれが負債
    につながる

    View Slide

  61. しかし選択するのが難し

    View Slide

  62. そこで clean-rails.org
    ですよ

    View Slide

  63. 可読性の高いRailsのコ
    ードを議論するコミュニ
    ティ

    View Slide

  64. どのように実装するとよ
    いか判断に悩むときに相
    談できる

    View Slide

  65. 皆様からの投稿お待ちし
    ています
    !

    View Slide

  66. まとめ
    » 負債を作らないためには、べんりな機能、要注意
    な機能を知ることが大事
    » 要注意なものについて、チーム全体で理解できる
    ように詳細を詰めて周知する
    » 勉強と教育をがんばりましょう

    View Slide

  67. 判断に悩むものは一緒に
    勉強していきましょう

    View Slide